Add the Labs projects provided in the V10.2.1_191129 zip file.

This commit is contained in:
Richard Barry 2019-12-02 23:39:25 +00:00
parent 46e5937529
commit e5708b38e9
801 changed files with 356576 additions and 0 deletions

View file

@ -0,0 +1,5 @@
[{000214A0-0000-0000-C000-000000000046}]
Prop3=19,11
[InternetShortcut]
IDList=
URL=https://www.freertos.org/jobs

View file

@ -0,0 +1,927 @@
/*
* AWS IoT Jobs V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_jobs.h
* @brief User-facing functions of the Jobs library.
*/
#ifndef AWS_IOT_JOBS_H_
#define AWS_IOT_JOBS_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Jobs types include. */
#include "types/aws_iot_jobs_types.h"
/*------------------------- Jobs library functions --------------------------*/
/**
* @functionspage{jobs,Jobs library}
* - @functionname{jobs_function_init}
* - @functionname{jobs_function_cleanup}
* - @functionname{jobs_function_getpendingasync}
* - @functionname{jobs_function_getpendingsync}
* - @functionname{jobs_function_startnextasync}
* - @functionname{jobs_function_startnextsync}
* - @functionname{jobs_function_describeasync}
* - @functionname{jobs_function_describesync}
* - @functionname{jobs_function_updateasync}
* - @functionname{jobs_function_updatesync}
* - @functionname{jobs_function_wait}
* - @functionname{jobs_function_setnotifypendingcallback}
* - @functionname{jobs_function_setnotifynextcallback}
* - @functionname{jobs_function_removepersistentsubscriptions}
* - @functionname{jobs_function_strerror}
* - @functionname{jobs_function_statename}
*/
/**
* @functionpage{AwsIotJobs_Init,jobs,init}
* @functionpage{AwsIotJobs_Cleanup,jobs,cleanup}
* @functionpage{AwsIotJobs_GetPendingAsync,jobs,getpendingasync}
* @functionpage{AwsIotJobs_GetPendingSync,jobs,getpendingsync}
* @functionpage{AwsIotJobs_StartNextAsync,jobs,startnextasync}
* @functionpage{AwsIotJobs_StartNextSync,jobs,startnextsync}
* @functionpage{AwsIotJobs_DescribeAsync,jobs,describeasync}
* @functionpage{AwsIotJobs_DescribeSync,jobs,describesync}
* @functionpage{AwsIotJobs_UpdateAsync,jobs,updateasync}
* @functionpage{AwsIotJobs_UpdateSync,jobs,updatesync}
* @functionpage{AwsIotJobs_Wait,jobs,wait}
* @functionpage{AwsIotJobs_SetNotifyPendingCallback,jobs,setnotifypendingcallback}
* @functionpage{AwsIotJobs_SetNotifyNextCallback,jobs,setnotifynextcallback}
* @functionpage{AwsIotJobs_RemovePersistentSubscriptions,jobs,removepersistentsubscriptions}
* @functionpage{AwsIotJobs_strerror,jobs,strerror}
* @functionpage{AwsIotJobs_StateName,jobs,statename}
*/
/**
* @brief One-time initialization function for the Jobs library.
*
* This function performs internal setup of the Jobs library. <b>It must be
* called once (and only once) before calling any other Jobs function.</b>
* Calling this function more than once without first calling @ref
* jobs_function_cleanup may result in a crash.
*
* @param[in] mqttTimeoutMs The amount of time (in milliseconds) that the Jobs
* library will wait for MQTT operations. Optional; set this to `0` to use
* @ref AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_INIT_FAILED
*
* @warning No thread-safety guarantees are provided for this function.
*
* @see @ref jobs_function_cleanup
*/
/* @[declare_jobs_init] */
AwsIotJobsError_t AwsIotJobs_Init( uint32_t mqttTimeoutMs );
/* @[declare_jobs_init] */
/**
* @brief One-time deinitialization function for the Jobs library.
*
* This function frees resources taken in @ref jobs_function_init and deletes
* any [persistent subscriptions.](@ref AWS_IOT_JOBS_FLAG_KEEP_SUBSCRIPTIONS)
* It should be called to clean up the Jobs library. After this function returns,
* @ref jobs_function_init must be called again before calling any other Jobs
* function.
*
* @warning No thread-safety guarantees are provided for this function.
*
* @see @ref jobs_function_init
*/
/* @[declare_jobs_cleanup] */
void AwsIotJobs_Cleanup( void );
/* @[declare_jobs_cleanup] */
/**
* @brief Get the list of all pending jobs for a Thing and receive an asynchronous
* notification when the response arrives.
*
* This function implements the [GetPendingJobExecutions]
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-getpendingjobexecutions)
* command of the Jobs API, which gets the list of all Jobs for a Thing that are
* not in a terminal state. The list of retrieved Jobs is returned as the `pResponse`
* member in #AwsIotJobsCallbackParam_t, or through the #AwsIotJobsResponse_t
* parameter of @ref jobs_function_wait.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
* @param[out] pGetPendingOperation Set to a handle by which this operation may be referenced
* after this function returns. This reference is invalidated once the Jobs operation
* completes.
*
* @return This function will return #AWS_IOT_JOBS_STATUS_PENDING upon successfully
* queuing the Jobs operation.
* @return If this function fails before queuing the Jobs operation, it will return one of:
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* @return Upon successful completion of the Jobs operation (either through an #AwsIotJobsCallbackInfo_t
* or @ref jobs_function_wait), the status will be #AWS_IOT_JOBS_SUCCESS.
* @return Should the Jobs operation fail, the status will be one of:
* - #AWS_IOT_JOBS_NO_MEMORY (Memory could not be allocated for incoming document)
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*
* @see @ref jobs_function_getpendingsync for a blocking variant of this function.
*
* <b>Example</b>
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* // Signature of Jobs callback function.
* void _jobsCallback( void * pCallbackContext, AwsIotJobsCallbackParam_t * pCallbackParam );
*
* AwsIotJobsOperation_t getPendingOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
* AwsIotJobsCallbackInfo_t callbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
*
* // Set the request info.
* requestInfo.mqttConnection = _mqttConnection;
* requestInfo.pThingName = THING_NAME;
* requestInfo.thingNameLength = THING_NAME_LENGTH;
*
* // Set the callback function to invoke.
* callbackInfo.function = _jobsCallback;
*
* // Queue Jobs GET PENDING.
* AwsIotJobsError_t getPendingResult = AwsIotJobs_GetPendingAsync( &requestInfo,
* 0,
* &callbackInfo,
* &getPendingOperation );
*
* // GET PENDING should have returned AWS_IOT_JOBS_STATUS_PENDING. The function
* // _jobsCallback will be invoked once the Jobs response is received.
* @endcode
*/
/* @[declare_jobs_getpendingasync] */
AwsIotJobsError_t AwsIotJobs_GetPendingAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
AwsIotJobsOperation_t * const pGetPendingOperation );
/* @[declare_jobs_getpendingasync] */
/**
* @brief Get the list of all pending jobs for a Thing with a timeout for receiving
* the response.
*
* This function queues a Jobs GET PENDING, then waits for the result. Internally,
* this function is a call to @ref jobs_function_getpendingasync followed by
* @ref jobs_function_wait. See @ref jobs_function_getpendingasync for more information
* on the Jobs GET PENDING command.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] timeoutMs If a response is not received within this timeout, this
* function returns #AWS_IOT_JOBS_TIMEOUT.
* @param[out] pJobsResponse The response received from the Jobs service.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - #AWS_IOT_JOBS_TIMEOUT
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*/
/* @[declare_jobs_getpendingsync] */
AwsIotJobsError_t AwsIotJobs_GetPendingSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
uint32_t flags,
uint32_t timeoutMs,
AwsIotJobsResponse_t * const pJobsResponse );
/* @[declare_jobs_getpendingsync] */
/**
* @brief Start the next pending job execution for a Thing and receive an asynchronous
* notification when the response arrives.
*
* This function implements the [StartNextPendingJobExecution]
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-startnextpendingjobexecution)
* command of the Jobs API, which gets and starts the next pending job execution
* for a Thing.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] pUpdateInfo Jobs update parameters.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
* @param[out] pStartNextOperation Set to a handle by which this operation may be referenced
* after this function returns. This reference is invalidated once the Jobs operation
* completes.
*
* @return This function will return #AWS_IOT_JOBS_STATUS_PENDING upon successfully
* queuing the Jobs operation.
* @return If this function fails before queuing the Jobs operation, it will return one of:
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* @return Upon successful completion of the Jobs operation (either through an #AwsIotJobsCallbackInfo_t
* or @ref jobs_function_wait), the status will be #AWS_IOT_JOBS_SUCCESS.
* @return Should the Jobs operation fail, the status will be one of:
* - #AWS_IOT_JOBS_NO_MEMORY (Memory could not be allocated for incoming document)
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*
* @see @ref jobs_function_startnextsync for a blocking variant of this function.
*
* <b>Example</b>
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* // Signature of Jobs callback function.
* void _jobsCallback( void * pCallbackContext, AwsIotJobsCallbackParam_t * pCallbackParam );
*
* AwsIotJobsOperation_t startNextOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
* AwsIotJobsUpdateInfo_t updateInfo = AWS_IOT_JOBS_UPDATE_INFO_INITIALIZER;
* AwsIotJobsCallbackInfo_t callbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
*
* // Set the request info. The update info generally does not need to be
* // changed, as its defaults are suitable.
* requestInfo.mqttConnection = _mqttConnection;
* requestInfo.pThingName = THING_NAME;
* requestInfo.thingNameLength = THING_NAME_LENGTH;
*
* // Set the callback function to invoke.
* callbackInfo.function = _jobsCallback;
*
* // Queue Jobs START NEXT.
* AwsIotJobsError_t startNextResult = AwsIotJobs_StartNextAsync( &requestInfo,
* &updateInfo,
* 0,
* &callbackInfo,
* &startNextOperation );
*
* // START NEXT should have returned AWS_IOT_JOBS_STATUS_PENDING. The function
* // _jobsCallback will be invoked once the Jobs response is received.
* @endcode
*/
/* @[declare_jobs_startnextasync] */
AwsIotJobsError_t AwsIotJobs_StartNextAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
const AwsIotJobsUpdateInfo_t * pUpdateInfo,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
AwsIotJobsOperation_t * const pStartNextOperation );
/* @[declare_jobs_startnextasync] */
/**
* @brief Start the next pending job execution for a Thing with a timeout for
* receiving the response.
*
* This function queues a Jobs START NEXT, then waits for the result. Internally,
* this function is a call to @ref jobs_function_startnextasync followed by
* @ref jobs_function_wait. See @ref jobs_function_startnextasync for more information
* on the Jobs START NEXT command.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] pUpdateInfo Jobs update parameters.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] timeoutMs If a response is not received within this timeout, this
* function returns #AWS_IOT_JOBS_TIMEOUT.
* @param[out] pJobsResponse The response received from the Jobs service.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - #AWS_IOT_JOBS_TIMEOUT
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*/
/* @[declare_jobs_startnextsync] */
AwsIotJobsError_t AwsIotJobs_StartNextSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
const AwsIotJobsUpdateInfo_t * pUpdateInfo,
uint32_t flags,
uint32_t timeoutMs,
AwsIotJobsResponse_t * const pJobsResponse );
/* @[declare_jobs_startnextsync] */
/**
* @brief Get detailed information about a job execution and receive an asynchronous
* notification when the response arrives.
*
* This function implements the [DescribeJobExecution]
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-describejobexecution)
* command of the Jobs API, which gets detailed information about a job execution.
* The description is returned as the `pResponse` member in #AwsIotJobsCallbackParam_t,
* or through the #AwsIotJobsResponse_t parameter of @ref jobs_function_wait.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] executionNumber The execution number to describe. Optional; pass
* #AWS_IOT_JOBS_NO_EXECUTION_NUMBER to ignore.
* @param[in] includeJobDocument Whether the response should include the full
* Job document.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
* @param[out] pDescribeOperation Set to a handle by which this operation may be referenced
* after this function returns. This reference is invalidated once the Jobs operation
* completes.
*
* @return This function will return #AWS_IOT_JOBS_STATUS_PENDING upon successfully
* queuing the Jobs operation.
* @return If this function fails before queuing the Jobs operation, it will return one of:
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* @return Upon successful completion of the Jobs operation (either through an #AwsIotJobsCallbackInfo_t
* or @ref jobs_function_wait), the status will be #AWS_IOT_JOBS_SUCCESS.
* @return Should the Jobs operation fail, the status will be one of:
* - #AWS_IOT_JOBS_NO_MEMORY (Memory could not be allocated for incoming document)
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*
* @see @ref jobs_function_describesync for a blocking variant of this function.
*
* <b>Example</b>
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* // Signature of Jobs callback function.
* void _jobsCallback( void * pCallbackContext, AwsIotJobsCallbackParam_t * pCallbackParam );
*
* AwsIotJobsOperation_t describeOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
* AwsIotJobsCallbackInfo_t callbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
*
* // Set the request info.
* requestInfo.mqttConnection = _mqttConnection;
* requestInfo.pThingName = THING_NAME;
* requestInfo.thingNameLength = THING_NAME_LENGTH;
*
* // Describe the next Job. Or, this may be set to a specific Job ID.
* requestInfo.pJobId = AWS_IOT_JOBS_NEXT_JOB;
* requestInfo.jobIdLength = AWS_IOT_JOBS_NEXT_JOB_LENGTH;
*
* // Set the callback function to invoke.
* callbackInfo.function = _jobsCallback;
*
* // Queue Jobs DESCRIBE.
* AwsIotJobsError_t describeResult = AwsIotJobs_DescribeAsync( &requestInfo,
* AWS_IOT_JOBS_NO_EXECUTION_NUMBER,
* false,
* 0,
* &callbackInfo,
* &describeOperation );
*
* // DESCRIBE should have returned AWS_IOT_JOBS_STATUS_PENDING. The function
* // _jobsCallback will be invoked once the Jobs response is received.
* @endcode
*/
/* @[declare_jobs_describeasync] */
AwsIotJobsError_t AwsIotJobs_DescribeAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
int32_t executionNumber,
bool includeJobDocument,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
AwsIotJobsOperation_t * const pDescribeOperation );
/* @[declare_jobs_describeasync] */
/**
* @brief Get detailed information about a job execution with a timeout for receiving
* the response.
*
* This function queues a Jobs DESCRIBE, then waits for the result. Internally,
* this function is a call to @ref jobs_function_describeasync followed by
* @ref jobs_function_wait. See @ref jobs_function_describeasync for more information
* on the Jobs DESCRIBE command.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] executionNumber The execution number to describe. Optional; pass
* #AWS_IOT_JOBS_NO_EXECUTION_NUMBER to ignore.
* @param[in] includeJobDocument Whether the response should include the full
* Job document.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] timeoutMs If a response is not received within this timeout, this
* function returns #AWS_IOT_JOBS_TIMEOUT.
* @param[out] pJobsResponse The response received from the Jobs service.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - #AWS_IOT_JOBS_TIMEOUT
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*/
/* @[declare_jobs_describesync] */
AwsIotJobsError_t AwsIotJobs_DescribeSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
int32_t executionNumber,
bool includeJobDocument,
uint32_t flags,
uint32_t timeoutMs,
AwsIotJobsResponse_t * const pJobsResponse );
/* @[declare_jobs_describesync] */
/**
* @brief Update the status of a job execution and receive an asynchronous
* notification when the Job update completes.
*
* This function implements the [UpdateJobExecution]
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-updatejobexecution)
* command of the Jobs API, which updates the status of a Job execution.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] pUpdateInfo Jobs update parameters.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
* @param[out] pUpdateOperation Set to a handle by which this operation may be referenced
* after this function returns. This reference is invalidated once the Jobs operation
* completes.
*
* @return This function will return #AWS_IOT_JOBS_STATUS_PENDING upon successfully
* queuing the Jobs operation.
* @return If this function fails before queuing the Jobs operation, it will return one of:
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* @return Upon successful completion of the Jobs operation (either through an #AwsIotJobsCallbackInfo_t
* or @ref jobs_function_wait), the status will be #AWS_IOT_JOBS_SUCCESS.
* @return Should the Jobs operation fail, the status will be one of:
* - #AWS_IOT_JOBS_NO_MEMORY (Memory could not be allocated for incoming document)
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*
* @see @ref jobs_function_updatesync for a blocking variant of this function.
*
* <b>Example</b>
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* // Signature of Jobs callback function.
* void _jobsCallback( void * pCallbackContext, AwsIotJobsCallbackParam_t * pCallbackParam );
*
* AwsIotJobsOperation_t updateOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
* AwsIotJobsUpdateInfo_t updateInfo = AWS_IOT_JOBS_UPDATE_INFO_INITIALIZER;
* AwsIotJobsCallbackInfo_t callbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
*
* // Set the request info.
* requestInfo.mqttConnection = _mqttConnection;
* requestInfo.pThingName = THING_NAME;
* requestInfo.thingNameLength = THING_NAME_LENGTH;
*
* // A Job ID must be set. AWS_IOT_JOBS_NEXT_JOB is not valid for UPDATE.
* requestInfo.pJobId = "job-id";
* requestInfo.jobIdLength = 6;
*
* // Set the update info.
* updateInfo.newStatus = AWS_IOT_JOB_STATE_SUCCEEDED;
*
* // Set the callback function to invoke.
* callbackInfo.function = _jobsCallback;
*
* // Queue Jobs UPDATE.
* AwsIotJobsError_t updateResult = AwsIotJobs_UpdateAsync( &requestInfo,
* &updateInfo,
* 0,
* &callbackInfo,
* &updateOperation );
*
* // UPDATE should have returned AWS_IOT_JOBS_STATUS_PENDING. The function
* // _jobsCallback will be invoked once the Jobs response is received.
* @endcode
*/
/* @[declare_jobs_updateasync] */
AwsIotJobsError_t AwsIotJobs_UpdateAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
const AwsIotJobsUpdateInfo_t * pUpdateInfo,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
AwsIotJobsOperation_t * const pUpdateOperation );
/* @[declare_jobs_updateasync] */
/**
* @brief Update the status of a job execution with a timeout for receiving the
* response.
*
* This function queues a Jobs UPDATE, then waits for the result. Internally,
* this function is a call to @ref jobs_function_updateasync followed by
* @ref jobs_function_wait. See @ref jobs_function_updateasync for more information
* on the Jobs UPDATE command.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] pUpdateInfo Jobs update parameters.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] timeoutMs If a response is not received within this timeout, this
* function returns #AWS_IOT_JOBS_TIMEOUT.
* @param[out] pJobsResponse The response received from the Jobs service.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - #AWS_IOT_JOBS_TIMEOUT
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*/
/* @[declare_jobs_updatesync] */
AwsIotJobsError_t AwsIotJobs_UpdateSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
const AwsIotJobsUpdateInfo_t * pUpdateInfo,
uint32_t flags,
uint32_t timeoutMs,
AwsIotJobsResponse_t * const pJobsResponse );
/* @[declare_jobs_updatesync] */
/**
* @brief Wait for a Jobs operation to complete.
*
* This function blocks to wait for a [GET PENDING](@ref jobs_function_getpendingasync),
* [START NEXT](@ref jobs_function_startnextasync), [DESCRIBE](@ref jobs_function_describeasync),
* or [UPDATE](@ref jobs_function_updateasync) operation to complete. These operations are
* by default asynchronous; the function calls queue an operation for processing,
* and a callback is invoked once the operation is complete.
*
* To use this function, the flag #AWS_IOT_JOBS_FLAG_WAITABLE must have been
* set in the operation's function call. Additionally, this function must always
* be called with any waitable operation to clean up resources.
*
* Regardless of its return value, this function always clean up resources used
* by the waitable operation. This means `operation` is invalidated as soon as
* this function returns, even if it returns #AWS_IOT_JOBS_TIMEOUT or another
* error.
*
* @param[in] operation Reference to the Jobs operation to wait for. The flag
* #AWS_IOT_JOBS_FLAG_WAITABLE must have been set for this operation.
* @param[in] timeoutMs How long to wait before returning #AWS_IOT_JOBS_TIMEOUT.
* @param[out] pJobsResponse The response received from the Jobs service.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - #AWS_IOT_JOBS_TIMEOUT
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*
* <b>Example</b>:
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* AwsIotJobsOperation_t updateOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
* AwsIotJobsUpdateInfo_t updateInfo = AWS_IOT_JOBS_UPDATE_INFO_INITIALIZER;
* AwsIotJobsResponse_t jobsResponse = AWS_IOT_JOBS_RESPONSE_INITIALIZER;
*
* // Set the request info.
* requestInfo.mqttConnection = _mqttConnection;
* requestInfo.pThingName = THING_NAME;
* requestInfo.thingNameLength = THING_NAME_LENGTH;
*
* // Set the function used to allocate memory for an incoming response.
* requestInfo.mallocResponse = malloc;
*
* // A Job ID must be set. AWS_IOT_JOBS_NEXT_JOB is not valid for UPDATE.
* requestInfo.pJobId = "job-id";
* requestInfo.jobIdLength = 6;
*
* // Set the update info.
* updateInfo.newStatus = AWS_IOT_JOB_STATE_SUCCEEDED;
*
* // Queue Jobs UPDATE.
* AwsIotJobsError_t updateResult = AwsIotJobs_UpdateAsync( &requestInfo,
* &updateInfo,
* AWS_IOT_JOBS_FLAG_WAITABLE,
* NULL,
* &updateOperation );
*
* // UPDATE should have returned AWS_IOT_JOBS_STATUS_PENDING. The call to wait
* // returns once the result of the UPDATE is available or the timeout expires.
* if( updateResult == AWS_IOT_JOBS_STATUS_PENDING )
* {
* updateResult = AwsIotJobs_Wait( updateOperation, 5000, &jobsResponse );
*
* if( updateResult == AWS_IOT_JOBS_SUCCESS )
* {
* // Jobs operation succeeded. Do something with the Jobs response.
*
* // Once the Jobs response is no longer needed, free it.
* free( jobsResponse.pJobsResponse );
* }
* else
* {
* // Jobs operation failed.
* }
* }
* @endcode
*/
/* @[declare_jobs_wait] */
AwsIotJobsError_t AwsIotJobs_Wait( AwsIotJobsOperation_t operation,
uint32_t timeoutMs,
AwsIotJobsResponse_t * const pJobsResponse );
/* @[declare_jobs_wait] */
/**
* @brief Set a callback to be invoked when the list of pending Jobs changes.
*
* The Jobs service publishes a [JobExecutionsChanged]
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-jobexecutionschanged)
* message to the `jobs/notify` topic whenever a Job execution is added to or
* removed from the list of pending Job executions for a Thing. The message sent is
* useful for monitoring the list of pending Job executions.
*
* A <i>NOTIFY PENDING</i> callback may be invoked whenever a message is published
* to `jobs/notify`. Each Thing may have up to @ref AWS_IOT_JOBS_NOTIFY_CALLBACKS
* NOTIFY PENDING callbacks set. This function modifies the NOTIFY PENDING callback
* for a specific Thing depending on the `pNotifyPendingCallback` parameter and the
* presence of any existing NOTIFY PENDING callback.
* - When no existing NOTIFY PENDING callback exists for a specific Thing, a new
* callback is added.
* - If there is an existing NOTIFY PENDING callback and `pNotifyPendingCallback` is not `NULL`,
* then the existing callback function and parameter are replaced with `pNotifyPendingCallback`.
* - If there is an existing NOTIFY PENDING callback and `pNotifyPendingCallback` is `NULL`,
* then the callback is removed.
*
* The member @ref AwsIotJobsCallbackInfo_t.oldFunction must be used to select an
* already-registered callback function for replacement or removal when @ref
* AWS_IOT_JOBS_NOTIFY_CALLBACKS is greater than `1`. When multiple callbacks are
* set, all of them will be invoked when a message is received.
*
* @param[in] mqttConnection The MQTT connection to use for the subscription to `jobs/notify`.
* @param[in] pThingName The subscription to `jobs/notify` will be added for
* this Thing Name.
* @param[in] thingNameLength The length of `pThingName`.
* @param[in] flags This parameter is for future-compatibility. Currently, flags are
* not supported for this function and this parameter is ignored.
* @param[in] pNotifyPendingCallback Callback function to invoke for incoming messages.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_TIMEOUT
*
* @see @ref jobs_function_setnotifynextcallback for the function to register callbacks
* for next Job changes.
*
* <b>Example</b>:
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* AwsIotJobsError_t result = AWS_IOT_JOBS_STATUS_PENDING;
* AwsIotJobsCallbackInfo_t notifyPendingCallback = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
*
* // _jobsCallback will be invoked when any messages are received.
* notifyPendingCallback.function = _jobsCallback;
*
* // Set the NOTIFY PENDING callback for the Thing "Test_device".
* result = AwsIotJobs_SetNotifyPendingCallback( mqttConnection,
* THING_NAME,
* THING_NAME_LENGTH,
* 0,
* &notifyPendingCallback );
*
* // Check if the callback was successfully set.
* if( status == AWS_IOT_JOBS_SUCCESS )
* {
* // The callback will now be invoked whenever the list of pending Job
* // executions changes.
*
* // Once the callback is no longer needed, it may be removed by passing
* // NULL as the callback function and specifying the function to remove.
* notifyPendingCallback.function = NULL;
* notifyPendingCallback.oldFunction = _jobsCallback;
*
* status = AwsIotJobs_SetNotifyPendingCallback( mqttConnection,
* THING_NAME,
* THING_NAME_LENGTH,
* 0,
* &notifyPendingCallback );
*
* // The return value from removing a callback should always be success.
* assert( status == AWS_IOT_JOBS_SUCCESS );
* }
* @endcode
*/
/* @[declare_jobs_setnotifypendingcallback] */
AwsIotJobsError_t AwsIotJobs_SetNotifyPendingCallback( IotMqttConnection_t mqttConnection,
const char * pThingName,
size_t thingNameLength,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pNotifyPendingCallback );
/* @[declare_jobs_setnotifypendingcallback] */
/**
* @brief Set a callback to be invoked when the next pending Job changes.
*
* The Jobs service publishes a [NextJobExecutionChanged]
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-nextjobexecutionchanged)
* message to the `jobs/notify-next` topic whenever the next Job execution in
* the list of pending Job executions changes for a Thing. The message sent is
* useful for being notified of changes to the next Job.
*
* A <i>NOTIFY NEXT</i> callback may be invoked whenever a message is published
* to `jobs/notify-next`. Each Thing may have up to @ref AWS_IOT_JOBS_NOTIFY_CALLBACKS
* NOTIFY NEXT callbacks set. This function modifies the NOTIFY NEXT callback for
* a specific Thing depending on the `pNotifyNextCallback` parameter and the presence
* of any existing NOTIFY NEXT callback.
* - When no existing NOTIFY NEXT callback exists for a specific Thing, a new
* callback is added.
* - If there is an existing NOTIFY NEXT callback and `pNotifyNextCallback` is not `NULL`,
* then the existing callback function and parameter are replaced with `pNotifyNextCallback`.
* - If there is an existing NOTIFY NEXT callback and `pNotifyNextCallback` is `NULL`,
* then the callback is removed.
*
* The member @ref AwsIotJobsCallbackInfo_t.oldFunction must be used to select an
* already-registered callback function for replacement or removal when @ref
* AWS_IOT_JOBS_NOTIFY_CALLBACKS is greater than `1`. When multiple callbacks are
* set, all of them will be invoked when a message is received.
*
* @param[in] mqttConnection The MQTT connection to use for the subscription to `jobs/notify-next`.
* @param[in] pThingName The subscription to `jobs/notify-next` will be added for
* this Thing Name.
* @param[in] thingNameLength The length of `pThingName`.
* @param[in] flags This parameter is for future-compatibility. Currently, flags are
* not supported for this function and this parameter is ignored.
* @param[in] pNotifyNextCallback Callback function to invoke for incoming messages.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_TIMEOUT
*
* @see @ref jobs_function_setnotifypendingcallback for the function to register callbacks
* for all pending Job changes.
*
* <b>Example</b>:
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* AwsIotJobsError_t result = AWS_IOT_JOBS_STATUS_PENDING;
* AwsIotJobsCallbackInfo_t notifyNextCallback = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
*
* // _jobsCallback will be invoked when any messages are received.
* notifyNextCallback.function = _jobsCallback;
*
* // Set the NOTIFY NEXT callback for the Thing "Test_device".
* result = AwsIotJobs_SetNotifyNextCallback( mqttConnection,
* THING_NAME,
* THING_NAME_LENGTH,
* 0,
* &notifyNextCallback );
*
* // Check if the callback was successfully set.
* if( status == AWS_IOT_JOBS_SUCCESS )
* {
* // The callback will now be invoked whenever the next pending Job
* // execution changes.
*
* // Once the callback is no longer needed, it may be removed by passing
* // NULL as the callback function and specifying the function to remove.
* notifyNextCallback.function = NULL;
* notifyNextCallback.oldFunction = _jobsCallback;
*
* status = AwsIotJobs_SetNotifyNextCallback( mqttConnection,
* THING_NAME,
* THING_NAME_LENGTH,
* 0,
* &notifyNextCallback );
*
* // The return value from removing a callback should always be success.
* assert( status == AWS_IOT_JOBS_SUCCESS );
* }
* @endcode
*/
/* @[declare_jobs_setnotifynextcallback] */
AwsIotJobsError_t AwsIotJobs_SetNotifyNextCallback( IotMqttConnection_t mqttConnection,
const char * pThingName,
size_t thingNameLength,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pNotifyNextCallback );
/* @[declare_jobs_setnotifynextcallback] */
/**
* @brief Remove persistent Jobs operation topic subscriptions.
*
* Passing the flag @ref AWS_IOT_JOBS_FLAG_KEEP_SUBSCRIPTIONS to @ref jobs_function_getpendingasync,
* @ref jobs_function_startnextasync, @ref jobs_function_describeasync, @ref jobs_function_updateasync,
* or their blocking versions causes the Jobs operation topic subscriptions to be
* maintained for future calls to the same function. If a persistent subscription for a
* Jobs topic are no longer needed, this function may be used to remove it.
*
* @param[in] pRequestInfo Jobs request info. Only the [pThingName]
* (@ref #AwsIotJobsRequestInfo_t.pThingName), [thingNameLength]
* (@ref #AwsIotJobsRequestInfo_t.thingNameLength), and [mqttConnection]
* (@ref #AwsIotJobsRequestInfo_t.mqttConnection) members need to be set for this
* function.
* @param[in] flags Flags that determine which subscriptions to remove. Valid values are
* the bitwise OR of the following individual flags:
* - @ref AWS_IOT_JOBS_FLAG_REMOVE_GET_PENDING_SUBSCRIPTIONS
* - @ref AWS_IOT_JOBS_FLAG_REMOVE_START_NEXT_SUBSCRIPTIONS
* - @ref AWS_IOT_JOBS_FLAG_REMOVE_DESCRIBE_SUBSCRIPTIONS
* - @ref AWS_IOT_JOBS_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS
*
* @return On success:
* - #AWS_IOT_JOBS_SUCCESS
* @return If an MQTT UNSUBSCRIBE packet cannot be sent, one of the following:
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
*
* @note @ref jobs_function_cleanup removes persistent sessions as well.
*
* @warning This function is not safe to call with any in-progress operations!
* It also does not affect NOTIFY PENDING and NOTIFY NEXT callbacks registered
* with @ref jobs_function_setnotifypendingcallback and
* @ref jobs_function_setnotifynextcallback, respectively. (See documentation for
* those functions on how to remove their callbacks).
*/
/* @[declare_jobs_removepersistentsubscriptions] */
AwsIotJobsError_t AwsIotJobs_RemovePersistentSubscriptions( const AwsIotJobsRequestInfo_t * pRequestInfo,
uint32_t flags );
/* @[declare_jobs_removepersistentsubscriptions] */
/*-------------------------- Jobs helper functions --------------------------*/
/**
* @brief Returns a string that describes an #AwsIotJobsError_t.
*
* Like POSIX's `strerror`, this function returns a string describing a return
* code. In this case, the return code is a Jobs library error code, `status`.
*
* The string returned by this function <b>MUST</b> be treated as read-only: any
* attempt to modify its contents may result in a crash. Therefore, this function
* is limited to usage in logging.
*
* @param[in] status The status to describe.
*
* @return A read-only string that describes `status`.
*
* @warning The string returned by this function must never be modified.
*/
/* @[declare_jobs_strerror] */
const char * AwsIotJobs_strerror( AwsIotJobsError_t status );
/* @[declare_jobs_strerror] */
/**
* @brief Returns a string that describes an #AwsIotJobState_t.
*
* This function returns a string describing a Job state, `state`.
*
* The string returned by this function <b>MUST</b> be treated as read-only: any
* attempt to modify its contents may result in a crash. Therefore, this function
* is limited to usage in logging.
*
* @param[in] state The job state to describe.
*
* @return A read-only string that describes `state`.
*
* @warning The string returned by this function must never be modified.
*/
/* @[declare_jobs_statename] */
const char * AwsIotJobs_StateName( AwsIotJobState_t state );
/* @[declare_jobs_statename] */
#endif /* ifndef AWS_IOT_JOBS_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,885 @@
/*
* AWS IoT Jobs V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_jobs_operation.c
* @brief Implements functions that process Jobs operations.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* Jobs internal include. */
#include "private/aws_iot_jobs_internal.h"
/* Platform threads include. */
#include "platform/iot_threads.h"
/* Error handling include. */
#include "iot_error.h"
/* MQTT include. */
#include "iot_mqtt.h"
/*-----------------------------------------------------------*/
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
/**
* @brief Printable names for each of the Jobs operations.
*/
const char * const _pAwsIotJobsOperationNames[] =
{
"GET PENDING",
"START NEXT",
"DESCRIBE",
"UPDATE",
"SET NOTIFY-PENDING",
"SET NOTIFY-NEXT"
};
#endif /* if LIBRARY_LOG_LEVEL > IOT_LOG_NONE */
/*-----------------------------------------------------------*/
/**
* @brief First parameter to #_jobsOperation_match.
*/
typedef struct _operationMatchParams
{
_jobsOperationType_t type; /**< @brief GET PENDING, START NEXT, DESCRIBE, or UPDATE. */
const char * pThingName; /**< @brief Thing Name of Jobs operation. */
size_t thingNameLength; /**< @brief Length of #_operationMatchParams_t.pThingName. */
const char * pResponse; /**< @brief Jobs response document. */
size_t responseLength; /**< @brief Length of #_operationMatchParams_t.pResponse. */
} _operationMatchParams_t;
/*-----------------------------------------------------------*/
/**
* @brief Match a received Jobs response with a Jobs operation awaiting a
* response.
*
* @param[in] pOperationLink Pointer to the link member of the #_jobsOperation_t
* to check.
* @param[in] pMatch Pointer to an #_operationMatchParams_t.
*
* @return `true` if `pMatch` matches the received response; `false` otherwise.
*/
static bool _jobsOperation_match( const IotLink_t * pOperationLink,
void * pMatch );
/**
* @brief Invoked when a Jobs response is received for Jobs GET PENDING.
*
* @param[in] pArgument Ignored.
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
*/
static void _getPendingCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Invoked when a Jobs response is received for a Jobs START NEXT.
*
* @param[in] pArgument Ignored.
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
*/
static void _startNextCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Invoked when a Jobs response is received for Jobs DESCRIBE.
*
* @param[in] pArgument Ignored.
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
*/
static void _describeCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Invoked when a Jobs response is received for a Jobs UPDATE.
*
* @param[in] pArgument Ignored.
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
*/
static void _updateCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Common function for processing received Jobs responses.
*
* @param[in] type GET PENDING, START NEXT, DESCRIBE, or UPDATE.
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
*/
static void _commonOperationCallback( _jobsOperationType_t type,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Notify of a completed Jobs operation.
*
* @param[in] pOperation The operation which completed.
*
* Depending on the parameters passed to a user-facing Jobs function, the
* notification will cause @ref jobs_function_wait to return or invoke a
* user-provided callback.
*/
static void _notifyCompletion( _jobsOperation_t * pOperation );
/**
* @brief Get a Jobs subscription to use with a Jobs operation.
*
* This function may use an existing Jobs subscription, or it may allocate a
* new one.
*
* @param[in] pRequestInfo Common Jobs request parameters.
* @param[in] pTopicBuffer Contains the topic to use for subscribing.
* @param[in] operationTopicLength The length of the base topic in `pTopicBuffer`.
* @param[in] pOperation Jobs operation that needs a subscription.
* @param[out] pFreeTopicBuffer Whether the caller may free `pTopicBuffer`
* (which may be assigned to a subscription).
*
* @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_NO_MEMORY
*/
static AwsIotJobsError_t _findSubscription( const AwsIotJobsRequestInfo_t * pRequestInfo,
char * pTopicBuffer,
uint16_t operationTopicLength,
_jobsOperation_t * pOperation,
bool * pFreeTopicBuffer );
/*-----------------------------------------------------------*/
/**
* @brief List of active Jobs operations awaiting a response from the Jobs
* service.
*/
IotListDouble_t _AwsIotJobsPendingOperations = { 0 };
/**
* @brief Protects #_AwsIotJobsPendingOperations from concurrent access.
*/
IotMutex_t _AwsIotJobsPendingOperationsMutex;
/*-----------------------------------------------------------*/
static bool _jobsOperation_match( const IotLink_t * pOperationLink,
void * pMatch )
{
/* Because this function is called from a container function, the given link
* must never be NULL. */
AwsIotJobs_Assert( pOperationLink != NULL );
_jobsOperation_t * pOperation = IotLink_Container( _jobsOperation_t,
pOperationLink,
link );
_operationMatchParams_t * pParam = ( _operationMatchParams_t * ) pMatch;
_jobsSubscription_t * pSubscription = pOperation->pSubscription;
const char * pClientToken = NULL;
size_t clientTokenLength = 0;
/* Check for matching Thing Name and operation type. */
bool match = ( pOperation->type == pParam->type ) &&
( pParam->thingNameLength == pSubscription->thingNameLength ) &&
( strncmp( pParam->pThingName,
pSubscription->pThingName,
pParam->thingNameLength ) == 0 );
if( match == true )
{
IotLogDebug( "Verifying client tokens for Jobs %s.",
_pAwsIotJobsOperationNames[ pParam->type ] );
/* Check the response for a client token. */
match = AwsIot_GetClientToken( pParam->pResponse,
pParam->responseLength,
&pClientToken,
&clientTokenLength );
/* If the response contains a client token, check for a match. */
if( match == true )
{
match = ( pOperation->clientTokenLength == clientTokenLength ) &&
( strncmp( pOperation->pClientToken, pClientToken, clientTokenLength ) == 0 );
}
else
{
IotLogWarn( "Received a Jobs %s response with no client token. "
"This is possibly a response to a bad JSON document:\r\n%.*s",
_pAwsIotJobsOperationNames[ pParam->type ],
pParam->responseLength,
pParam->pResponse );
}
}
return match;
}
/*-----------------------------------------------------------*/
static void _getPendingCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage )
{
/* Silence warnings about unused parameter. */
( void ) pArgument;
_commonOperationCallback( JOBS_GET_PENDING, pMessage );
}
/*-----------------------------------------------------------*/
static void _startNextCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage )
{
/* Silence warnings about unused parameter. */
( void ) pArgument;
_commonOperationCallback( JOBS_START_NEXT, pMessage );
}
/*-----------------------------------------------------------*/
static void _describeCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage )
{
/* Silence warnings about unused parameter. */
( void ) pArgument;
_commonOperationCallback( JOBS_DESCRIBE, pMessage );
}
/*-----------------------------------------------------------*/
static void _updateCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage )
{
/* Silence warnings about unused parameter. */
( void ) pArgument;
_commonOperationCallback( JOBS_UPDATE, pMessage );
}
/*-----------------------------------------------------------*/
static void _commonOperationCallback( _jobsOperationType_t type,
IotMqttCallbackParam_t * pMessage )
{
_jobsOperation_t * pOperation = NULL;
IotLink_t * pOperationLink = NULL;
_operationMatchParams_t param = { 0 };
AwsIotStatus_t status = AWS_IOT_UNKNOWN;
uint32_t flags = 0;
/* Set operation type and response. */
param.type = type;
param.pResponse = pMessage->u.message.info.pPayload;
param.responseLength = pMessage->u.message.info.payloadLength;
/* Parse the Thing Name from the MQTT topic name. */
if( AwsIot_ParseThingName( pMessage->u.message.info.pTopicName,
pMessage->u.message.info.topicNameLength,
&( param.pThingName ),
&( param.thingNameLength ) ) == false )
{
IOT_GOTO_CLEANUP();
}
/* Lock the pending operations list for exclusive access. */
IotMutex_Lock( &( _AwsIotJobsPendingOperationsMutex ) );
/* Search for a matching pending operation. */
pOperationLink = IotListDouble_FindFirstMatch( &_AwsIotJobsPendingOperations,
NULL,
_jobsOperation_match,
&param );
/* Find and remove the first Jobs operation of the given type. */
if( pOperationLink == NULL )
{
/* Operation is not pending. It may have already been processed. Return
* without doing anything */
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
IotLogWarn( "Jobs %s callback received an unknown operation.",
_pAwsIotJobsOperationNames[ type ] );
IOT_GOTO_CLEANUP();
}
else
{
pOperation = IotLink_Container( _jobsOperation_t, pOperationLink, link );
/* Copy the flags from the Jobs operation. */
flags = pOperation->flags;
/* Remove a non-waitable operation from the pending operation list.
* Waitable operations are removed by the Wait function. */
if( ( flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == 0 )
{
IotListDouble_Remove( &( pOperation->link ) );
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
}
}
/* Parse the status from the topic name. */
status = AwsIot_ParseStatus( pMessage->u.message.info.pTopicName,
pMessage->u.message.info.topicNameLength );
switch( status )
{
case AWS_IOT_ACCEPTED:
case AWS_IOT_REJECTED:
_AwsIotJobs_ParseResponse( status,
pMessage->u.message.info.pPayload,
pMessage->u.message.info.payloadLength,
pOperation );
break;
default:
IotLogWarn( "Unknown status for %s of %.*s Jobs. Ignoring message.",
_pAwsIotJobsOperationNames[ type ],
pOperation->pSubscription->thingNameLength,
pOperation->pSubscription->pThingName );
pOperation->status = AWS_IOT_JOBS_BAD_RESPONSE;
break;
}
_notifyCompletion( pOperation );
/* For waitable operations, unlock the pending operation list mutex to allow
* the Wait function to run. */
if( ( flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )
{
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
}
/* This function has no return value and no cleanup, but uses the cleanup
* label to exit on error. */
IOT_FUNCTION_CLEANUP_BEGIN();
}
/*-----------------------------------------------------------*/
static void _notifyCompletion( _jobsOperation_t * pOperation )
{
AwsIotJobsCallbackParam_t callbackParam = { .callbackType = ( AwsIotJobsCallbackType_t ) 0 };
_jobsSubscription_t * pSubscription = pOperation->pSubscription,
* pRemovedSubscription = NULL;
/* If the operation is waiting, post to its wait semaphore and return. */
if( ( pOperation->flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )
{
IotSemaphore_Post( &( pOperation->notify.waitSemaphore ) );
}
else
{
/* Decrement the reference count. This also removes subscriptions if the
* count reaches 0. */
IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
_AwsIotJobs_DecrementReferences( pOperation,
pSubscription->pTopicBuffer,
&pRemovedSubscription );
IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
/* Set the subscription pointer used for the user callback based on whether
* a subscription was removed from the list. */
if( pRemovedSubscription != NULL )
{
pSubscription = pRemovedSubscription;
}
AwsIotJobs_Assert( pSubscription != NULL );
/* Invoke the user callback if provided. */
if( pOperation->notify.callback.function != NULL )
{
/* Set the common members of the callback parameter. */
callbackParam.callbackType = ( AwsIotJobsCallbackType_t ) pOperation->type;
callbackParam.mqttConnection = pOperation->mqttConnection;
callbackParam.pThingName = pSubscription->pThingName;
callbackParam.thingNameLength = pSubscription->thingNameLength;
callbackParam.u.operation.result = pOperation->status;
callbackParam.u.operation.reference = pOperation;
callbackParam.u.operation.pResponse = pOperation->pJobsResponse;
callbackParam.u.operation.responseLength = pOperation->jobsResponseLength;
pOperation->notify.callback.function( pOperation->notify.callback.pCallbackContext,
&callbackParam );
}
/* Destroy a removed subscription. */
if( pRemovedSubscription != NULL )
{
_AwsIotJobs_DestroySubscription( pRemovedSubscription );
}
_AwsIotJobs_DestroyOperation( pOperation );
}
}
/*-----------------------------------------------------------*/
static AwsIotJobsError_t _findSubscription( const AwsIotJobsRequestInfo_t * pRequestInfo,
char * pTopicBuffer,
uint16_t operationTopicLength,
_jobsOperation_t * pOperation,
bool * pFreeTopicBuffer )
{
AwsIotJobsError_t status = AWS_IOT_JOBS_SUCCESS;
_jobsSubscription_t * pSubscription = NULL;
/* Lookup table for Jobs operation callbacks. */
const AwsIotMqttCallbackFunction_t jobsCallbacks[ JOBS_OPERATION_COUNT ] =
{
_getPendingCallback,
_startNextCallback,
_describeCallback,
_updateCallback
};
/* Lock the subscriptions mutex for exclusive access. */
IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
/* Check for an existing subscription. This function will attempt to allocate
* a new subscription if not found. */
pSubscription = _AwsIotJobs_FindSubscription( pRequestInfo->pThingName,
pRequestInfo->thingNameLength,
true );
if( pSubscription == NULL )
{
status = AWS_IOT_JOBS_NO_MEMORY;
}
else
{
/* Ensure that the subscription Thing Name matches. */
AwsIotJobs_Assert( pSubscription != NULL );
AwsIotJobs_Assert( pSubscription->thingNameLength == pRequestInfo->thingNameLength );
AwsIotJobs_Assert( strncmp( pSubscription->pThingName,
pRequestInfo->pThingName,
pRequestInfo->thingNameLength ) == 0 );
/* Set the subscription object for the Jobs operation. */
pOperation->pSubscription = pSubscription;
/* Assign the topic buffer to the subscription to use for unsubscribing if
* the subscription has no topic buffer. */
if( pSubscription->pTopicBuffer == NULL )
{
pSubscription->pTopicBuffer = pTopicBuffer;
/* Don't free the topic buffer if it was allocated to the subscription. */
*pFreeTopicBuffer = false;
}
else
{
*pFreeTopicBuffer = true;
}
/* Increment the reference count for this Jobs operation's
* subscriptions. */
status = _AwsIotJobs_IncrementReferences( pOperation,
pTopicBuffer,
operationTopicLength,
jobsCallbacks[ pOperation->type ] );
if( status != AWS_IOT_JOBS_SUCCESS )
{
/* Failed to add subscriptions for a Jobs operation. The reference
* count was not incremented. Check if this subscription should be
* deleted. */
_AwsIotJobs_RemoveSubscription( pSubscription, NULL );
}
}
/* Unlock the Jobs subscription list mutex. */
IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
return status;
}
/*-----------------------------------------------------------*/
AwsIotJobsError_t _AwsIotJobs_CreateOperation( _jobsOperationType_t type,
const AwsIotJobsRequestInfo_t * pRequestInfo,
const _jsonRequestContents_t * pRequestContents,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
_jobsOperation_t ** pNewOperation )
{
IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );
size_t operationSize = sizeof( _jobsOperation_t );
_jobsOperation_t * pOperation = NULL;
IotLogDebug( "Creating operation record for Jobs %s.",
_pAwsIotJobsOperationNames[ type ] );
/* The Job ID must be saved for DESCRIBE and UPDATE operations. */
if( ( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) )
{
/* Ensure a valid Job ID is provided. */
AwsIotJobs_Assert( pRequestInfo->pJobId != NULL );
AwsIotJobs_Assert( pRequestInfo->jobIdLength > 1 );
AwsIotJobs_Assert( pRequestInfo->jobIdLength <= JOBS_MAX_ID_LENGTH );
operationSize += pRequestInfo->jobIdLength;
}
/* Allocate memory for a new Jobs operation. */
pOperation = AwsIotJobs_MallocOperation( operationSize );
if( pOperation == NULL )
{
IotLogError( "Failed to allocate memory for Jobs %s.",
_pAwsIotJobsOperationNames[ type ] );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );
}
/* Clear the operation data. */
( void ) memset( pOperation, 0x00, sizeof( _jobsOperation_t ) );
/* Set the remaining common members of the Jobs operation. */
pOperation->type = type;
pOperation->flags = flags;
pOperation->status = AWS_IOT_JOBS_STATUS_PENDING;
pOperation->mallocResponse = pRequestInfo->mallocResponse;
/* Save the Job ID for DESCRIBE and UPDATE operations. */
if( ( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) )
{
pOperation->jobIdLength = pRequestInfo->jobIdLength;
( void ) memcpy( pOperation->pJobId, pRequestInfo->pJobId, pRequestInfo->jobIdLength );
}
/* Generate a Jobs request document. */
status = _AwsIotJobs_GenerateJsonRequest( type,
pRequestInfo,
pRequestContents,
pOperation );
if( status != AWS_IOT_JOBS_SUCCESS )
{
IOT_GOTO_CLEANUP();
}
/* Check if the waitable flag is set. If it is, create a semaphore to
* wait on. */
if( ( flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )
{
if( IotSemaphore_Create( &( pOperation->notify.waitSemaphore ), 0, 1 ) == false )
{
IotLogError( "Failed to create semaphore for waitable Jobs %s.",
_pAwsIotJobsOperationNames[ type ] );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );
}
}
else
{
/* If the waitable flag isn't set but a callback is, copy the callback
* information. */
if( pCallbackInfo != NULL )
{
pOperation->notify.callback = *pCallbackInfo;
}
}
IOT_FUNCTION_CLEANUP_BEGIN();
/* Clean up on error. */
if( status != AWS_IOT_JOBS_SUCCESS )
{
if( pOperation != NULL )
{
if( pOperation->pJobsRequest != NULL )
{
AwsIotJobs_FreeString( ( void * ) ( pOperation->pJobsRequest ) );
}
AwsIotJobs_FreeOperation( pOperation );
}
}
else
{
/* Set the output parameter. */
*pNewOperation = pOperation;
}
IOT_FUNCTION_CLEANUP_END();
}
/*-----------------------------------------------------------*/
void _AwsIotJobs_DestroyOperation( void * pData )
{
_jobsOperation_t * pOperation = ( _jobsOperation_t * ) pData;
AwsIotJobs_Assert( pOperation != NULL );
IotLogDebug( "Destroying Jobs operation %s.",
_pAwsIotJobsOperationNames[ pOperation->type ] );
/* Check if a wait semaphore was created for this operation. */
if( ( pOperation->flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )
{
/* Destroy the wait semaphore */
IotSemaphore_Destroy( &( pOperation->notify.waitSemaphore ) );
}
/* Free any Jobs request documents. */
if( pOperation->pJobsRequest != NULL )
{
AwsIotJobs_Assert( pOperation->jobsRequestLength > 0 );
AwsIotJobs_FreeString( ( void * ) ( pOperation->pJobsRequest ) );
}
/* Free the memory used to hold operation data. */
AwsIotJobs_FreeOperation( pOperation );
}
/*-----------------------------------------------------------*/
AwsIotJobsError_t _AwsIotJobs_GenerateJobsTopic( _jobsOperationType_t type,
const AwsIotJobsRequestInfo_t * pRequestInfo,
char ** pTopicBuffer,
uint16_t * pOperationTopicLength )
{
AwsIotJobsError_t status = AWS_IOT_JOBS_SUCCESS;
AwsIotTopicInfo_t topicInfo = { 0 };
char pJobOperationName[ JOBS_LONGEST_SUFFIX_LENGTH ] = { 0 };
uint16_t operationNameLength = 0;
/* Lookup table for Jobs operation strings. */
const char * const pOperationString[ JOBS_OPERATION_COUNT ] =
{
JOBS_GET_PENDING_OPERATION_STRING, /* Jobs get pending operation. */
JOBS_START_NEXT_OPERATION_STRING, /* Jobs start next operation. */
JOBS_DESCRIBE_OPERATION_STRING, /* Jobs describe operation */
JOBS_UPDATE_OPERATION_STRING /* Jobs update operation. */
};
/* Lookup table for Jobs operation string lengths. */
const uint16_t pOperationStringLength[ JOBS_OPERATION_COUNT ] =
{
JOBS_GET_PENDING_OPERATION_STRING_LENGTH, /* Jobs get pending operation */
JOBS_START_NEXT_OPERATION_STRING_LENGTH, /* Jobs start next operation. */
JOBS_DESCRIBE_OPERATION_STRING_LENGTH, /* Jobs describe operation */
JOBS_UPDATE_OPERATION_STRING_LENGTH /* Jobs update operation. */
};
/* Ensure type is valid. */
AwsIotJobs_Assert( ( type == JOBS_GET_PENDING ) || ( type == JOBS_START_NEXT ) ||
( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) );
/* Set the members needed to generate an operation topic. */
topicInfo.pThingName = pRequestInfo->pThingName;
topicInfo.thingNameLength = pRequestInfo->thingNameLength;
topicInfo.longestSuffixLength = JOBS_LONGEST_SUFFIX_LENGTH;
topicInfo.mallocString = AwsIotJobs_MallocString;
/* Job operations that require a Job ID require additional processing to
* create an operation name with the Job ID. */
if( ( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) )
{
/* Ensure Job ID length is valid. */
AwsIotJobs_Assert( pRequestInfo->jobIdLength > 0 );
AwsIotJobs_Assert( pRequestInfo->jobIdLength <= JOBS_MAX_ID_LENGTH );
/* Construct the Jobs operation name with the Job ID. */
( void ) memcpy( pJobOperationName, "/jobs/", 6 );
operationNameLength = 6;
( void ) memcpy( pJobOperationName + operationNameLength,
pRequestInfo->pJobId,
pRequestInfo->jobIdLength );
operationNameLength = ( uint16_t ) ( pRequestInfo->jobIdLength + operationNameLength );
( void ) memcpy( pJobOperationName + operationNameLength,
pOperationString[ type ],
pOperationStringLength[ type ] );
operationNameLength = ( uint16_t ) ( operationNameLength + pOperationStringLength[ type ] );
topicInfo.pOperationName = pJobOperationName;
topicInfo.operationNameLength = operationNameLength;
}
else
{
topicInfo.pOperationName = pOperationString[ type ];
topicInfo.operationNameLength = pOperationStringLength[ type ];
}
if( AwsIot_GenerateOperationTopic( &topicInfo,
pTopicBuffer,
pOperationTopicLength ) == false )
{
status = AWS_IOT_JOBS_NO_MEMORY;
}
return status;
}
/*-----------------------------------------------------------*/
AwsIotJobsError_t _AwsIotJobs_ProcessOperation( const AwsIotJobsRequestInfo_t * pRequestInfo,
_jobsOperation_t * pOperation )
{
IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_STATUS_PENDING );
char * pTopicBuffer = NULL;
uint16_t operationTopicLength = 0;
bool freeTopicBuffer = true;
IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
IotMqttError_t publishStatus = IOT_MQTT_STATUS_PENDING;
IotLogDebug( "Processing Jobs operation %s for Thing %.*s.",
_pAwsIotJobsOperationNames[ pOperation->type ],
pRequestInfo->thingNameLength,
pRequestInfo->pThingName );
/* Set the operation's MQTT connection. */
pOperation->mqttConnection = pRequestInfo->mqttConnection;
/* Generate the operation topic buffer. */
status = _AwsIotJobs_GenerateJobsTopic( pOperation->type,
pRequestInfo,
&pTopicBuffer,
&operationTopicLength );
if( status != AWS_IOT_JOBS_SUCCESS )
{
IotLogError( "No memory for Jobs operation topic buffer." );
IOT_GOTO_CLEANUP();
}
/* Get a subscription object for this Jobs operation. */
status = _findSubscription( pRequestInfo,
pTopicBuffer,
operationTopicLength,
pOperation,
&freeTopicBuffer );
if( status != AWS_IOT_JOBS_SUCCESS )
{
/* No subscription was found and no subscription could be allocated. */
IOT_GOTO_CLEANUP();
}
/* Set the members for PUBLISH retry. */
publishInfo.qos = pRequestInfo->qos;
publishInfo.retryLimit = pRequestInfo->retryLimit;
publishInfo.retryMs = pRequestInfo->retryMs;
/* Set the payload as the Jobs request. */
publishInfo.pPayload = pOperation->pJobsRequest;
publishInfo.payloadLength = pOperation->jobsRequestLength;
/* Set the operation topic name. */
publishInfo.pTopicName = pTopicBuffer;
publishInfo.topicNameLength = operationTopicLength;
IotLogDebug( "Jobs %s message will be published to topic %.*s",
_pAwsIotJobsOperationNames[ pOperation->type ],
publishInfo.topicNameLength,
publishInfo.pTopicName );
/* Add Jobs operation to the pending operations list. */
IotMutex_Lock( &( _AwsIotJobsPendingOperationsMutex ) );
IotListDouble_InsertHead( &( _AwsIotJobsPendingOperations ),
&( pOperation->link ) );
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
/* Publish to the Jobs topic name. */
publishStatus = IotMqtt_PublishSync( pOperation->mqttConnection,
&publishInfo,
0,
_AwsIotJobsMqttTimeoutMs );
if( publishStatus != IOT_MQTT_SUCCESS )
{
IotLogError( "Failed to publish MQTT message to %s %.*s Jobs, error %s.",
_pAwsIotJobsOperationNames[ pOperation->type ],
pRequestInfo->thingNameLength,
pRequestInfo->pThingName,
IotMqtt_strerror( publishStatus ) );
/* Convert the MQTT "NO MEMORY" error to a Jobs "NO MEMORY" error. */
if( publishStatus == IOT_MQTT_NO_MEMORY )
{
status = AWS_IOT_JOBS_NO_MEMORY;
}
else
{
status = AWS_IOT_JOBS_MQTT_ERROR;
}
/* If the "keep subscriptions" flag is not set, decrement the reference
* count. */
if( ( pOperation->flags & AWS_IOT_JOBS_FLAG_KEEP_SUBSCRIPTIONS ) == 0 )
{
IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
_AwsIotJobs_DecrementReferences( pOperation,
pTopicBuffer,
NULL );
IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
}
/* Remove Jobs operation from the pending operations list. */
IotMutex_Lock( &( _AwsIotJobsPendingOperationsMutex ) );
IotListDouble_Remove( &( pOperation->link ) );
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
}
else
{
IotLogDebug( "Jobs %s PUBLISH message successfully sent.",
_pAwsIotJobsOperationNames[ pOperation->type ] );
}
IOT_FUNCTION_CLEANUP_BEGIN();
/* Free the topic buffer used by this function if it was not assigned to a
* subscription. */
if( ( freeTopicBuffer == true ) && ( pTopicBuffer != NULL ) )
{
AwsIotJobs_FreeString( pTopicBuffer );
}
/* Destroy the Jobs operation on failure. */
if( status != AWS_IOT_JOBS_SUCCESS )
{
_AwsIotJobs_DestroyOperation( pOperation );
}
else
{
/* Convert successful return code to "status pending", as the Jobs
* library is now waiting for a response from the service. */
status = AWS_IOT_JOBS_STATUS_PENDING;
}
IOT_FUNCTION_CLEANUP_END();
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,169 @@
/*
* AWS IoT Jobs V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_jobs_static_memory.c
* @brief Implementation of Jobs static memory functions.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* This file should only be compiled if dynamic memory allocation is forbidden. */
#if IOT_STATIC_MEMORY_ONLY == 1
/* Standard includes. */
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
/* Static memory include. */
#include "iot_static_memory.h"
/* Jobs internal include. */
#include "private/aws_iot_jobs_internal.h"
/*-----------------------------------------------------------*/
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Provide default values for undefined configuration constants.
*/
#ifndef AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS
#define AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS ( 10 )
#endif
#ifndef AWS_IOT_JOBS_SUBSCRIPTIONS
#define AWS_IOT_JOBS_SUBSCRIPTIONS ( 2 )
#endif
/** @endcond */
/* Validate static memory configuration settings. */
#if AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS <= 0
#error "AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS cannot be 0 or negative."
#endif
#if AWS_IOT_JOBS_SUBSCRIPTIONS <= 0
#error "AWS_IOT_JOBS_SUBSCRIPTIONS cannot be 0 or negative."
#endif
/**
* @brief The size of a static memory Jobs operation.
*
* Since the pJobId member of #_jobsOperation_t is variable-length,
* the constant `JOBS_MAX_ID_LENGTH` is used for the length of
* #_jobsOperation_t.pJobId.
*/
#define JOBS_OPERATION_SIZE ( sizeof( _jobsOperation_t ) + JOBS_MAX_ID_LENGTH )
/**
* @brief The size of a static memory Jobs subscription.
*
* Since the pThingName member of #_jobsSubscription_t is variable-length,
* the constant `AWS_IOT_MAX_THING_NAME_LENGTH` is used for the length of
* #_jobsSubscription_t.pThingName.
*/
#define JOBS_SUBSCRIPTION_SIZE ( sizeof( _jobsSubscription_t ) + AWS_IOT_MAX_THING_NAME_LENGTH )
/*-----------------------------------------------------------*/
/*
* Static memory buffers and flags, allocated and zeroed at compile-time.
*/
static uint32_t _pInUseJobsOperations[ AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS ] = { 0U }; /**< @brief Jobs operation in-use flags. */
static char _pJobsOperations[ AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS ][ JOBS_OPERATION_SIZE ] = { { 0 } }; /**< @brief Jobs operations. */
static uint32_t _pInUseJobsSubscriptions[ AWS_IOT_JOBS_SUBSCRIPTIONS ] = { 0U }; /**< @brief Jobs subscription in-use flags. */
static char _pJobsSubscriptions[ AWS_IOT_JOBS_SUBSCRIPTIONS ][ JOBS_SUBSCRIPTION_SIZE ] = { { 0 } }; /**< @brief Jobs subscriptions. */
/*-----------------------------------------------------------*/
void * AwsIotJobs_MallocOperation( size_t size )
{
int32_t freeIndex = -1;
void * pNewOperation = NULL;
/* Check size argument. */
if( size <= JOBS_OPERATION_SIZE )
{
/* Find a free Jobs operation. */
freeIndex = IotStaticMemory_FindFree( _pInUseJobsOperations,
AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS );
if( freeIndex != -1 )
{
pNewOperation = &( _pJobsOperations[ freeIndex ] );
}
}
return pNewOperation;
}
/*-----------------------------------------------------------*/
void AwsIotJobs_FreeOperation( void * ptr )
{
/* Return the in-use Jobs operation. */
IotStaticMemory_ReturnInUse( ptr,
_pJobsOperations,
_pInUseJobsOperations,
AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS,
JOBS_OPERATION_SIZE );
}
/*-----------------------------------------------------------*/
void * AwsIotJobs_MallocSubscription( size_t size )
{
int32_t freeIndex = -1;
void * pNewSubscription = NULL;
if( size <= JOBS_SUBSCRIPTION_SIZE )
{
/* Get the index of a free Jobs subscription. */
freeIndex = IotStaticMemory_FindFree( _pInUseJobsSubscriptions,
AWS_IOT_JOBS_SUBSCRIPTIONS );
if( freeIndex != -1 )
{
pNewSubscription = &( _pJobsSubscriptions[ freeIndex ][ 0 ] );
}
}
return pNewSubscription;
}
/*-----------------------------------------------------------*/
void AwsIotJobs_FreeSubscription( void * ptr )
{
/* Return the in-use Jobs subscription. */
IotStaticMemory_ReturnInUse( ptr,
_pJobsSubscriptions,
_pInUseJobsSubscriptions,
AWS_IOT_JOBS_SUBSCRIPTIONS,
JOBS_SUBSCRIPTION_SIZE );
}
/*-----------------------------------------------------------*/
#endif

View file

@ -0,0 +1,581 @@
/*
* AWS IoT Jobs V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_jobs_subscription.c
* @brief Implements functions for interacting with the Jobs library's
* subscription list.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* Jobs internal include. */
#include "private/aws_iot_jobs_internal.h"
/* Error handling include. */
#include "iot_error.h"
/* Platform layer includes. */
#include "platform/iot_threads.h"
/* MQTT include. */
#include "iot_mqtt.h"
/*-----------------------------------------------------------*/
/**
* @brief Match two #_jobsSubscription_t by Thing Name.
*
* @param[in] pSubscriptionLink Pointer to the link member of a #_jobsSubscription_t
* containing the Thing Name to check.
* @param[in] pMatch Pointer to a `AwsIotThingName_t`.
*
* @return `true` if the Thing Names match; `false` otherwise.
*/
static bool _jobsSubscription_match( const IotLink_t * pSubscriptionLink,
void * pMatch );
/*-----------------------------------------------------------*/
/**
* @brief List of active Jobs subscriptions objects.
*/
IotListDouble_t _AwsIotJobsSubscriptions = { 0 };
/**
* @brief Protects #_AwsIotJobsSubscriptions from concurrent access.
*/
IotMutex_t _AwsIotJobsSubscriptionsMutex;
/*-----------------------------------------------------------*/
static bool _jobsSubscription_match( const IotLink_t * pSubscriptionLink,
void * pMatch )
{
bool match = false;
/* Because this function is called from a container function, the given link
* must never be NULL. */
AwsIotJobs_Assert( pSubscriptionLink != NULL );
const _jobsSubscription_t * pSubscription = IotLink_Container( _jobsSubscription_t,
pSubscriptionLink,
link );
const AwsIotThingName_t * pThingName = ( AwsIotThingName_t * ) pMatch;
if( pThingName->thingNameLength == pSubscription->thingNameLength )
{
/* Check for matching Thing Names. */
match = ( strncmp( pThingName->pThingName,
pSubscription->pThingName,
pThingName->thingNameLength ) == 0 );
}
return match;
}
/*-----------------------------------------------------------*/
_jobsSubscription_t * _AwsIotJobs_FindSubscription( const char * pThingName,
size_t thingNameLength,
bool createIfNotFound )
{
_jobsSubscription_t * pSubscription = NULL;
IotLink_t * pSubscriptionLink = NULL;
AwsIotThingName_t thingName = { 0 };
thingName.pThingName = pThingName;
thingName.thingNameLength = thingNameLength;
/* Search the list for an existing subscription for Thing Name. */
pSubscriptionLink = IotListDouble_FindFirstMatch( &( _AwsIotJobsSubscriptions ),
NULL,
_jobsSubscription_match,
&thingName );
/* Check if a subscription was found. */
if( pSubscriptionLink == NULL )
{
if( createIfNotFound == true )
{
/* No subscription found. Allocate a new subscription. */
pSubscription = AwsIotJobs_MallocSubscription( sizeof( _jobsSubscription_t ) + thingNameLength );
if( pSubscription != NULL )
{
/* Clear the new subscription. */
( void ) memset( pSubscription, 0x00, sizeof( _jobsSubscription_t ) + thingNameLength );
/* Set the Thing Name length and copy the Thing Name into the new subscription. */
pSubscription->thingNameLength = thingNameLength;
( void ) memcpy( pSubscription->pThingName, pThingName, thingNameLength );
/* Add the new subscription to the subscription list. */
IotListDouble_InsertHead( &( _AwsIotJobsSubscriptions ),
&( pSubscription->link ) );
IotLogDebug( "Created new Jobs subscriptions object for %.*s.",
thingNameLength,
pThingName );
}
else
{
IotLogError( "Failed to allocate memory for %.*s Jobs subscriptions.",
thingNameLength,
pThingName );
}
}
}
else
{
IotLogDebug( "Found existing Jobs subscriptions object for %.*s.",
thingNameLength,
pThingName );
pSubscription = IotLink_Container( _jobsSubscription_t, pSubscriptionLink, link );
}
return pSubscription;
}
/*-----------------------------------------------------------*/
void _AwsIotJobs_RemoveSubscription( _jobsSubscription_t * pSubscription,
_jobsSubscription_t ** pRemovedSubscription )
{
IOT_FUNCTION_ENTRY( bool, true );
int32_t i = 0, callbackIndex = 0;
IotLogDebug( "Checking if subscription object for %.*s can be removed.",
pSubscription->thingNameLength,
pSubscription->pThingName );
/* Check for active operations. If any Jobs operation's subscription
* reference count is not 0, then the subscription cannot be removed. */
for( i = 0; i < JOBS_OPERATION_COUNT; i++ )
{
if( pSubscription->operationReferences[ i ] > 0 )
{
IotLogDebug( "Reference count %ld for %.*s subscription object. "
"Subscription cannot be removed yet.",
( long int ) pSubscription->operationReferences[ i ],
pSubscription->thingNameLength,
pSubscription->pThingName );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else if( pSubscription->operationReferences[ i ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
{
IotLogDebug( "Subscription object for %.*s has persistent subscriptions. "
"Subscription will not be removed.",
pSubscription->thingNameLength,
pSubscription->pThingName );
IOT_SET_AND_GOTO_CLEANUP( false );
}
}
/* Check for active subscriptions. If any Jobs callbacks are active, then the
* subscription cannot be removed. */
if( pSubscription->callbackReferences > 0 )
{
IotLogDebug( "Notify callbacks are using %.*s subscription object. "
"Subscription cannot be removed yet.",
pSubscription->thingNameLength,
pSubscription->pThingName );
IOT_SET_AND_GOTO_CLEANUP( false );
}
for( i = 0; i < JOBS_CALLBACK_COUNT; i++ )
{
for( callbackIndex = 0; callbackIndex < AWS_IOT_JOBS_NOTIFY_CALLBACKS; callbackIndex++ )
{
if( pSubscription->callbacks[ i ][ callbackIndex ].function != NULL )
{
IotLogDebug( "Found active Jobs %s callback for %.*s subscription object. "
"Subscription cannot be removed yet.",
_pAwsIotJobsCallbackNames[ i ],
pSubscription->thingNameLength,
pSubscription->pThingName );
IOT_SET_AND_GOTO_CLEANUP( false );
}
}
}
/* Remove the subscription if unused. */
IOT_FUNCTION_CLEANUP_BEGIN();
if( status == true )
{
/* No Jobs operation subscription references or active Jobs callbacks.
* Remove the subscription object. */
IotListDouble_Remove( &( pSubscription->link ) );
IotLogDebug( "Removed subscription object for %.*s.",
pSubscription->thingNameLength,
pSubscription->pThingName );
/* If the caller requested the removed subscription, set the output parameter.
* Otherwise, free the memory used by the subscription. */
if( pRemovedSubscription != NULL )
{
*pRemovedSubscription = pSubscription;
}
else
{
_AwsIotJobs_DestroySubscription( pSubscription );
}
}
}
/*-----------------------------------------------------------*/
void _AwsIotJobs_DestroySubscription( void * pData )
{
_jobsSubscription_t * pSubscription = ( _jobsSubscription_t * ) pData;
/* Free the topic buffer if allocated. */
if( pSubscription->pTopicBuffer != NULL )
{
AwsIotJobs_FreeString( pSubscription->pTopicBuffer );
}
/* Free memory used by subscription. */
AwsIotJobs_FreeSubscription( pSubscription );
}
/*-----------------------------------------------------------*/
AwsIotJobsError_t _AwsIotJobs_IncrementReferences( _jobsOperation_t * pOperation,
char * pTopicBuffer,
uint16_t operationTopicLength,
AwsIotMqttCallbackFunction_t callback )
{
IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );
const _jobsOperationType_t type = pOperation->type;
_jobsSubscription_t * pSubscription = pOperation->pSubscription;
IotMqttError_t subscriptionStatus = IOT_MQTT_STATUS_PENDING;
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
/* Do nothing if this operation has persistent subscriptions. */
if( pSubscription->operationReferences[ type ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
{
IotLogDebug( "Jobs %s for %.*s has persistent subscriptions. Reference "
"count will not be incremented.",
_pAwsIotJobsOperationNames[ type ],
pSubscription->thingNameLength,
pSubscription->pThingName );
IOT_GOTO_CLEANUP();
}
/* When persistent subscriptions are not present, the reference count must
* not be negative. */
AwsIotJobs_Assert( pSubscription->operationReferences[ type ] >= 0 );
/* Check if there are any existing references for this operation. */
if( pSubscription->operationReferences[ type ] == 0 )
{
/* Set the parameters needed to add subscriptions. */
subscriptionInfo.mqttConnection = pOperation->mqttConnection;
subscriptionInfo.callbackFunction = callback;
subscriptionInfo.timeout = _AwsIotJobsMqttTimeoutMs;
subscriptionInfo.pTopicFilterBase = pTopicBuffer;
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
subscriptionStatus = AwsIot_ModifySubscriptions( IotMqtt_SubscribeSync,
&subscriptionInfo );
/* Convert MQTT return code to Jobs return code. */
switch( subscriptionStatus )
{
case IOT_MQTT_SUCCESS:
status = AWS_IOT_JOBS_SUCCESS;
break;
case IOT_MQTT_NO_MEMORY:
status = AWS_IOT_JOBS_NO_MEMORY;
break;
default:
status = AWS_IOT_JOBS_MQTT_ERROR;
break;
}
if( status != AWS_IOT_JOBS_SUCCESS )
{
IOT_GOTO_CLEANUP();
}
}
/* Increment the number of subscription references for this operation when
* the keep subscriptions flag is not set. */
if( ( pOperation->flags & AWS_IOT_JOBS_FLAG_KEEP_SUBSCRIPTIONS ) == 0 )
{
( pSubscription->operationReferences[ type ] )++;
IotLogDebug( "Jobs %s subscriptions for %.*s now has count %d.",
_pAwsIotJobsOperationNames[ type ],
pSubscription->thingNameLength,
pSubscription->pThingName,
pSubscription->operationReferences[ type ] );
}
/* Otherwise, set the persistent subscriptions flag. */
else
{
pSubscription->operationReferences[ type ] = AWS_IOT_PERSISTENT_SUBSCRIPTION;
IotLogDebug( "Set persistent subscriptions flag for Jobs %s of %.*s.",
_pAwsIotJobsOperationNames[ type ],
pSubscription->thingNameLength,
pSubscription->pThingName );
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
void _AwsIotJobs_DecrementReferences( _jobsOperation_t * pOperation,
char * pTopicBuffer,
_jobsSubscription_t ** pRemovedSubscription )
{
const _jobsOperationType_t type = pOperation->type;
_jobsSubscription_t * pSubscription = pOperation->pSubscription;
uint16_t operationTopicLength = 0;
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
/* Do nothing if this Jobs operation has persistent subscriptions. */
if( pSubscription->operationReferences[ type ] != AWS_IOT_PERSISTENT_SUBSCRIPTION )
{
/* Decrement the number of subscription references for this operation.
* Ensure that it's positive. */
( pSubscription->operationReferences[ type ] )--;
AwsIotJobs_Assert( pSubscription->operationReferences[ type ] >= 0 );
/* Check if the number of references has reached 0. */
if( pSubscription->operationReferences[ type ] == 0 )
{
IotLogDebug( "Reference count for %.*s %s is 0. Unsubscribing.",
pSubscription->thingNameLength,
pSubscription->pThingName,
_pAwsIotJobsOperationNames[ type ] );
/* Subscription must have a topic buffer. */
AwsIotJobs_Assert( pSubscription->pTopicBuffer != NULL );
/* Set the parameters needed to generate a Jobs topic. */
requestInfo.pThingName = pSubscription->pThingName;
requestInfo.thingNameLength = pSubscription->thingNameLength;
requestInfo.pJobId = pOperation->pJobId;
requestInfo.jobIdLength = pOperation->jobIdLength;
/* Generate the prefix of the Jobs topic. This function will not
* fail when given a buffer. */
( void ) _AwsIotJobs_GenerateJobsTopic( ( _jobsOperationType_t ) type,
&requestInfo,
&( pSubscription->pTopicBuffer ),
&operationTopicLength );
/* Set the parameters needed to remove subscriptions. */
subscriptionInfo.mqttConnection = pOperation->mqttConnection;
subscriptionInfo.timeout = _AwsIotJobsMqttTimeoutMs;
subscriptionInfo.pTopicFilterBase = pTopicBuffer;
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
( void ) AwsIot_ModifySubscriptions( IotMqtt_UnsubscribeSync,
&subscriptionInfo );
}
/* Check if this subscription should be deleted. */
_AwsIotJobs_RemoveSubscription( pSubscription,
pRemovedSubscription );
}
else
{
IotLogDebug( "Jobs %s for %.*s has persistent subscriptions. Reference "
"count will not be decremented.",
_pAwsIotJobsOperationNames[ type ],
pSubscription->thingNameLength,
pSubscription->pThingName );
}
}
/*-----------------------------------------------------------*/
AwsIotJobsError_t AwsIotJobs_RemovePersistentSubscriptions( const AwsIotJobsRequestInfo_t * pRequestInfo,
uint32_t flags )
{
IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );
int32_t i = 0;
uint16_t operationTopicLength = 0;
IotMqttError_t unsubscribeStatus = IOT_MQTT_STATUS_PENDING;
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
_jobsSubscription_t * pSubscription = NULL;
IotLink_t * pSubscriptionLink = NULL;
AwsIotThingName_t thingName = { 0 };
thingName.pThingName = pRequestInfo->pThingName;
thingName.thingNameLength = pRequestInfo->thingNameLength;
IotLogInfo( "Removing persistent subscriptions for %.*s.",
pRequestInfo->thingNameLength,
pRequestInfo->pThingName );
/* Check parameters. */
if( pRequestInfo->mqttConnection == IOT_MQTT_CONNECTION_INITIALIZER )
{
IotLogError( "MQTT connection is not initialized." );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
}
if( AwsIot_ValidateThingName( pRequestInfo->pThingName,
pRequestInfo->thingNameLength ) == false )
{
IotLogError( "Thing Name is not valid." );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
}
if( ( ( flags & AWS_IOT_JOBS_FLAG_REMOVE_DESCRIBE_SUBSCRIPTIONS ) != 0 ) ||
( ( flags & AWS_IOT_JOBS_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS ) != 0 ) )
{
if( ( pRequestInfo->pJobId == NULL ) || ( pRequestInfo->jobIdLength == 0 ) )
{
IotLogError( "Job ID must be set." );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
}
if( pRequestInfo->jobIdLength > JOBS_MAX_ID_LENGTH )
{
IotLogError( "Job ID cannot be longer than %d.",
JOBS_MAX_ID_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
}
}
IotMutex_Lock( &( _AwsIotJobsSubscriptionsMutex ) );
/* Search the list for an existing subscription for Thing Name. */
pSubscriptionLink = IotListDouble_FindFirstMatch( &( _AwsIotJobsSubscriptions ),
NULL,
_jobsSubscription_match,
&thingName );
if( pSubscriptionLink != NULL )
{
IotLogDebug( "Found subscription object for %.*s. Checking for persistent "
"subscriptions to remove.",
pRequestInfo->thingNameLength,
pRequestInfo->pThingName );
pSubscription = IotLink_Container( _jobsSubscription_t, pSubscriptionLink, link );
for( i = 0; i < JOBS_OPERATION_COUNT; i++ )
{
if( ( flags & ( 0x1UL << i ) ) != 0 )
{
IotLogDebug( "Removing %.*s %s persistent subscriptions.",
pRequestInfo->thingNameLength,
pRequestInfo->pThingName,
_pAwsIotJobsOperationNames[ i ] );
/* Subscription must have a topic buffer. */
AwsIotJobs_Assert( pSubscription->pTopicBuffer != NULL );
if( pSubscription->operationReferences[ i ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
{
/* Generate the prefix of the Jobs topic. This function will not
* fail when given a buffer. */
( void ) _AwsIotJobs_GenerateJobsTopic( ( _jobsOperationType_t ) i,
pRequestInfo,
&( pSubscription->pTopicBuffer ),
&operationTopicLength );
/* Set the parameters needed to remove subscriptions. */
subscriptionInfo.mqttConnection = pRequestInfo->mqttConnection;
subscriptionInfo.timeout = _AwsIotJobsMqttTimeoutMs;
subscriptionInfo.pTopicFilterBase = pSubscription->pTopicBuffer;
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
unsubscribeStatus = AwsIot_ModifySubscriptions( IotMqtt_UnsubscribeSync,
&subscriptionInfo );
/* Convert MQTT return code to Shadow return code. */
switch( unsubscribeStatus )
{
case IOT_MQTT_SUCCESS:
status = AWS_IOT_JOBS_SUCCESS;
break;
case IOT_MQTT_NO_MEMORY:
status = AWS_IOT_JOBS_NO_MEMORY;
break;
default:
status = AWS_IOT_JOBS_MQTT_ERROR;
break;
}
if( status != AWS_IOT_JOBS_SUCCESS )
{
break;
}
/* Clear the persistent subscriptions flag and check if the
* subscription can be removed. */
pSubscription->operationReferences[ i ] = 0;
_AwsIotJobs_RemoveSubscription( pSubscription, NULL );
}
else
{
IotLogDebug( "%.*s %s does not have persistent subscriptions.",
pRequestInfo->thingNameLength,
pRequestInfo->pThingName,
_pAwsIotJobsOperationNames[ i ] );
}
}
}
}
else
{
IotLogWarn( "No subscription object found for %.*s",
pRequestInfo->thingNameLength,
pRequestInfo->pThingName );
}
IotMutex_Unlock( &( _AwsIotJobsSubscriptionsMutex ) );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,628 @@
/*
* AWS IoT Jobs V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_jobs_internal.h
* @brief Internal header of Jobs library. This header should not be included in
* typical application code.
*/
#ifndef AWS_IOT_JOBS_INTERNAL_H_
#define AWS_IOT_JOBS_INTERNAL_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Linear containers (lists and queues) include. */
#include "iot_linear_containers.h"
/* Jobs include. */
#include "aws_iot_jobs.h"
/* AWS IoT include. */
#include "aws_iot.h"
/**
* @def AwsIotJobs_Assert( expression )
* @brief Assertion macro for the Jobs library.
*
* Set @ref AWS_IOT_JOBS_ENABLE_ASSERTS to `1` to enable assertions in the Jobs
* library.
*
* @param[in] expression Expression to be evaluated.
*/
#if AWS_IOT_JOBS_ENABLE_ASSERTS == 1
#ifndef AwsIotJobs_Assert
#ifdef Iot_DefaultAssert
#define AwsIotJobs_Assert( expression ) Iot_DefaultAssert( expression )
#else
#error "Asserts are enabled for Jobs, but AwsIotJobs_Assert is not defined"
#endif
#endif
#else
#define AwsIotJobs_Assert( expression )
#endif
/* Configure logs for Jobs functions. */
#ifdef AWS_IOT_LOG_LEVEL_JOBS
#define LIBRARY_LOG_LEVEL AWS_IOT_LOG_LEVEL_JOBS
#else
#ifdef IOT_LOG_LEVEL_GLOBAL
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL
#else
#define LIBRARY_LOG_LEVEL IOT_LOG_NONE
#endif
#endif
#define LIBRARY_LOG_NAME ( "Jobs" )
#include "iot_logging_setup.h"
/*
* Provide default values for undefined memory allocation functions based on
* the usage of dynamic memory allocation.
*/
#if IOT_STATIC_MEMORY_ONLY == 1
#include "iot_static_memory.h"
/**
* @brief Allocate a #_jobsOperation_t. This function should have the same
* signature as [malloc]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
*/
void * AwsIotJobs_MallocOperation( size_t size );
/**
* @brief Free a #_jobsOperation_t. This function should have the same
* signature as [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
void AwsIotJobs_FreeOperation( void * ptr );
/**
* @brief Allocate a buffer for a short string, used for topic names or client
* tokens. This function should have the same signature as [malloc]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
*/
#define AwsIotJobs_MallocString Iot_MallocMessageBuffer
/**
* @brief Free a string. This function should have the same signature as
* [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
#define AwsIotJobs_FreeString Iot_FreeMessageBuffer
/**
* @brief Allocate a #_jobsSubscription_t. This function should have the
* same signature as [malloc]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
*/
void * AwsIotJobs_MallocSubscription( size_t size );
/**
* @brief Free a #_jobsSubscription_t. This function should have the same
* signature as [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
void AwsIotJobs_FreeSubscription( void * ptr );
#else /* if IOT_STATIC_MEMORY_ONLY == 1 */
#ifndef AwsIotJobs_MallocOperation
#ifdef Iot_DefaultMalloc
#define AwsIotJobs_MallocOperation Iot_DefaultMalloc
#else
#error "No malloc function defined for AwsIotJobs_MallocOperation"
#endif
#endif
#ifndef AwsIotJobs_FreeOperation
#ifdef Iot_DefaultFree
#define AwsIotJobs_FreeOperation Iot_DefaultFree
#else
#error "No free function defined for AwsIotJobs_FreeOperation"
#endif
#endif
#ifndef AwsIotJobs_MallocString
#ifdef Iot_DefaultMalloc
#define AwsIotJobs_MallocString Iot_DefaultMalloc
#else
#error "No malloc function defined for AwsIotJobs_MallocString"
#endif
#endif
#ifndef AwsIotJobs_FreeString
#ifdef Iot_DefaultFree
#define AwsIotJobs_FreeString Iot_DefaultFree
#else
#error "No free function defined for AwsIotJobs_FreeString"
#endif
#endif
#ifndef AwsIotJobs_MallocSubscription
#ifdef Iot_DefaultMalloc
#define AwsIotJobs_MallocSubscription Iot_DefaultMalloc
#else
#error "No malloc function defined for AwsIotJobs_MallocSubscription"
#endif
#endif
#ifndef AwsIotJobs_FreeSubscription
#ifdef Iot_DefaultFree
#define AwsIotJobs_FreeSubscription Iot_DefaultFree
#else
#error "No free function defined for AwsIotJobs_FreeSubscription"
#endif
#endif
#endif /* if IOT_STATIC_MEMORY_ONLY == 1 */
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Provide default values for undefined configuration constants.
*/
#ifndef AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS
#define AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS ( 5000 )
#endif
#ifndef AWS_IOT_JOBS_NOTIFY_CALLBACKS
#define AWS_IOT_JOBS_NOTIFY_CALLBACKS ( 1 )
#endif
/** @endcond */
/**
* @brief The number of currently available Jobs operations.
*
* The 4 Jobs operations are GET PENDING, START NEXT, DESCRIBE, and UPDATE.
*/
#define JOBS_OPERATION_COUNT ( 4 )
/**
* @brief The number of currently available Jobs callbacks.
*
* The 2 Jobs callbacks are `jobs/notify` (AKA "Notify Pending") and
* `/jobs/notify-next` (AKA "Notify Next").
*/
#define JOBS_CALLBACK_COUNT ( 2 )
/**
* @brief The string representing a Jobs GET PENDING operation in a Jobs MQTT topic.
*/
#define JOBS_GET_PENDING_OPERATION_STRING "/jobs/get"
/**
* @brief The length of #JOBS_GET_PENDING_OPERATION_STRING.
*/
#define JOBS_GET_PENDING_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_GET_PENDING_OPERATION_STRING ) - 1 ) )
/**
* @brief The string representing a Jobs START NEXT operation in a Jobs MQTT topic.
*/
#define JOBS_START_NEXT_OPERATION_STRING "/jobs/start-next"
/**
* @brief The length of #JOBS_START_NEXT_OPERATION_STRING.
*/
#define JOBS_START_NEXT_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_START_NEXT_OPERATION_STRING ) - 1 ) )
/**
* @brief The string representing a Jobs DESCRIBE operation in a Jobs MQTT topic.
*
* This string should be placed after a Job ID.
*/
#define JOBS_DESCRIBE_OPERATION_STRING "/get"
/**
* @brief The length of #JOBS_DESCRIBE_OPERATION_STRING.
*/
#define JOBS_DESCRIBE_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_DESCRIBE_OPERATION_STRING ) - 1 ) )
/**
* @brief The string representing a Jobs UPDATE operation in a Jobs MQTT topic.
*
* This string should be placed after a Job ID.
*/
#define JOBS_UPDATE_OPERATION_STRING "/update"
/**
* @brief The length of #JOBS_UPDATE_OPERATION_STRING.
*
* This length excludes the length of the placeholder %s.
*/
#define JOBS_UPDATE_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_UPDATE_OPERATION_STRING ) - 1 ) )
/**
* @brief The string representing the Jobs MQTT topic for receiving notifications
* of pending Jobs.
*/
#define JOBS_NOTIFY_PENDING_CALLBACK_STRING "/jobs/notify"
/**
* @brief The length of #JOBS_NOTIFY_PENDING_CALLBACK_STRING.
*/
#define JOBS_NOTIFY_PENDING_CALLBACK_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_NOTIFY_PENDING_CALLBACK_STRING ) - 1 ) )
/**
* @brief The string representing the Jobs MQTT topic for receiving notifications
* of the next pending Job.
*/
#define JOBS_NOTIFY_NEXT_CALLBACK_STRING "/jobs/notify-next"
/**
* @brief The length of #JOBS_NOTIFY_NEXT_CALLBACK_STRING.
*/
#define JOBS_NOTIFY_NEXT_CALLBACK_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_NOTIFY_NEXT_CALLBACK_STRING ) - 1 ) )
/**
* @brief The maximum length of a Job ID, per AWS IoT Service Limits.
*
* See https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#job-limits
*/
#define JOBS_MAX_ID_LENGTH ( 64 )
/**
* @brief The maximum value of the Jobs step timeout, per AWS IoT Service Limits.
*
* See https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#job-limits
*/
#define JOBS_MAX_TIMEOUT ( 10080 )
/**
* @brief A limit on the maximum length of a Jobs status details, per AWS IoT
* Service Limits.
*
* See https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#job-limits
*
* This is actually the limit on the length of an entire Jobs document; but the
* status details must also not exceed this length,
*/
#define JOBS_MAX_STATUS_DETAILS_LENGTH ( 32768 )
/**
* @brief The length of the longest Jobs topic suffix.
*
* This is the length of the longest Job ID, plus the length of the "UPDATE"
* operation suffix, plus the length of "/jobs/".
*/
#define JOBS_LONGEST_SUFFIX_LENGTH ( JOBS_MAX_ID_LENGTH + JOBS_UPDATE_OPERATION_STRING_LENGTH + 6 )
/*------------------------ Jobs internal data types -------------------------*/
/**
* @brief Enumerations representing each of the Jobs library's API functions.
*/
typedef enum _jobsOperationType
{
/* Jobs operations. */
JOBS_GET_PENDING = 0, /**< @ref jobs_function_getpendingasync */
JOBS_START_NEXT = 1, /**< @ref jobs_function_startnextasync */
JOBS_DESCRIBE = 2, /**< @ref jobs_function_describeasync */
JOBS_UPDATE = 3, /**< @ref jobs_function_updateasync */
/* Jobs callbacks. */
SET_NOTIFY_PENDING_CALLBACK = 4, /**< @ref jobs_function_setnotifypendingcallback */
SET_NOTIFY_NEXT_CALLBACK = 5 /**< @ref jobs_function_setnotifynextcallback */
} _jobsOperationType_t;
/**
* @brief Enumerations representing each of the Jobs callback functions.
*/
typedef enum _jobsCallbackType
{
NOTIFY_PENDING_CALLBACK = 0, /**< Pending Job notification callback. */
NOTIFY_NEXT_CALLBACK = 1 /**< Next Job notification callback. */
} _jobsCallbackType_t;
/**
* @brief Parameter to #_AwsIotJobs_GenerateJsonRequest.
*/
typedef union _jsonRequestContents
{
const AwsIotJobsUpdateInfo_t * pUpdateInfo; /**< @brief Valid for #JOBS_START_NEXT and #JOBS_UPDATE. */
struct
{
int32_t executionNumber; /**< @brief Execution number. */
bool includeJobDocument; /**< @brief Whether the response should include the Job document. */
} describe; /**< @brief Valid for #JOBS_DESCRIBE. */
} _jsonRequestContents_t;
/**
* @brief Represents a Jobs subscriptions object.
*
* These structures are stored in a list.
*/
typedef struct _jobsSubscription
{
IotLink_t link; /**< @brief List link member. */
int32_t operationReferences[ JOBS_OPERATION_COUNT ]; /**< @brief Reference counters for Jobs operation topics. */
int32_t callbackReferences; /**< @brief Reference counter for Jobs callbacks. */
/** @brief Jobs callbacks for this Thing. */
AwsIotJobsCallbackInfo_t callbacks[ JOBS_CALLBACK_COUNT ][ AWS_IOT_JOBS_NOTIFY_CALLBACKS ];
/**
* @brief Buffer allocated for removing Jobs topics.
*
* This buffer is pre-allocated to ensure that memory is available when
* unsubscribing.
*/
char * pTopicBuffer;
size_t thingNameLength; /**< @brief Length of Thing Name. */
char pThingName[]; /**< @brief Thing Name associated with this subscriptions object. */
} _jobsSubscription_t;
/**
* @brief Internal structure representing a single Jobs operation.
*
* A list of these structures keeps track of all in-progress Jobs operations.
*/
typedef struct _jobsOperation
{
IotLink_t link; /**< @brief List link member. */
/* Basic operation information. */
_jobsOperationType_t type; /**< @brief Operation type. */
uint32_t flags; /**< @brief Flags passed to operation API function. */
AwsIotJobsError_t status; /**< @brief Status of operation. */
IotMqttConnection_t mqttConnection; /**< @brief MQTT connection associated with this operation. */
_jobsSubscription_t * pSubscription; /**< @brief Jobs subscriptions object associated with this operation. */
/* Jobs request information. */
const char * pJobsRequest; /**< @brief JSON document to send to the Jobs service. */
size_t jobsRequestLength; /**< @brief Length of #_jobsOperation_t.pJobsRequest. */
const char * pClientToken; /**< @brief Client token sent with request. */
size_t clientTokenLength; /**< @brief Length of #_jobsOperation_t.pClientToken. */
/* Jobs response information. */
const char * pJobsResponse; /**< @brief Response received from the Jobs service. */
size_t jobsResponseLength; /**< @brief Length of #_jobsOperation_t.pJobsResponse. */
/**
* @brief Function to allocate memory for an incoming Jobs response.
*
* Only used when the flag #AWS_IOT_JOBS_FLAG_WAITABLE is set.
*/
void * ( *mallocResponse )( size_t );
/* How to notify of an operation's completion. */
union
{
IotSemaphore_t waitSemaphore; /**< @brief Semaphore to be used with @ref jobs_function_wait. */
AwsIotJobsCallbackInfo_t callback; /**< @brief User-provided callback function and parameter. */
} notify; /**< @brief How to notify of an operation's completion. */
size_t jobIdLength; /**< @brief Length of #_jobsOperation_t.pJobId. */
char pJobId[]; /**< @brief Job ID, saved for DESCRIBE and UPDATE operations. */
} _jobsOperation_t;
/* Declarations of names printed in logs. */
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
extern const char * const _pAwsIotJobsOperationNames[];
extern const char * const _pAwsIotJobsCallbackNames[];
#endif
/* Declarations of variables for internal Jobs files. */
extern uint32_t _AwsIotJobsMqttTimeoutMs;
extern IotListDouble_t _AwsIotJobsPendingOperations;
extern IotListDouble_t _AwsIotJobsSubscriptions;
extern IotMutex_t _AwsIotJobsPendingOperationsMutex;
extern IotMutex_t _AwsIotJobsSubscriptionsMutex;
/*------------------------ Jobs operation functions -------------------------*/
/**
* @brief Create a record for a new in-progress Jobs operation.
*
* @param[in] type The type of Jobs operation for the request.
* @param[in] pRequestInfo Common Jobs request parameters.
* @param[in] pRequestContents Additional values to place in the JSON document,
* depending on `type`.
* @param[in] flags Flags variables passed to a user-facing Jobs function.
* @param[in] pCallbackInfo User-provided callback function and parameter.
* @param[out] pNewOperation Set to point to the new operation on success.
*
* @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_NO_MEMORY
*/
AwsIotJobsError_t _AwsIotJobs_CreateOperation( _jobsOperationType_t type,
const AwsIotJobsRequestInfo_t * pRequestInfo,
const _jsonRequestContents_t * pRequestContents,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
_jobsOperation_t ** pNewOperation );
/**
* @brief Free resources used to record a Jobs operation. This is called when
* the operation completes.
*
* @param[in] pData The operation which completed. This parameter is of type
* `void*` to match the signature of [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
void _AwsIotJobs_DestroyOperation( void * pData );
/**
* @brief Fill a buffer with a Jobs topic.
*
* @param[in] type One of: GET PENDING, START NEXT, DESCRIBE, or UPDATE.
* @param[in] pRequestInfo Common Jobs request parameters.
* @param[out] pTopicBuffer Address of the buffer for the Jobs topic. If the
* pointer at this address is `NULL`, this function will allocate a new buffer;
* otherwise, it will use the provided buffer.
* @param[out] pOperationTopicLength Length of the Jobs operation topic (excluding
* any suffix) placed in `pTopicBuffer`.
*
* @warning This function does not check the length of `pTopicBuffer`! Any provided
* buffer must be large enough to accommodate the full Jobs topic, plus
* #JOBS_LONGEST_SUFFIX_LENGTH.
*
* @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_NO_MEMORY. This function
* will not return #AWS_IOT_JOBS_NO_MEMORY when a buffer is provided.
*/
AwsIotJobsError_t _AwsIotJobs_GenerateJobsTopic( _jobsOperationType_t type,
const AwsIotJobsRequestInfo_t * pRequestInfo,
char ** pTopicBuffer,
uint16_t * pOperationTopicLength );
/**
* @brief Process a Jobs operation by sending the necessary MQTT packets.
*
* @param[in] pRequestInfo Common Jobs request parameters.
* @param[in] pOperation Operation data to process.
*
* @return #AWS_IOT_JOBS_STATUS_PENDING on success. On error, one of
* #AWS_IOT_JOBS_NO_MEMORY or #AWS_IOT_JOBS_MQTT_ERROR.
*/
AwsIotJobsError_t _AwsIotJobs_ProcessOperation( const AwsIotJobsRequestInfo_t * pRequestInfo,
_jobsOperation_t * pOperation );
/*----------------------- Jobs subscription functions -----------------------*/
/**
* @brief Find a Jobs subscription object. May create a new subscription object
* and adds it to the subscription list if not found.
*
* @param[in] pThingName Thing Name in the subscription object.
* @param[in] thingNameLength Length of `pThingName`.
* @param[in] createIfNotFound If `true`, attempt to create a new subscription
* object if no match is found.
*
* @return Pointer to a Jobs subscription object, either found or newly
* allocated. Returns `NULL` if no subscription object is found and a new
* subscription object could not be allocated.
*
* @note This function should be called with the subscription list mutex locked.
*/
_jobsSubscription_t * _AwsIotJobs_FindSubscription( const char * pThingName,
size_t thingNameLength,
bool createIfNotFound );
/**
* @brief Remove a Jobs subscription object from the subscription list if
* unreferenced.
*
* @param[in] pSubscription Subscription object to check. If this object has no
* active references, it is removed from the subscription list.
* @param[out] pRemovedSubscription Removed subscription object, if any. Optional;
* pass `NULL` to ignore. If not `NULL`, this parameter will be set to the removed
* subscription and that subscription will not be destroyed.
*
* @note This function should be called with the subscription list mutex locked.
*/
void _AwsIotJobs_RemoveSubscription( _jobsSubscription_t * pSubscription,
_jobsSubscription_t ** pRemovedSubscription );
/**
* @brief Free resources used for a Jobs subscription object.
*
* @param[in] pData The subscription object to destroy. This parameter is of type
* `void*` to match the signature of [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
void _AwsIotJobs_DestroySubscription( void * pData );
/**
* @brief Increment the reference count of a Jobs subscriptions object.
*
* Also adds MQTT subscriptions if necessary.
*
* @param[in] pOperation The operation for which the reference count should be
* incremented.
* @param[in] pTopicBuffer Topic buffer containing the operation topic, used if
* subscriptions need to be added.
* @param[in] operationTopicLength The length of the operation topic in `pTopicBuffer`.
* @param[in] callback MQTT callback function for when this operation completes.
*
* @warning This function does not check the length of `pTopicBuffer`! Any provided
* buffer must already contain the Jobs operation topic, plus enough space for the
* status suffix.
*
* @return #AWS_IOT_JOBS_SUCCESS on success. On error, one of
* #AWS_IOT_JOBS_NO_MEMORY or #AWS_IOT_JOBS_MQTT_ERROR.
*
* @note This function should be called with the subscription list mutex locked.
*/
AwsIotJobsError_t _AwsIotJobs_IncrementReferences( _jobsOperation_t * pOperation,
char * pTopicBuffer,
uint16_t operationTopicLength,
AwsIotMqttCallbackFunction_t callback );
/**
* @brief Decrement the reference count of a Jobs subscriptions object.
*
* Also removed MQTT subscriptions and deletes the subscription object if necessary.
*
* @param[in] pOperation The operation for which the reference count should be
* decremented.
* @param[in] pTopicBuffer Topic buffer containing the operation topic, used if
* subscriptions need to be removed.
* @param[out] pRemovedSubscription Set to point to a removed subscription.
* Optional; pass `NULL` to ignore. If not `NULL`, this function will not destroy
* a removed subscription.
*
* @warning This function does not check the length of `pTopicBuffer`! Any provided
* buffer must be large enough to accommodate the full Jobs topic, plus
* #JOBS_LONGEST_SUFFIX_LENGTH.
*
* @note This function should be called with the subscription list mutex locked.
*/
void _AwsIotJobs_DecrementReferences( _jobsOperation_t * pOperation,
char * pTopicBuffer,
_jobsSubscription_t ** pRemovedSubscription );
/*------------------------ Jobs serializer functions ------------------------*/
/**
* @brief Generates a Jobs JSON request document from an #AwsIotJobsRequestInfo_t
* and an #AwsIotJobsUpdateInfo_t.
*
* @param[in] type The type of Jobs operation for the request.
* @param[in] pRequestInfo Common Jobs request parameters.
* @param[in] pRequestContents Additional values to place in the JSON document,
* depending on `type`.
* @param[in] pOperation Operation associated with the Jobs request.
*
* @return #AWS_IOT_JOBS_SUCCESS on success; otherwise, #AWS_IOT_JOBS_NO_MEMORY.
*/
AwsIotJobsError_t _AwsIotJobs_GenerateJsonRequest( _jobsOperationType_t type,
const AwsIotJobsRequestInfo_t * pRequestInfo,
const _jsonRequestContents_t * pRequestContents,
_jobsOperation_t * pOperation );
/**
* @brief Parse a response received from the Jobs service.
*
* @param[in] status Either ACCEPTED or REJECTED.
* @param[in] pResponse The response received from the Jobs service.
* @param[in] responseLength Length of `pResponse`.
* @param[out] pOperation Associated Jobs operation, where parse results are
* written.
*/
void _AwsIotJobs_ParseResponse( AwsIotStatus_t status,
const char * pResponse,
size_t responseLength,
_jobsOperation_t * pOperation );
#endif /* ifndef AWS_IOT_JOBS_INTERNAL_H_ */