diff --git a/samples/fleet_provisioning/fleet_provisioning/main.cpp b/samples/fleet_provisioning/fleet_provisioning/main.cpp index 3fb3d55c4..02b307f7e 100644 --- a/samples/fleet_provisioning/fleet_provisioning/main.cpp +++ b/samples/fleet_provisioning/fleet_provisioning/main.cpp @@ -2,10 +2,16 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ + +/** + * A sample application demonstrating usage of AWS IoT Fleet provisioning. + * + * It's easier to follow a synchronous workflow, when events happen one after another. For that reason, this sample + * performs all actions, like connecting to a server or registering a thing, in synchronous manner. + */ + #include #include -#include -#include #include @@ -21,87 +27,81 @@ #include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include #include "../../utils/CommandLineUtils.h" using namespace Aws::Crt; using namespace Aws::Iotidentity; -using namespace std::this_thread; // sleep_for, sleep_until -using namespace std::chrono; // nanoseconds, system_clock, seconds -static void sleep(int sleeptime) +static String getFileData(const String &fileName) { - std::cout << "Sleeping for " << sleeptime << " seconds..." << std::endl; - sleep_until(system_clock::now() + seconds(sleeptime)); -} - -static std::string getFileData(std::string const &fileName) -{ - std::ifstream ifs(fileName); + std::ifstream ifs(fileName.c_str()); std::string str; getline(ifs, str, (char)ifs.eof()); - return str; + return str.c_str(); } -int main(int argc, char *argv[]) +/** + * Auxiliary structure for holding data used by MQTT connection. + */ +struct ConnectionContext { - /************************ Setup ****************************/ - - // Do the global initialization for the API - ApiHandle apiHandle; - // Variables for the sample - String csrFile; - String token; - RegisterThingResponse registerThingResponse; + std::promise connectionCompletedPromise; + std::promise connectionClosedPromise; +}; - /** - * cmdData is the arguments/input from the command line placed into a single struct for - * use in this sample. This handles all of the command line parsing, validating, etc. - * See the Utils/CommandLineUtils for more information. - */ - Utils::cmdData cmdData = Utils::parseSampleInputFleetProvisioning(argc, argv, &apiHandle); +/** + * Auxiliary structure for holding data used when creating a certificate. + */ +struct CreateCertificateContext +{ + std::promise pubAckPromise; + std::promise acceptedSubAckPromise; + std::promise rejectedSubAckPromise; + std::promise tokenPromise; +}; - if (cmdData.input_csrPath != "") - { - csrFile = getFileData(cmdData.input_csrPath.c_str()).c_str(); - } +/** + * Auxiliary structure for holding data used when registering a thing. + */ +struct RegisterThingContext +{ + std::promise pubAckPromise; + std::promise acceptedSubAckPromise; + std::promise rejectedSubAckPromise; + std::promise thingCreatedPromise; +}; +/** + * Create MQTT3 connection. + */ +std::shared_ptr createConnection(const Utils::cmdData &cmdData, ConnectionContext &ctx) +{ /** * In a real world application you probably don't want to enforce synchronous behavior - * but this is a sample console application, so we'll just do that with a condition variable. + * but this is a sample console application, so we'll just do that with a promise. */ - std::promise connectionCompletedPromise; - std::promise connectionClosedPromise; // Invoked when a MQTT connect has completed or failed - auto onConnectionCompleted = [&](Mqtt::MqttConnection &, int errorCode, Mqtt::ReturnCode returnCode, bool) { + auto onConnectionCompleted = [&ctx](Mqtt::MqttConnection &, int errorCode, Mqtt::ReturnCode returnCode, bool) { if (errorCode) { fprintf(stdout, "Connection failed with error %s\n", ErrorDebugString(errorCode)); - connectionCompletedPromise.set_value(false); + ctx.connectionCompletedPromise.set_value(false); } else { fprintf(stdout, "Connection completed with return code %d\n", returnCode); - connectionCompletedPromise.set_value(true); + ctx.connectionCompletedPromise.set_value(true); } }; // Invoked when a disconnect has been completed - auto onDisconnect = [&](Mqtt::MqttConnection & /*conn*/) { + auto onDisconnect = [&ctx](Mqtt::MqttConnection & /*conn*/) { { fprintf(stdout, "Disconnect completed\n"); - connectionClosedPromise.set_value(); + ctx.connectionClosedPromise.set_value(); } }; @@ -138,6 +138,307 @@ int main(int argc, char *argv[]) connection->OnConnectionCompleted = std::move(onConnectionCompleted); connection->OnDisconnect = std::move(onDisconnect); + return connection; +} + +/** + * Keys-and-Certificate workflow. + * + * @note Subscriptions created here will be active even after the function completes. So, all variables accessed in the + * callbacks must be alive for the whole duration of the identityClient's lifetime. An instance of + * CreateCertificateContext is used to store variables used by the callbacks. + */ +void createKeysAndCertificate(IotIdentityClient &identityClient, CreateCertificateContext &ctx) +{ + auto onKeysPublishPubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error publishing to CreateKeysAndCertificate: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.pubAckPromise.set_value(); + }; + + auto onKeysAcceptedSubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error subscribing to CreateKeysAndCertificate accepted: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.acceptedSubAckPromise.set_value(); + }; + + auto onKeysRejectedSubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error subscribing to CreateKeysAndCertificate rejected: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.rejectedSubAckPromise.set_value(); + }; + + auto onKeysAccepted = [&ctx](CreateKeysAndCertificateResponse *response, int ioErr) { + if (ioErr == AWS_OP_SUCCESS) + { + fprintf(stdout, "CreateKeysAndCertificateResponse certificateId: %s.\n", response->CertificateId->c_str()); + ctx.tokenPromise.set_value(*response->CertificateOwnershipToken); + } + else + { + fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); + exit(-1); + } + }; + + auto onKeysRejected = [](ErrorResponse *error, int ioErr) { + if (ioErr == AWS_OP_SUCCESS) + { + fprintf( + stdout, + "CreateKeysAndCertificate failed with statusCode %d, errorMessage %s and errorCode %s.", + *error->StatusCode, + error->ErrorMessage->c_str(), + error->ErrorCode->c_str()); + exit(-1); + } + else + { + fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); + exit(-1); + } + }; + + fprintf(stdout, "Subscribing to CreateKeysAndCertificate Accepted and Rejected topics\n"); + CreateKeysAndCertificateSubscriptionRequest keySubscriptionRequest; + identityClient.SubscribeToCreateKeysAndCertificateAccepted( + keySubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onKeysAccepted, onKeysAcceptedSubAck); + identityClient.SubscribeToCreateKeysAndCertificateRejected( + keySubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onKeysRejected, onKeysRejectedSubAck); + + // Wait for the subscriptions to the accept and reject keys-and-certificate topics to be established. + ctx.acceptedSubAckPromise.get_future().wait(); + ctx.rejectedSubAckPromise.get_future().wait(); + + // Now, when we subscribed to the keys and certificate topics, we can make a request for a certificate. + fprintf(stdout, "Publishing to CreateKeysAndCertificate topic\n"); + CreateKeysAndCertificateRequest createKeysAndCertificateRequest; + identityClient.PublishCreateKeysAndCertificate( + createKeysAndCertificateRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onKeysPublishPubAck); + ctx.pubAckPromise.get_future().wait(); +} + +/** + * Certificate-from-CSR workflow. + * + * @note Subscriptions created here will be active even after the function completes. So, all variables accessed in the + * callbacks must be alive for the whole duration of the identityClient's lifetime. An instance of + * CreateCertificateContext is used to store variables used by the callbacks. + */ +void createCertificateFromCsr(IotIdentityClient &identityClient, CreateCertificateContext &ctx, const String &csrFile) +{ + auto onCsrPublishPubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error publishing to CreateCertificateFromCsr: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.pubAckPromise.set_value(); + }; + + auto onCsrAcceptedSubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error subscribing to CreateCertificateFromCsr accepted: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.acceptedSubAckPromise.set_value(); + }; + + auto onCsrRejectedSubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error subscribing to CreateCertificateFromCsr rejected: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.rejectedSubAckPromise.set_value(); + }; + + auto onCsrAccepted = [&ctx](CreateCertificateFromCsrResponse *response, int ioErr) { + if (ioErr == AWS_OP_SUCCESS) + { + fprintf(stdout, "CreateCertificateFromCsrResponse certificateId: %s.\n", response->CertificateId->c_str()); + ctx.tokenPromise.set_value(*response->CertificateOwnershipToken); + } + else + { + fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); + exit(-1); + } + }; + + auto onCsrRejected = [](ErrorResponse *error, int ioErr) { + if (ioErr == AWS_OP_SUCCESS) + { + fprintf( + stdout, + "CreateCertificateFromCsr failed with statusCode %d, errorMessage %s and errorCode %s.", + *error->StatusCode, + error->ErrorMessage->c_str(), + error->ErrorCode->c_str()); + exit(-1); + } + else + { + fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); + exit(-1); + } + }; + + // CreateCertificateFromCsr workflow + fprintf(stdout, "Subscribing to CreateCertificateFromCsr Accepted and Rejected topics\n"); + CreateCertificateFromCsrSubscriptionRequest csrSubscriptionRequest; + identityClient.SubscribeToCreateCertificateFromCsrAccepted( + csrSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onCsrAccepted, onCsrAcceptedSubAck); + + identityClient.SubscribeToCreateCertificateFromCsrRejected( + csrSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onCsrRejected, onCsrRejectedSubAck); + + // Wait for the subscriptions to the accept and reject certificates topics to be established. + ctx.acceptedSubAckPromise.get_future().wait(); + ctx.rejectedSubAckPromise.get_future().wait(); + + // Now, when we subscribed to the certificates topics, we can make a request for a certificate. + fprintf(stdout, "Publishing to CreateCertificateFromCsr topic\n"); + CreateCertificateFromCsrRequest createCertificateFromCsrRequest; + createCertificateFromCsrRequest.CertificateSigningRequest = csrFile; + identityClient.PublishCreateCertificateFromCsr( + createCertificateFromCsrRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onCsrPublishPubAck); + ctx.pubAckPromise.get_future().wait(); +} + +/** + * Provision an AWS IoT thing using a pre-defined template. + */ +void registerThing( + IotIdentityClient &identityClient, + RegisterThingContext &ctx, + const Utils::cmdData &cmdData, + const String &token) +{ + auto onRegisterAcceptedSubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error subscribing to RegisterThing accepted: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.acceptedSubAckPromise.set_value(); + }; + + auto onRegisterRejectedSubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error subscribing to RegisterThing rejected: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.rejectedSubAckPromise.set_value(); + }; + + auto onRegisterAccepted = [&ctx](RegisterThingResponse *response, int ioErr) { + if (ioErr == AWS_OP_SUCCESS) + { + fprintf(stdout, "RegisterThingResponse ThingName: %s.\n", response->ThingName->c_str()); + ctx.thingCreatedPromise.set_value(); + } + else + { + fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); + exit(-1); + } + }; + + auto onRegisterRejected = [](ErrorResponse *error, int ioErr) { + if (ioErr == AWS_OP_SUCCESS) + { + fprintf( + stdout, + "RegisterThing failed with statusCode %d, errorMessage %s and errorCode %s.", + *error->StatusCode, + error->ErrorMessage->c_str(), + error->ErrorCode->c_str()); + } + else + { + fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); + exit(-1); + } + }; + + auto onRegisterPublishPubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error publishing to RegisterThing: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.pubAckPromise.set_value(); + }; + + fprintf(stdout, "Subscribing to RegisterThing Accepted and Rejected topics\n"); + RegisterThingSubscriptionRequest registerSubscriptionRequest; + registerSubscriptionRequest.TemplateName = cmdData.input_templateName; + + identityClient.SubscribeToRegisterThingAccepted( + registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterAccepted, onRegisterAcceptedSubAck); + + identityClient.SubscribeToRegisterThingRejected( + registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterRejected, onRegisterRejectedSubAck); + + // Wait for the subscriptions to the accept and reject RegisterThing topics to be established. + ctx.acceptedSubAckPromise.get_future().wait(); + ctx.rejectedSubAckPromise.get_future().wait(); + + fprintf(stdout, "Publishing to RegisterThing topic\n"); + RegisterThingRequest registerThingRequest; + registerThingRequest.TemplateName = cmdData.input_templateName; + + const Aws::Crt::String jsonValue = cmdData.input_templateParameters; + Aws::Crt::JsonObject value(jsonValue); + Map pm = value.View().GetAllObjects(); + Aws::Crt::Map params = Aws::Crt::Map(); + + for (const auto &x : pm) + { + params.emplace(x.first, x.second.AsString()); + } + + registerThingRequest.Parameters = params; + // NOTE: In a real application creating multiple certificates you'll probably need to protect token var with + // a critical section. This sample makes only one request for a certificate, so no data race is possible. + registerThingRequest.CertificateOwnershipToken = token; + + identityClient.PublishRegisterThing(registerThingRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterPublishPubAck); + ctx.pubAckPromise.get_future().wait(); + + // Wait for registering a thing to succeed. + ctx.thingCreatedPromise.get_future().wait(); +} + +int main(int argc, char *argv[]) +{ + /************************ Setup ****************************/ + + // Do the global initialization for the API + ApiHandle apiHandle; + + /** + * cmdData is the arguments/input from the command line placed into a single struct for + * use in this sample. This handles all of the command line parsing, validating, etc. + * See the Utils/CommandLineUtils for more information. + */ + Utils::cmdData cmdData = Utils::parseSampleInputFleetProvisioning(argc, argv, &apiHandle); + + ConnectionContext connectionContext; + auto connection = createConnection(cmdData, connectionContext); + /************************ Run the sample ****************************/ fprintf(stdout, "Connecting...\n"); @@ -147,327 +448,37 @@ int main(int argc, char *argv[]) exit(-1); } - if (connectionCompletedPromise.get_future().get()) + if (!connectionContext.connectionCompletedPromise.get_future().get()) { - IotIdentityClient identityClient(connection); - - std::promise csrPublishCompletedPromise; - std::promise csrAcceptedCompletedPromise; - std::promise csrRejectedCompletedPromise; - - std::promise keysPublishCompletedPromise; - std::promise keysAcceptedCompletedPromise; - std::promise keysRejectedCompletedPromise; - - std::promise registerPublishCompletedPromise; - std::promise registerAcceptedCompletedPromise; - std::promise registerRejectedCompletedPromise; - - auto onCsrPublishSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf(stderr, "Error publishing to CreateCertificateFromCsr: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - - csrPublishCompletedPromise.set_value(); - }; - - auto onCsrAcceptedSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf( - stderr, "Error subscribing to CreateCertificateFromCsr accepted: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - - csrAcceptedCompletedPromise.set_value(); - }; - - auto onCsrRejectedSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf( - stderr, "Error subscribing to CreateCertificateFromCsr rejected: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - csrRejectedCompletedPromise.set_value(); - }; - - auto onCsrAccepted = [&](CreateCertificateFromCsrResponse *response, int ioErr) { - if (ioErr == AWS_OP_SUCCESS) - { - fprintf( - stdout, "CreateCertificateFromCsrResponse certificateId: %s.\n", response->CertificateId->c_str()); - token = *response->CertificateOwnershipToken; - } - else - { - fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); - exit(-1); - } - }; - - auto onCsrRejected = [&](ErrorResponse *error, int ioErr) { - if (ioErr == AWS_OP_SUCCESS) - { - fprintf( - stdout, - "CreateCertificateFromCsr failed with statusCode %d, errorMessage %s and errorCode %s.", - *error->StatusCode, - error->ErrorMessage->c_str(), - error->ErrorCode->c_str()); - exit(-1); - } - else - { - fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); - exit(-1); - } - }; - - auto onKeysPublishSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf(stderr, "Error publishing to CreateKeysAndCertificate: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - - keysPublishCompletedPromise.set_value(); - }; - - auto onKeysAcceptedSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf( - stderr, "Error subscribing to CreateKeysAndCertificate accepted: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - - keysAcceptedCompletedPromise.set_value(); - }; - - auto onKeysRejectedSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf( - stderr, "Error subscribing to CreateKeysAndCertificate rejected: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - keysRejectedCompletedPromise.set_value(); - }; - - auto onKeysAccepted = [&](CreateKeysAndCertificateResponse *response, int ioErr) { - if (ioErr == AWS_OP_SUCCESS) - { - fprintf( - stdout, "CreateKeysAndCertificateResponse certificateId: %s.\n", response->CertificateId->c_str()); - token = *response->CertificateOwnershipToken; - } - else - { - fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); - exit(-1); - } - }; - - auto onKeysRejected = [&](ErrorResponse *error, int ioErr) { - if (ioErr == AWS_OP_SUCCESS) - { - fprintf( - stdout, - "CreateKeysAndCertificate failed with statusCode %d, errorMessage %s and errorCode %s.", - *error->StatusCode, - error->ErrorMessage->c_str(), - error->ErrorCode->c_str()); - exit(-1); - } - else - { - fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); - exit(-1); - } - }; - - auto onRegisterAcceptedSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf(stderr, "Error subscribing to RegisterThing accepted: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - - registerAcceptedCompletedPromise.set_value(); - }; - - auto onRegisterRejectedSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf(stderr, "Error subscribing to RegisterThing rejected: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - registerRejectedCompletedPromise.set_value(); - }; - - auto onRegisterAccepted = [&](RegisterThingResponse *response, int ioErr) { - if (ioErr == AWS_OP_SUCCESS) - { - fprintf(stdout, "RegisterThingResponse ThingName: %s.\n", response->ThingName->c_str()); - } - else - { - fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); - exit(-1); - } - }; - - auto onRegisterRejected = [&](ErrorResponse *error, int ioErr) { - if (ioErr == AWS_OP_SUCCESS) - { - fprintf( - stdout, - "RegisterThing failed with statusCode %d, errorMessage %s and errorCode %s.", - *error->StatusCode, - error->ErrorMessage->c_str(), - error->ErrorCode->c_str()); - } - else - { - fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); - exit(-1); - } - }; - - auto onRegisterPublishSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf(stderr, "Error publishing to RegisterThing: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - - registerPublishCompletedPromise.set_value(); - }; - - if (csrFile.empty()) - { - // CreateKeysAndCertificate workflow - std::cout << "Subscribing to CreateKeysAndCertificate Accepted and Rejected topics" << std::endl; - CreateKeysAndCertificateSubscriptionRequest keySubscriptionRequest; - identityClient.SubscribeToCreateKeysAndCertificateAccepted( - keySubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onKeysAccepted, onKeysAcceptedSubAck); - - identityClient.SubscribeToCreateKeysAndCertificateRejected( - keySubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onKeysRejected, onKeysRejectedSubAck); - - std::cout << "Publishing to CreateKeysAndCertificate topic" << std::endl; - CreateKeysAndCertificateRequest createKeysAndCertificateRequest; - identityClient.PublishCreateKeysAndCertificate( - createKeysAndCertificateRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onKeysPublishSubAck); - - std::cout << "Subscribing to RegisterThing Accepted and Rejected topics" << std::endl; - RegisterThingSubscriptionRequest registerSubscriptionRequest; - registerSubscriptionRequest.TemplateName = cmdData.input_templateName; - - identityClient.SubscribeToRegisterThingAccepted( - registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterAccepted, onRegisterAcceptedSubAck); - - identityClient.SubscribeToRegisterThingRejected( - registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterRejected, onRegisterRejectedSubAck); - - sleep(1); - - std::cout << "Publishing to RegisterThing topic" << std::endl; - RegisterThingRequest registerThingRequest; - registerThingRequest.TemplateName = cmdData.input_templateName; - - const Aws::Crt::String jsonValue = cmdData.input_templateParameters; - Aws::Crt::JsonObject value(jsonValue); - Map pm = value.View().GetAllObjects(); - Aws::Crt::Map params = - Aws::Crt::Map(); - - for (const auto &x : pm) - { - params.emplace(x.first, x.second.AsString()); - } - - registerThingRequest.Parameters = params; - registerThingRequest.CertificateOwnershipToken = token; - - identityClient.PublishRegisterThing( - registerThingRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterPublishSubAck); - sleep(1); - - keysPublishCompletedPromise.get_future().wait(); - keysAcceptedCompletedPromise.get_future().wait(); - keysRejectedCompletedPromise.get_future().wait(); - registerPublishCompletedPromise.get_future().wait(); - registerAcceptedCompletedPromise.get_future().wait(); - registerRejectedCompletedPromise.get_future().wait(); - } - else - { - // CreateCertificateFromCsr workflow - std::cout << "Subscribing to CreateCertificateFromCsr Accepted and Rejected topics" << std::endl; - CreateCertificateFromCsrSubscriptionRequest csrSubscriptionRequest; - identityClient.SubscribeToCreateCertificateFromCsrAccepted( - csrSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onCsrAccepted, onCsrAcceptedSubAck); - - identityClient.SubscribeToCreateCertificateFromCsrRejected( - csrSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onCsrRejected, onCsrRejectedSubAck); - - std::cout << "Publishing to CreateCertificateFromCsr topic" << std::endl; - CreateCertificateFromCsrRequest createCertificateFromCsrRequest; - createCertificateFromCsrRequest.CertificateSigningRequest = csrFile; - identityClient.PublishCreateCertificateFromCsr( - createCertificateFromCsrRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onCsrPublishSubAck); - - std::cout << "Subscribing to RegisterThing Accepted and Rejected topics" << std::endl; - RegisterThingSubscriptionRequest registerSubscriptionRequest; - registerSubscriptionRequest.TemplateName = cmdData.input_templateName; - - identityClient.SubscribeToRegisterThingAccepted( - registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterAccepted, onRegisterAcceptedSubAck); - - identityClient.SubscribeToRegisterThingRejected( - registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterRejected, onRegisterRejectedSubAck); - - sleep(2); - - std::cout << "Publishing to RegisterThing topic" << std::endl; - RegisterThingRequest registerThingRequest; - registerThingRequest.TemplateName = cmdData.input_templateName; - - const Aws::Crt::String jsonValue = cmdData.input_templateParameters; - Aws::Crt::JsonObject value(jsonValue); - Map pm = value.View().GetAllObjects(); - Aws::Crt::Map params = - Aws::Crt::Map(); - - for (const auto &x : pm) - { - params.emplace(x.first, x.second.AsString()); - } - - registerThingRequest.Parameters = params; - registerThingRequest.CertificateOwnershipToken = token; - - identityClient.PublishRegisterThing( - registerThingRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterPublishSubAck); - sleep(2); - - csrPublishCompletedPromise.get_future().wait(); - csrAcceptedCompletedPromise.get_future().wait(); - csrRejectedCompletedPromise.get_future().wait(); - registerPublishCompletedPromise.get_future().wait(); - registerAcceptedCompletedPromise.get_future().wait(); - registerRejectedCompletedPromise.get_future().wait(); - } + exit(-1); + } + + // Create fleet provisioning client. + IotIdentityClient identityClient(connection); + + // Create certificate. + CreateCertificateContext certificateContext; + if (cmdData.input_csrPath != "") + { + auto csrFile = getFileData(cmdData.input_csrPath); + createCertificateFromCsr(identityClient, certificateContext, csrFile); + } + else + { + createKeysAndCertificate(identityClient, certificateContext); } + // Wait for a certificate token to be obtained. + auto token = certificateContext.tokenPromise.get_future().get(); + + // After certificate is obtained, it's time to register a thing. + RegisterThingContext registerThingContext; + registerThing(identityClient, registerThingContext, cmdData, token); + // Disconnect if (connection->Disconnect()) { - connectionClosedPromise.get_future().wait(); + connectionContext.connectionClosedPromise.get_future().wait(); } return 0; diff --git a/samples/fleet_provisioning/mqtt5_fleet_provisioning/main.cpp b/samples/fleet_provisioning/mqtt5_fleet_provisioning/main.cpp index 994d83a56..3ea7ef274 100644 --- a/samples/fleet_provisioning/mqtt5_fleet_provisioning/main.cpp +++ b/samples/fleet_provisioning/mqtt5_fleet_provisioning/main.cpp @@ -2,10 +2,16 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ + +/** + * A sample application demonstrating usage of AWS IoT Fleet provisioning with MQTT5 client. + * + * It's easier to follow a synchronous workflow, when events happen one after another. For that reason, this sample + * performs all actions, like connecting to a server or registering a thing, in synchronous manner. + */ + #include #include -#include -#include #include #include #include @@ -22,61 +28,59 @@ #include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include #include "../../utils/CommandLineUtils.h" using namespace Aws::Crt; using namespace Aws::Iotidentity; -using namespace std::this_thread; // sleep_for, sleep_until -using namespace std::chrono; // nanoseconds, system_clock, seconds -static void sleep(int sleeptime) +static String getFileData(const String &fileName) { - std::cout << "Sleeping for " << sleeptime << " seconds..." << std::endl; - sleep_until(system_clock::now() + seconds(sleeptime)); -} - -static std::string getFileData(std::string const &fileName) -{ - std::ifstream ifs(fileName); + std::ifstream ifs(fileName.c_str()); std::string str; getline(ifs, str, (char)ifs.eof()); - return str; + return str.c_str(); } -int main(int argc, char *argv[]) +/** + * Auxiliary structure for holding data used by MQTT connection. + */ +struct Mqtt5ClientContext { - /************************ Setup ****************************/ - - // Do the global initialization for the API - ApiHandle apiHandle; - // Variables for the sample - String csrFile; - String token; - RegisterThingResponse registerThingResponse; + std::promise connectionPromise; + std::promise stoppedPromise; + std::promise disconnectPromise; + std::promise subscribeSuccess; +}; - /** - * cmdData is the arguments/input from the command line placed into a single struct for - * use in this sample. This handles all of the command line parsing, validating, etc. - * See the Utils/CommandLineUtils for more information. - */ - Utils::cmdData cmdData = Utils::parseSampleInputFleetProvisioning(argc, argv, &apiHandle); +/** + * Auxiliary structure for holding data used when creating a certificate. + */ +struct CreateCertificateContext +{ + std::promise pubAckPromise; + std::promise acceptedSubAckPromise; + std::promise rejectedSubAckPromise; + std::promise tokenPromise; +}; - if (cmdData.input_csrPath != "") - { - csrFile = getFileData(cmdData.input_csrPath.c_str()).c_str(); - } +/** + * Auxiliary structure for holding data used when registering a thing. + */ +struct RegisterThingContext +{ + std::promise pubAckPromise; + std::promise acceptedSubAckPromise; + std::promise rejectedSubAckPromise; + std::promise thingCreatedPromise; +}; +/** + * Create MQTT5 client. + */ +std::shared_ptr createMqtt5Client(Mqtt5ClientContext &ctx, const Utils::cmdData &cmdData) +{ // Create the MQTT5 builder and populate it with data from cmdData. Aws::Iot::Mqtt5ClientBuilder *builder = Aws::Iot::Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithMtlsFromPath( cmdData.input_endpoint, cmdData.input_cert.c_str(), cmdData.input_key.c_str()); @@ -86,7 +90,7 @@ int main(int argc, char *argv[]) { printf( "Failed to setup mqtt5 client builder with error code %d: %s", LastError(), ErrorDebugString(LastError())); - return -1; + exit(-1); } // Setup connection options @@ -98,40 +102,335 @@ int main(int argc, char *argv[]) builder->WithPort(static_cast(cmdData.input_port)); } - std::promise connectionPromise; - std::promise stoppedPromise; - std::promise disconnectPromise; - std::promise subscribeSuccess; - // Setup lifecycle callbacks - builder->WithClientConnectionSuccessCallback( - [&connectionPromise](const Mqtt5::OnConnectionSuccessEventData &eventData) { - fprintf( - stdout, - "Mqtt5 Client connection succeed, clientid: %s.\n", - eventData.negotiatedSettings->getClientId().c_str()); - connectionPromise.set_value(true); - }); - builder->WithClientConnectionFailureCallback([&connectionPromise]( - const Mqtt5::OnConnectionFailureEventData &eventData) { + builder->WithClientConnectionSuccessCallback([&ctx](const Mqtt5::OnConnectionSuccessEventData &eventData) { + fprintf( + stdout, + "Mqtt5 Client connection succeed, clientid: %s.\n", + eventData.negotiatedSettings->getClientId().c_str()); + ctx.connectionPromise.set_value(true); + }); + builder->WithClientConnectionFailureCallback([&ctx](const Mqtt5::OnConnectionFailureEventData &eventData) { fprintf(stdout, "Mqtt5 Client connection failed with error: %s.\n", aws_error_debug_str(eventData.errorCode)); - connectionPromise.set_value(false); + ctx.connectionPromise.set_value(false); }); - builder->WithClientStoppedCallback([&stoppedPromise](const Mqtt5::OnStoppedEventData &) { + builder->WithClientStoppedCallback([&ctx](const Mqtt5::OnStoppedEventData &) { fprintf(stdout, "Mqtt5 Client stopped.\n"); - stoppedPromise.set_value(); + ctx.stoppedPromise.set_value(); }); builder->WithClientAttemptingConnectCallback([](const Mqtt5::OnAttemptingConnectEventData &) { fprintf(stdout, "Mqtt5 Client attempting connection...\n"); }); - builder->WithClientDisconnectionCallback([&disconnectPromise](const Mqtt5::OnDisconnectionEventData &eventData) { + builder->WithClientDisconnectionCallback([&ctx](const Mqtt5::OnDisconnectionEventData &eventData) { fprintf(stdout, "Mqtt5 Client disconnection with reason: %s.\n", aws_error_debug_str(eventData.errorCode)); - disconnectPromise.set_value(); + ctx.disconnectPromise.set_value(); }); // Create Mqtt5Client std::shared_ptr client = builder->Build(); delete builder; + + return client; +} + +/** + * Keys-and-Certificate workflow. + * + * @note Subscriptions created here will be active even after the function completes. So, all variables accessed in the + * callbacks must be alive for the whole duration of the identityClient's lifetime. An instance of + * CreateCertificateContext is used to store variables used by the callbacks. + */ +void createKeysAndCertificate(IotIdentityClient &identityClient, CreateCertificateContext &ctx) +{ + auto onKeysPublishPubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error publishing to CreateKeysAndCertificate: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.pubAckPromise.set_value(); + }; + + auto onKeysAcceptedSubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error subscribing to CreateKeysAndCertificate accepted: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.acceptedSubAckPromise.set_value(); + }; + + auto onKeysRejectedSubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error subscribing to CreateKeysAndCertificate rejected: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.rejectedSubAckPromise.set_value(); + }; + + auto onKeysAccepted = [&ctx](CreateKeysAndCertificateResponse *response, int ioErr) { + if (ioErr == AWS_OP_SUCCESS) + { + fprintf(stdout, "CreateKeysAndCertificateResponse certificateId: %s.\n", response->CertificateId->c_str()); + ctx.tokenPromise.set_value(*response->CertificateOwnershipToken); + } + else + { + fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); + exit(-1); + } + }; + + auto onKeysRejected = [](ErrorResponse *error, int ioErr) { + if (ioErr == AWS_OP_SUCCESS) + { + fprintf( + stdout, + "CreateKeysAndCertificate failed with statusCode %d, errorMessage %s and errorCode %s.", + *error->StatusCode, + error->ErrorMessage->c_str(), + error->ErrorCode->c_str()); + exit(-1); + } + else + { + fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); + exit(-1); + } + }; + + fprintf(stdout, "Subscribing to CreateKeysAndCertificate Accepted and Rejected topics\n"); + CreateKeysAndCertificateSubscriptionRequest keySubscriptionRequest; + identityClient.SubscribeToCreateKeysAndCertificateAccepted( + keySubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onKeysAccepted, onKeysAcceptedSubAck); + identityClient.SubscribeToCreateKeysAndCertificateRejected( + keySubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onKeysRejected, onKeysRejectedSubAck); + + // Wait for the subscriptions to the accept and reject keys-and-certificate topics to be established. + ctx.acceptedSubAckPromise.get_future().wait(); + ctx.rejectedSubAckPromise.get_future().wait(); + + // Now, when we subscribed to the keys and certificate topics, we can make a request for a certificate. + fprintf(stdout, "Publishing to CreateKeysAndCertificate topic\n"); + CreateKeysAndCertificateRequest createKeysAndCertificateRequest; + identityClient.PublishCreateKeysAndCertificate( + createKeysAndCertificateRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onKeysPublishPubAck); + ctx.pubAckPromise.get_future().wait(); +} + +/** + * Certificate-from-CSR workflow. + * + * @note Subscriptions created here will be active even after the function completes. So, all variables accessed in the + * callbacks must be alive for the whole duration of the identityClient's lifetime. An instance of + * CreateCertificateContext is used to store variables used by the callbacks. + */ +void createCertificateFromCsr(IotIdentityClient &identityClient, CreateCertificateContext &ctx, const String &csrFile) +{ + auto onCsrPublishPubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error publishing to CreateCertificateFromCsr: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.pubAckPromise.set_value(); + }; + + auto onCsrAcceptedSubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error subscribing to CreateCertificateFromCsr accepted: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.acceptedSubAckPromise.set_value(); + }; + + auto onCsrRejectedSubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error subscribing to CreateCertificateFromCsr rejected: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.rejectedSubAckPromise.set_value(); + }; + + auto onCsrAccepted = [&ctx](CreateCertificateFromCsrResponse *response, int ioErr) { + if (ioErr == AWS_OP_SUCCESS) + { + fprintf(stdout, "CreateCertificateFromCsrResponse certificateId: %s.\n", response->CertificateId->c_str()); + ctx.tokenPromise.set_value(*response->CertificateOwnershipToken); + } + else + { + fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); + exit(-1); + } + }; + + auto onCsrRejected = [](ErrorResponse *error, int ioErr) { + if (ioErr == AWS_OP_SUCCESS) + { + fprintf( + stdout, + "CreateCertificateFromCsr failed with statusCode %d, errorMessage %s and errorCode %s.", + *error->StatusCode, + error->ErrorMessage->c_str(), + error->ErrorCode->c_str()); + exit(-1); + } + else + { + fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); + exit(-1); + } + }; + + // CreateCertificateFromCsr workflow + fprintf(stdout, "Subscribing to CreateCertificateFromCsr Accepted and Rejected topics\n"); + CreateCertificateFromCsrSubscriptionRequest csrSubscriptionRequest; + identityClient.SubscribeToCreateCertificateFromCsrAccepted( + csrSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onCsrAccepted, onCsrAcceptedSubAck); + + identityClient.SubscribeToCreateCertificateFromCsrRejected( + csrSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onCsrRejected, onCsrRejectedSubAck); + + // Wait for the subscriptions to the accept and reject certificates topics to be established. + ctx.acceptedSubAckPromise.get_future().wait(); + ctx.rejectedSubAckPromise.get_future().wait(); + + // Now, when we subscribed to the certificates topics, we can make a request for a certificate. + fprintf(stdout, "Publishing to CreateCertificateFromCsr topic\n"); + CreateCertificateFromCsrRequest createCertificateFromCsrRequest; + createCertificateFromCsrRequest.CertificateSigningRequest = csrFile; + identityClient.PublishCreateCertificateFromCsr( + createCertificateFromCsrRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onCsrPublishPubAck); + ctx.pubAckPromise.get_future().wait(); +} + +/** + * Provision an AWS IoT thing using a pre-defined template. + */ +void registerThing( + IotIdentityClient &identityClient, + RegisterThingContext &ctx, + const Utils::cmdData &cmdData, + const String &token) +{ + auto onRegisterAcceptedSubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error subscribing to RegisterThing accepted: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.acceptedSubAckPromise.set_value(); + }; + + auto onRegisterRejectedSubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error subscribing to RegisterThing rejected: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.rejectedSubAckPromise.set_value(); + }; + + auto onRegisterAccepted = [&ctx](RegisterThingResponse *response, int ioErr) { + if (ioErr == AWS_OP_SUCCESS) + { + fprintf(stdout, "RegisterThingResponse ThingName: %s.\n", response->ThingName->c_str()); + ctx.thingCreatedPromise.set_value(); + } + else + { + fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); + exit(-1); + } + }; + + auto onRegisterRejected = [](ErrorResponse *error, int ioErr) { + if (ioErr == AWS_OP_SUCCESS) + { + fprintf( + stdout, + "RegisterThing failed with statusCode %d, errorMessage %s and errorCode %s.", + *error->StatusCode, + error->ErrorMessage->c_str(), + error->ErrorCode->c_str()); + } + else + { + fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); + exit(-1); + } + }; + + auto onRegisterPublishPubAck = [&ctx](int ioErr) { + if (ioErr != AWS_OP_SUCCESS) + { + fprintf(stderr, "Error publishing to RegisterThing: %s\n", ErrorDebugString(ioErr)); + exit(-1); + } + ctx.pubAckPromise.set_value(); + }; + + fprintf(stdout, "Subscribing to RegisterThing Accepted and Rejected topics\n"); + RegisterThingSubscriptionRequest registerSubscriptionRequest; + registerSubscriptionRequest.TemplateName = cmdData.input_templateName; + + identityClient.SubscribeToRegisterThingAccepted( + registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterAccepted, onRegisterAcceptedSubAck); + + identityClient.SubscribeToRegisterThingRejected( + registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterRejected, onRegisterRejectedSubAck); + + // Wait for the subscriptions to the accept and reject RegisterThing topics to be established. + ctx.acceptedSubAckPromise.get_future().wait(); + ctx.rejectedSubAckPromise.get_future().wait(); + + fprintf(stdout, "Publishing to RegisterThing topic\n"); + RegisterThingRequest registerThingRequest; + registerThingRequest.TemplateName = cmdData.input_templateName; + + const Aws::Crt::String jsonValue = cmdData.input_templateParameters; + Aws::Crt::JsonObject value(jsonValue); + Map pm = value.View().GetAllObjects(); + Aws::Crt::Map params = Aws::Crt::Map(); + + for (const auto &x : pm) + { + params.emplace(x.first, x.second.AsString()); + } + + registerThingRequest.Parameters = params; + // NOTE: In a real application creating multiple certificates you'll probably need to protect token var with + // a critical section. This sample makes only one request for a certificate, so no data race is possible. + registerThingRequest.CertificateOwnershipToken = token; + + identityClient.PublishRegisterThing(registerThingRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterPublishPubAck); + ctx.pubAckPromise.get_future().wait(); + + // Wait for registering a thing to succeed. + ctx.thingCreatedPromise.get_future().wait(); +} + +int main(int argc, char *argv[]) +{ + /************************ Setup ****************************/ + + // Do the global initialization for the API + ApiHandle apiHandle; + + /** + * cmdData is the arguments/input from the command line placed into a single struct for + * use in this sample. This handles all of the command line parsing, validating, etc. + * See the Utils/CommandLineUtils for more information. + */ + Utils::cmdData cmdData = Utils::parseSampleInputFleetProvisioning(argc, argv, &apiHandle); + + Mqtt5ClientContext mqtt5ClientContext; + auto client = createMqtt5Client(mqtt5ClientContext, cmdData); + /************************ Run the sample ****************************/ fprintf(stdout, "Connecting...\n"); @@ -141,327 +440,37 @@ int main(int argc, char *argv[]) exit(-1); } - if (connectionPromise.get_future().get()) + if (!mqtt5ClientContext.connectionPromise.get_future().get()) { - IotIdentityClient identityClient(client); - - std::promise csrPublishCompletedPromise; - std::promise csrAcceptedCompletedPromise; - std::promise csrRejectedCompletedPromise; - - std::promise keysPublishCompletedPromise; - std::promise keysAcceptedCompletedPromise; - std::promise keysRejectedCompletedPromise; - - std::promise registerPublishCompletedPromise; - std::promise registerAcceptedCompletedPromise; - std::promise registerRejectedCompletedPromise; - - auto onCsrPublishSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf(stderr, "Error publishing to CreateCertificateFromCsr: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - - csrPublishCompletedPromise.set_value(); - }; - - auto onCsrAcceptedSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf( - stderr, "Error subscribing to CreateCertificateFromCsr accepted: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - - csrAcceptedCompletedPromise.set_value(); - }; - - auto onCsrRejectedSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf( - stderr, "Error subscribing to CreateCertificateFromCsr rejected: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - csrRejectedCompletedPromise.set_value(); - }; - - auto onCsrAccepted = [&](CreateCertificateFromCsrResponse *response, int ioErr) { - if (ioErr == AWS_OP_SUCCESS) - { - fprintf( - stdout, "CreateCertificateFromCsrResponse certificateId: %s.\n", response->CertificateId->c_str()); - token = *response->CertificateOwnershipToken; - } - else - { - fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); - exit(-1); - } - }; - - auto onCsrRejected = [&](ErrorResponse *error, int ioErr) { - if (ioErr == AWS_OP_SUCCESS) - { - fprintf( - stdout, - "CreateCertificateFromCsr failed with statusCode %d, errorMessage %s and errorCode %s.", - *error->StatusCode, - error->ErrorMessage->c_str(), - error->ErrorCode->c_str()); - exit(-1); - } - else - { - fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); - exit(-1); - } - }; - - auto onKeysPublishSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf(stderr, "Error publishing to CreateKeysAndCertificate: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - - keysPublishCompletedPromise.set_value(); - }; - - auto onKeysAcceptedSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf( - stderr, "Error subscribing to CreateKeysAndCertificate accepted: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - - keysAcceptedCompletedPromise.set_value(); - }; - - auto onKeysRejectedSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf( - stderr, "Error subscribing to CreateKeysAndCertificate rejected: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - keysRejectedCompletedPromise.set_value(); - }; - - auto onKeysAccepted = [&](CreateKeysAndCertificateResponse *response, int ioErr) { - if (ioErr == AWS_OP_SUCCESS) - { - fprintf( - stdout, "CreateKeysAndCertificateResponse certificateId: %s.\n", response->CertificateId->c_str()); - token = *response->CertificateOwnershipToken; - } - else - { - fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); - exit(-1); - } - }; - - auto onKeysRejected = [&](ErrorResponse *error, int ioErr) { - if (ioErr == AWS_OP_SUCCESS) - { - fprintf( - stdout, - "CreateKeysAndCertificate failed with statusCode %d, errorMessage %s and errorCode %s.", - *error->StatusCode, - error->ErrorMessage->c_str(), - error->ErrorCode->c_str()); - exit(-1); - } - else - { - fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); - exit(-1); - } - }; - - auto onRegisterAcceptedSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf(stderr, "Error subscribing to RegisterThing accepted: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - - registerAcceptedCompletedPromise.set_value(); - }; - - auto onRegisterRejectedSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf(stderr, "Error subscribing to RegisterThing rejected: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - registerRejectedCompletedPromise.set_value(); - }; - - auto onRegisterAccepted = [&](RegisterThingResponse *response, int ioErr) { - if (ioErr == AWS_OP_SUCCESS) - { - fprintf(stdout, "RegisterThingResponse ThingName: %s.\n", response->ThingName->c_str()); - } - else - { - fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); - exit(-1); - } - }; - - auto onRegisterRejected = [&](ErrorResponse *error, int ioErr) { - if (ioErr == AWS_OP_SUCCESS) - { - fprintf( - stdout, - "RegisterThing failed with statusCode %d, errorMessage %s and errorCode %s.", - *error->StatusCode, - error->ErrorMessage->c_str(), - error->ErrorCode->c_str()); - } - else - { - fprintf(stderr, "Error on subscription: %s.\n", ErrorDebugString(ioErr)); - exit(-1); - } - }; - - auto onRegisterPublishSubAck = [&](int ioErr) { - if (ioErr != AWS_OP_SUCCESS) - { - fprintf(stderr, "Error publishing to RegisterThing: %s\n", ErrorDebugString(ioErr)); - exit(-1); - } - - registerPublishCompletedPromise.set_value(); - }; - - if (csrFile.empty()) - { - // CreateKeysAndCertificate workflow - std::cout << "Subscribing to CreateKeysAndCertificate Accepted and Rejected topics" << std::endl; - CreateKeysAndCertificateSubscriptionRequest keySubscriptionRequest; - identityClient.SubscribeToCreateKeysAndCertificateAccepted( - keySubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onKeysAccepted, onKeysAcceptedSubAck); - - identityClient.SubscribeToCreateKeysAndCertificateRejected( - keySubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onKeysRejected, onKeysRejectedSubAck); - - std::cout << "Publishing to CreateKeysAndCertificate topic" << std::endl; - CreateKeysAndCertificateRequest createKeysAndCertificateRequest; - identityClient.PublishCreateKeysAndCertificate( - createKeysAndCertificateRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onKeysPublishSubAck); - - std::cout << "Subscribing to RegisterThing Accepted and Rejected topics" << std::endl; - RegisterThingSubscriptionRequest registerSubscriptionRequest; - registerSubscriptionRequest.TemplateName = cmdData.input_templateName; - - identityClient.SubscribeToRegisterThingAccepted( - registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterAccepted, onRegisterAcceptedSubAck); - - identityClient.SubscribeToRegisterThingRejected( - registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterRejected, onRegisterRejectedSubAck); - - sleep(1); - - std::cout << "Publishing to RegisterThing topic" << std::endl; - RegisterThingRequest registerThingRequest; - registerThingRequest.TemplateName = cmdData.input_templateName; - - const Aws::Crt::String jsonValue = cmdData.input_templateParameters; - Aws::Crt::JsonObject value(jsonValue); - Map pm = value.View().GetAllObjects(); - Aws::Crt::Map params = - Aws::Crt::Map(); - - for (const auto &x : pm) - { - params.emplace(x.first, x.second.AsString()); - } - - registerThingRequest.Parameters = params; - registerThingRequest.CertificateOwnershipToken = token; - - identityClient.PublishRegisterThing( - registerThingRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterPublishSubAck); - sleep(1); - - keysPublishCompletedPromise.get_future().wait(); - keysAcceptedCompletedPromise.get_future().wait(); - keysRejectedCompletedPromise.get_future().wait(); - registerPublishCompletedPromise.get_future().wait(); - registerAcceptedCompletedPromise.get_future().wait(); - registerRejectedCompletedPromise.get_future().wait(); - } - else - { - // CreateCertificateFromCsr workflow - std::cout << "Subscribing to CreateCertificateFromCsr Accepted and Rejected topics" << std::endl; - CreateCertificateFromCsrSubscriptionRequest csrSubscriptionRequest; - identityClient.SubscribeToCreateCertificateFromCsrAccepted( - csrSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onCsrAccepted, onCsrAcceptedSubAck); - - identityClient.SubscribeToCreateCertificateFromCsrRejected( - csrSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onCsrRejected, onCsrRejectedSubAck); - - std::cout << "Publishing to CreateCertificateFromCsr topic" << std::endl; - CreateCertificateFromCsrRequest createCertificateFromCsrRequest; - createCertificateFromCsrRequest.CertificateSigningRequest = csrFile; - identityClient.PublishCreateCertificateFromCsr( - createCertificateFromCsrRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onCsrPublishSubAck); - - std::cout << "Subscribing to RegisterThing Accepted and Rejected topics" << std::endl; - RegisterThingSubscriptionRequest registerSubscriptionRequest; - registerSubscriptionRequest.TemplateName = cmdData.input_templateName; - - identityClient.SubscribeToRegisterThingAccepted( - registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterAccepted, onRegisterAcceptedSubAck); - - identityClient.SubscribeToRegisterThingRejected( - registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterRejected, onRegisterRejectedSubAck); - - sleep(2); - - std::cout << "Publishing to RegisterThing topic" << std::endl; - RegisterThingRequest registerThingRequest; - registerThingRequest.TemplateName = cmdData.input_templateName; - - const Aws::Crt::String jsonValue = cmdData.input_templateParameters; - Aws::Crt::JsonObject value(jsonValue); - Map pm = value.View().GetAllObjects(); - Aws::Crt::Map params = - Aws::Crt::Map(); - - for (const auto &x : pm) - { - params.emplace(x.first, x.second.AsString()); - } - - registerThingRequest.Parameters = params; - registerThingRequest.CertificateOwnershipToken = token; - - identityClient.PublishRegisterThing( - registerThingRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterPublishSubAck); - sleep(2); - - csrPublishCompletedPromise.get_future().wait(); - csrAcceptedCompletedPromise.get_future().wait(); - csrRejectedCompletedPromise.get_future().wait(); - registerPublishCompletedPromise.get_future().wait(); - registerAcceptedCompletedPromise.get_future().wait(); - registerRejectedCompletedPromise.get_future().wait(); - } + return -1; + } + + // Create fleet provisioning client. + IotIdentityClient identityClient(client); + + // Create certificate. + CreateCertificateContext certificateContext; + if (cmdData.input_csrPath != "") + { + auto csrFile = getFileData(cmdData.input_csrPath); + createCertificateFromCsr(identityClient, certificateContext, csrFile); + } + else + { + createKeysAndCertificate(identityClient, certificateContext); } + // Wait for a certificate token to be obtained. + auto token = certificateContext.tokenPromise.get_future().get(); + + // After certificate is obtained, it's time to register a thing. + RegisterThingContext registerThingContext; + registerThing(identityClient, registerThingContext, cmdData, token); + // Disconnect if (client->Stop()) { - stoppedPromise.get_future().wait(); + mqtt5ClientContext.stoppedPromise.get_future().wait(); } return 0;