2 * AWS IoT Jobs V1.0.0
\r
3 * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
6 * this software and associated documentation files (the "Software"), to deal in
\r
7 * the Software without restriction, including without limitation the rights to
\r
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
9 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
10 * subject to the following conditions:
\r
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\r
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
24 * @file aws_iot_jobs_api.c
\r
25 * @brief Implements the user-facing functions of the Jobs library.
\r
28 /* The config header is always included first. */
\r
29 #include "iot_config.h"
\r
31 /* Standard includes. */
\r
34 /* Platform threads include. */
\r
35 #include "platform/iot_threads.h"
\r
37 /* Jobs internal include. */
\r
38 #include "private/aws_iot_jobs_internal.h"
\r
40 /* Error handling include. */
\r
41 #include "iot_error.h"
\r
44 #include "iot_mqtt.h"
\r
46 /* Validate Jobs configuration settings. */
\r
47 #if AWS_IOT_JOBS_ENABLE_ASSERTS != 0 && AWS_IOT_JOBS_ENABLE_ASSERTS != 1
\r
48 #error "AWS_IOT_JOBS_ENABLE_ASSERTS must be 0 or 1."
\r
50 #if AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS <= 0
\r
51 #error "AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS cannot be 0 or negative."
\r
53 #if AWS_IOT_JOBS_NOTIFY_CALLBACKS <= 0
\r
54 #error "AWS_IOT_JOBS_NOTIFY_CALLBACKS cannot be 0 or negative."
\r
58 * @brief Returned by @ref _getCallbackIndex when there's no space in the callback array.
\r
60 #define NO_SPACE_FOR_CALLBACK ( -1 )
\r
63 * @brief Returned by @ref _getCallbackIndex when a searching for an oldCallback that
\r
66 #define OLD_CALLBACK_NOT_FOUND ( -2 )
\r
68 /*-----------------------------------------------------------*/
\r
71 * @brief Check if the library is initialized.
\r
73 * @return `true` if AwsIotJobs_Init was called; `false` otherwise.
\r
75 static bool _checkInit( void );
\r
78 * @brief Validate the #AwsIotJobsRequestInfo_t passed to a Jobs API function.
\r
80 * @param[in] type The Jobs API function type.
\r
81 * @param[in] pRequestInfo The request info passed to a Jobs API function.
\r
82 * @param[in] flags Flags used by the Jobs API function.
\r
83 * @param[in] pCallbackInfo The callback info passed with the request info.
\r
84 * @param[in] pOperation Operation reference pointer passed to a Jobs API function.
\r
86 * @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_BAD_PARAMETER.
\r
88 static AwsIotJobsError_t _validateRequestInfo( _jobsOperationType_t type,
\r
89 const AwsIotJobsRequestInfo_t * pRequestInfo,
\r
91 const AwsIotJobsCallbackInfo_t * pCallbackInfo,
\r
92 const AwsIotJobsOperation_t * pOperation );
\r
95 * @brief Validate the #AwsIotJobsUpdateInfo_t passed to a Jobs API function.
\r
97 * @param[in] type The Jobs API function type.
\r
98 * @param[in] pUpdateInfo The update info passed to a Jobs API function.
\r
100 * @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_BAD_PARAMETER.
\r
102 static AwsIotJobsError_t _validateUpdateInfo( _jobsOperationType_t type,
\r
103 const AwsIotJobsUpdateInfo_t * pUpdateInfo );
\r
106 * @brief Gets an element of the callback array to modify.
\r
108 * @param[in] type The type of callback to modify.
\r
109 * @param[in] pSubscription Subscription object that holds the callback array.
\r
110 * @param[in] pCallbackInfo User provided callback info.
\r
112 * @return The index of the callback array to modify; on error:
\r
113 * - #NO_SPACE_FOR_CALLBACK if no free spaces are available
\r
114 * - #OLD_CALLBACK_NOT_FOUND if an old callback to remove was specified, but that function does not exist.
\r
116 * @note This function should be called with the subscription list mutex locked.
\r
118 static int32_t _getCallbackIndex( _jobsCallbackType_t type,
\r
119 _jobsSubscription_t * pSubscription,
\r
120 const AwsIotJobsCallbackInfo_t * pCallbackInfo );
\r
123 * @brief Common function for setting Jobs callbacks.
\r
125 * @param[in] mqttConnection The MQTT connection to use.
\r
126 * @param[in] type Type of Jobs callback.
\r
127 * @param[in] pThingName Thing Name for Jobs callback.
\r
128 * @param[in] thingNameLength Length of `pThingName`.
\r
129 * @param[in] pCallbackInfo Callback information to set.
\r
131 * @return #AWS_IOT_JOBS_SUCCESS, #AWS_IOT_JOBS_BAD_PARAMETER,
\r
132 * #AWS_IOT_JOBS_NO_MEMORY, or #AWS_IOT_JOBS_MQTT_ERROR.
\r
134 static AwsIotJobsError_t _setCallbackCommon( IotMqttConnection_t mqttConnection,
\r
135 _jobsCallbackType_t type,
\r
136 const char * pThingName,
\r
137 size_t thingNameLength,
\r
138 const AwsIotJobsCallbackInfo_t * pCallbackInfo );
\r
141 * @brief Change the subscriptions for Jobs callbacks, either by subscribing
\r
142 * or unsubscribing.
\r
144 * @param[in] mqttConnection The MQTT connection to use.
\r
145 * @param[in] type Type of Jobs callback.
\r
146 * @param[in] pSubscription Jobs subscriptions object for callback.
\r
147 * @param[in] mqttOperation Either @ref mqtt_function_subscribesync or
\r
148 * @ref mqtt_function_unsubscribesync.
\r
150 * @return #AWS_IOT_JOBS_SUCCESS, #AWS_IOT_JOBS_NO_MEMORY, or
\r
151 * #AWS_IOT_JOBS_MQTT_ERROR.
\r
153 static AwsIotJobsError_t _modifyCallbackSubscriptions( IotMqttConnection_t mqttConnection,
\r
154 _jobsCallbackType_t type,
\r
155 _jobsSubscription_t * pSubscription,
\r
156 AwsIotMqttFunction_t mqttOperation );
\r
159 * @brief Invoked when a document is received on the Jobs NOTIFY PENDING callback.
\r
161 * @param[in] pArgument Ignored.
\r
162 * @param[in] pMessage The received Jobs document (as an MQTT PUBLISH message).
\r
164 static void _notifyPendingCallbackWrapper( void * pArgument,
\r
165 IotMqttCallbackParam_t * pMessage );
\r
168 * @brief Invoked when a document is received on the Jobs NOTIFY NEXT callback.
\r
170 * @param[in] pArgument Ignored.
\r
171 * @param[in] pMessage The received Jobs document (as an MQTT PUBLISH message).
\r
173 static void _notifyNextCallbackWrapper( void * pArgument,
\r
174 IotMqttCallbackParam_t * pMessage );
\r
177 * @brief Common function for incoming Jobs callbacks.
\r
179 * @param[in] type Jobs callback type.
\r
180 * @param[in] pMessage The received Jobs callback document (as an MQTT PUBLISH
\r
183 static void _callbackWrapperCommon( _jobsCallbackType_t type,
\r
184 IotMqttCallbackParam_t * pMessage );
\r
186 /*-----------------------------------------------------------*/
\r
189 * @brief Tracks whether @ref jobs_function_init has been called.
\r
191 * API functions will fail if @ref jobs_function_init was not called.
\r
193 static uint32_t _initCalled = 0;
\r
196 * @brief Timeout used for MQTT operations.
\r
198 uint32_t _AwsIotJobsMqttTimeoutMs = AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS;
\r
200 #if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
\r
203 * @brief Printable names for the Jobs callbacks.
\r
205 const char * const _pAwsIotJobsCallbackNames[] =
\r
212 /*-----------------------------------------------------------*/
\r
214 static bool _checkInit( void )
\r
216 bool status = true;
\r
218 if( _initCalled == 0 )
\r
220 IotLogError( "AwsIotJobs_Init was not called." );
\r
228 /*-----------------------------------------------------------*/
\r
230 static AwsIotJobsError_t _validateRequestInfo( _jobsOperationType_t type,
\r
231 const AwsIotJobsRequestInfo_t * pRequestInfo,
\r
233 const AwsIotJobsCallbackInfo_t * pCallbackInfo,
\r
234 const AwsIotJobsOperation_t * pOperation )
\r
236 IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );
\r
238 /* Check that the given MQTT connection is valid. */
\r
239 if( pRequestInfo->mqttConnection == IOT_MQTT_CONNECTION_INITIALIZER )
\r
241 IotLogError( "MQTT connection is not initialized for Jobs %s.",
\r
242 _pAwsIotJobsOperationNames[ type ] );
\r
244 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
247 /* Check Thing Name. */
\r
248 if( AwsIot_ValidateThingName( pRequestInfo->pThingName,
\r
249 pRequestInfo->thingNameLength ) == false )
\r
251 IotLogError( "Thing Name is not valid for Jobs %s.",
\r
252 _pAwsIotJobsOperationNames[ type ] );
\r
254 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
257 /* Checks for waitable operations. */
\r
258 if( ( flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )
\r
260 if( pOperation == NULL )
\r
262 IotLogError( "Reference must be provided for a waitable Jobs %s.",
\r
263 _pAwsIotJobsOperationNames[ type ] );
\r
265 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
268 if( pRequestInfo->mallocResponse == NULL )
\r
270 IotLogError( "Memory allocation function must be set for waitable Jobs %s.",
\r
271 _pAwsIotJobsOperationNames[ type ] );
\r
273 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
276 if( pCallbackInfo != NULL )
\r
278 IotLogError( "Callback should not be set for a waitable Jobs %s.",
\r
279 _pAwsIotJobsOperationNames[ type ] );
\r
281 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
285 /* Check that a callback function is set. */
\r
286 if( pCallbackInfo != NULL )
\r
288 if( pCallbackInfo->function == NULL )
\r
290 IotLogError( "Callback function must be set for Jobs %s callback.",
\r
291 _pAwsIotJobsOperationNames[ type ] );
\r
293 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
297 /* Check client token length. */
\r
298 if( pRequestInfo->pClientToken != AWS_IOT_JOBS_CLIENT_TOKEN_AUTOGENERATE )
\r
300 if( pRequestInfo->clientTokenLength == 0 )
\r
302 IotLogError( "Client token length must be set for Jobs %s.",
\r
303 _pAwsIotJobsOperationNames[ type ] );
\r
305 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
308 if( pRequestInfo->clientTokenLength > AWS_IOT_CLIENT_TOKEN_MAX_LENGTH )
\r
310 IotLogError( "Client token for Jobs %s cannot be longer than %d.",
\r
311 _pAwsIotJobsOperationNames[ type ],
\r
312 AWS_IOT_CLIENT_TOKEN_MAX_LENGTH );
\r
314 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
318 /* Check Job ID for DESCRIBE and UPDATE. */
\r
319 if( ( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) )
\r
321 if( ( pRequestInfo->pJobId == NULL ) || ( pRequestInfo->jobIdLength == 0 ) )
\r
323 IotLogError( "Job ID must be set for Jobs %s.",
\r
324 _pAwsIotJobsOperationNames[ type ] );
\r
326 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
329 if( pRequestInfo->jobIdLength > JOBS_MAX_ID_LENGTH )
\r
331 IotLogError( "Job ID for Jobs %s cannot be longer than %d.",
\r
332 _pAwsIotJobsOperationNames[ type ],
\r
333 JOBS_MAX_ID_LENGTH );
\r
335 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
339 /* A Job ID (not $next job) must be specified for UPDATE. */
\r
340 if( type == JOBS_UPDATE )
\r
342 if( ( pRequestInfo->jobIdLength == AWS_IOT_JOBS_NEXT_JOB_LENGTH ) &&
\r
343 ( strncmp( AWS_IOT_JOBS_NEXT_JOB,
\r
344 pRequestInfo->pJobId,
\r
345 AWS_IOT_JOBS_NEXT_JOB_LENGTH ) == 0 ) )
\r
347 IotLogError( "Job ID $next is not valid for Jobs UPDATE." );
\r
349 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
353 IOT_FUNCTION_EXIT_NO_CLEANUP();
\r
356 /*-----------------------------------------------------------*/
\r
358 static AwsIotJobsError_t _validateUpdateInfo( _jobsOperationType_t type,
\r
359 const AwsIotJobsUpdateInfo_t * pUpdateInfo )
\r
361 IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );
\r
363 /* Only START NEXT and UPDATE operations need an update info. */
\r
364 AwsIotJobs_Assert( ( type == JOBS_START_NEXT ) || ( type == JOBS_UPDATE ) );
\r
366 /* Check that Job status to report is valid for Jobs UPDATE. */
\r
367 if( type == JOBS_UPDATE )
\r
369 switch( pUpdateInfo->newStatus )
\r
371 case AWS_IOT_JOB_STATE_IN_PROGRESS:
\r
372 case AWS_IOT_JOB_STATE_FAILED:
\r
373 case AWS_IOT_JOB_STATE_SUCCEEDED:
\r
374 case AWS_IOT_JOB_STATE_REJECTED:
\r
378 IotLogError( "Job state %s is not valid for Jobs UPDATE.",
\r
379 AwsIotJobs_StateName( pUpdateInfo->newStatus ) );
\r
381 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
385 /* Check that step timeout is valid. */
\r
386 if( ( pUpdateInfo->stepTimeoutInMinutes != AWS_IOT_JOBS_NO_TIMEOUT ) &&
\r
387 ( pUpdateInfo->stepTimeoutInMinutes != AWS_IOT_JOBS_CANCEL_TIMEOUT ) )
\r
389 if( pUpdateInfo->stepTimeoutInMinutes < 1 )
\r
391 IotLogError( "Step timeout for Jobs %s must be at least 1.",
\r
392 _pAwsIotJobsOperationNames[ type ] );
\r
394 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
396 else if( pUpdateInfo->stepTimeoutInMinutes > JOBS_MAX_TIMEOUT )
\r
398 IotLogError( "Step timeout for Jobs %s cannot exceed %d.",
\r
399 _pAwsIotJobsOperationNames[ type ],
\r
400 JOBS_MAX_TIMEOUT );
\r
402 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
406 /* Check status details. */
\r
407 if( pUpdateInfo->pStatusDetails != AWS_IOT_JOBS_NO_STATUS_DETAILS )
\r
409 if( pUpdateInfo->statusDetailsLength == 0 )
\r
411 IotLogError( "Status details length not set for Jobs %s.",
\r
412 _pAwsIotJobsOperationNames[ type ] );
\r
414 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
417 if( pUpdateInfo->statusDetailsLength > JOBS_MAX_STATUS_DETAILS_LENGTH )
\r
419 IotLogError( "Status details length for Jobs %s cannot exceed %d.",
\r
420 _pAwsIotJobsOperationNames[ type ],
\r
421 JOBS_MAX_STATUS_DETAILS_LENGTH );
\r
423 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
427 IOT_FUNCTION_EXIT_NO_CLEANUP();
\r
430 /*-----------------------------------------------------------*/
\r
432 static int32_t _getCallbackIndex( _jobsCallbackType_t type,
\r
433 _jobsSubscription_t * pSubscription,
\r
434 const AwsIotJobsCallbackInfo_t * pCallbackInfo )
\r
436 int32_t callbackIndex = 0;
\r
438 /* Find the matching oldFunction. */
\r
439 if( pCallbackInfo->oldFunction != NULL )
\r
441 for( callbackIndex = 0; callbackIndex < AWS_IOT_JOBS_NOTIFY_CALLBACKS; callbackIndex++ )
\r
443 if( pSubscription->callbacks[ type ][ callbackIndex ].function == pCallbackInfo->oldFunction )
\r
445 /* oldFunction found. */
\r
450 if( callbackIndex == AWS_IOT_JOBS_NOTIFY_CALLBACKS )
\r
452 /* oldFunction not found. */
\r
453 callbackIndex = OLD_CALLBACK_NOT_FOUND;
\r
456 /* Find space for a new callback. */
\r
459 for( callbackIndex = 0; callbackIndex < AWS_IOT_JOBS_NOTIFY_CALLBACKS; callbackIndex++ )
\r
461 if( pSubscription->callbacks[ type ][ callbackIndex ].function == NULL )
\r
467 if( callbackIndex == AWS_IOT_JOBS_NOTIFY_CALLBACKS )
\r
469 /* No memory for new callback. */
\r
470 callbackIndex = NO_SPACE_FOR_CALLBACK;
\r
474 return callbackIndex;
\r
477 /*-----------------------------------------------------------*/
\r
479 static AwsIotJobsError_t _setCallbackCommon( IotMqttConnection_t mqttConnection,
\r
480 _jobsCallbackType_t type,
\r
481 const char * pThingName,
\r
482 size_t thingNameLength,
\r
483 const AwsIotJobsCallbackInfo_t * pCallbackInfo )
\r
485 IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );
\r
486 bool subscriptionMutexLocked = false;
\r
487 _jobsSubscription_t * pSubscription = NULL;
\r
488 int32_t callbackIndex = 0;
\r
490 /* Check that AwsIotJobs_Init was called. */
\r
491 if( _checkInit() == false )
\r
493 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NOT_INITIALIZED );
\r
496 /* Validate Thing Name. */
\r
497 if( AwsIot_ValidateThingName( pThingName, thingNameLength ) == false )
\r
499 IotLogError( "Thing Name for Jobs %s callback is not valid.",
\r
500 _pAwsIotJobsCallbackNames[ type ] );
\r
502 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
505 /* Check that a callback parameter is provided. */
\r
506 if( pCallbackInfo == NULL )
\r
508 IotLogError( "Callback parameter must be provided for Jobs %s callback.",
\r
509 _pAwsIotJobsCallbackNames[ type ] );
\r
511 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
514 /* The oldFunction member must be set when removing or replacing a callback. */
\r
515 if( pCallbackInfo->function == NULL )
\r
517 if( pCallbackInfo->oldFunction == NULL )
\r
519 IotLogError( "Both oldFunction and function pointers cannot be NULL for Jobs %s callback.",
\r
520 _pAwsIotJobsCallbackNames[ type ] );
\r
522 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
526 IotLogInfo( "(%.*s) Modifying Jobs %s callback.",
\r
529 _pAwsIotJobsCallbackNames[ type ] );
\r
531 /* Lock the subscription list mutex to check for an existing subscription
\r
533 IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
\r
534 subscriptionMutexLocked = true;
\r
536 /* Check for an existing subscription. This function will attempt to allocate
\r
537 * a new subscription if not found. */
\r
538 pSubscription = _AwsIotJobs_FindSubscription( pThingName, thingNameLength, true );
\r
540 if( pSubscription == NULL )
\r
542 /* No existing subscription was found, and no new subscription could be
\r
544 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );
\r
547 /* Get the index of the callback element to modify. */
\r
548 callbackIndex = _getCallbackIndex( type, pSubscription, pCallbackInfo );
\r
550 switch( callbackIndex )
\r
552 case NO_SPACE_FOR_CALLBACK:
\r
553 IotLogError( "No memory for a new Jobs %s callback.",
\r
554 _pAwsIotJobsCallbackNames[ type ] );
\r
556 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );
\r
558 case OLD_CALLBACK_NOT_FOUND:
\r
559 IotLogWarn( "Requested replacement function for Jobs %s callback not found.",
\r
560 _pAwsIotJobsCallbackNames[ type ] );
\r
562 /* A subscription may have been allocated, but the callback operation can't
\r
563 * proceed. Check if the subscription should be removed. */
\r
564 _AwsIotJobs_RemoveSubscription( pSubscription, NULL );
\r
566 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
572 /* Check for an existing callback. */
\r
573 if( pSubscription->callbacks[ type ][ callbackIndex ].function != NULL )
\r
575 /* Replace existing callback. */
\r
576 if( pCallbackInfo->function != NULL )
\r
578 IotLogInfo( "(%.*s) Found existing %s callback. Replacing callback.",
\r
581 _pAwsIotJobsCallbackNames[ type ] );
\r
583 pSubscription->callbacks[ type ][ callbackIndex ] = *pCallbackInfo;
\r
585 /* Remove existing callback. */
\r
588 IotLogInfo( "(%.*s) Removing existing %s callback.",
\r
591 _pAwsIotJobsCallbackNames[ type ] );
\r
593 /* Clear the callback information and unsubscribe. */
\r
594 ( void ) memset( &( pSubscription->callbacks[ type ][ callbackIndex ] ),
\r
596 sizeof( AwsIotJobsCallbackInfo_t ) );
\r
597 ( void ) _modifyCallbackSubscriptions( mqttConnection,
\r
600 IotMqtt_UnsubscribeSync );
\r
602 /* Check if this subscription object can be removed. */
\r
603 _AwsIotJobs_RemoveSubscription( pSubscription, NULL );
\r
606 /* No existing callback. */
\r
609 /* Add new callback. */
\r
610 IotLogInfo( "(%.*s) Adding new %s callback.",
\r
613 _pAwsIotJobsCallbackNames[ type ] );
\r
615 status = _modifyCallbackSubscriptions( mqttConnection,
\r
618 IotMqtt_SubscribeSync );
\r
620 if( status == AWS_IOT_JOBS_SUCCESS )
\r
622 pSubscription->callbacks[ type ][ callbackIndex ] = *pCallbackInfo;
\r
626 /* On failure, check if this subscription can be removed. */
\r
627 _AwsIotJobs_RemoveSubscription( pSubscription, NULL );
\r
631 IOT_FUNCTION_CLEANUP_BEGIN();
\r
633 if( subscriptionMutexLocked == true )
\r
635 IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
\r
638 IotLogInfo( "(%.*s) Jobs %s callback operation complete with result %s.",
\r
641 _pAwsIotJobsCallbackNames[ type ],
\r
642 AwsIotJobs_strerror( status ) );
\r
644 IOT_FUNCTION_CLEANUP_END();
\r
647 /*-----------------------------------------------------------*/
\r
649 static AwsIotJobsError_t _modifyCallbackSubscriptions( IotMqttConnection_t mqttConnection,
\r
650 _jobsCallbackType_t type,
\r
651 _jobsSubscription_t * pSubscription,
\r
652 AwsIotMqttFunction_t mqttOperation )
\r
654 IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );
\r
656 IotMqttError_t mqttStatus = IOT_MQTT_STATUS_PENDING;
\r
657 IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
\r
658 AwsIotTopicInfo_t topicInfo = { 0 };
\r
659 char * pTopicFilter = NULL;
\r
660 uint16_t topicFilterLength = 0;
\r
662 /* Lookup table for Jobs callback suffixes. */
\r
663 const char * const pCallbackSuffix[ JOBS_CALLBACK_COUNT ] =
\r
665 JOBS_NOTIFY_PENDING_CALLBACK_STRING, /* Notify pending callback. */
\r
666 JOBS_NOTIFY_NEXT_CALLBACK_STRING /* Notify next callback. */
\r
669 /* Lookup table for Jobs callback suffix lengths. */
\r
670 const uint16_t pCallbackSuffixLength[ JOBS_CALLBACK_COUNT ] =
\r
672 JOBS_NOTIFY_PENDING_CALLBACK_STRING_LENGTH, /* Notify pending callback. */
\r
673 JOBS_NOTIFY_NEXT_CALLBACK_STRING_LENGTH /* Notify next callback. */
\r
676 /* Lookup table for Jobs callback function wrappers. */
\r
677 const AwsIotMqttCallbackFunction_t pCallbackWrapper[ JOBS_CALLBACK_COUNT ] =
\r
679 _notifyPendingCallbackWrapper, /* Notify pending callback. */
\r
680 _notifyNextCallbackWrapper, /* Notify next callback. */
\r
683 /* MQTT operation may only be subscribe or unsubscribe. */
\r
684 AwsIotJobs_Assert( ( mqttOperation == IotMqtt_SubscribeSync ) ||
\r
685 ( mqttOperation == IotMqtt_UnsubscribeSync ) );
\r
687 /* Check if any subscriptions are currently registered for this type. */
\r
688 for( i = 0; i < AWS_IOT_JOBS_NOTIFY_CALLBACKS; i++ )
\r
690 if( pSubscription->callbacks[ type ][ i ].function != NULL )
\r
692 /* No action is needed when another callback exists. */
\r
693 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_SUCCESS );
\r
697 /* Use the subscription's topic buffer if available. */
\r
698 if( pSubscription->pTopicBuffer != NULL )
\r
700 pTopicFilter = pSubscription->pTopicBuffer;
\r
703 /* Generate the topic for the MQTT subscription. */
\r
704 topicInfo.pThingName = pSubscription->pThingName;
\r
705 topicInfo.thingNameLength = pSubscription->thingNameLength;
\r
706 topicInfo.longestSuffixLength = JOBS_LONGEST_SUFFIX_LENGTH;
\r
707 topicInfo.mallocString = AwsIotJobs_MallocString;
\r
708 topicInfo.pOperationName = pCallbackSuffix[ type ];
\r
709 topicInfo.operationNameLength = pCallbackSuffixLength[ type ];
\r
711 if( AwsIot_GenerateOperationTopic( &topicInfo,
\r
713 &topicFilterLength ) == false )
\r
715 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );
\r
718 IotLogDebug( "%s subscription for %.*s",
\r
719 mqttOperation == IotMqtt_SubscribeSync ? "Adding" : "Removing",
\r
723 /* Set the members of the MQTT subscription. */
\r
724 subscription.qos = IOT_MQTT_QOS_1;
\r
725 subscription.pTopicFilter = pTopicFilter;
\r
726 subscription.topicFilterLength = topicFilterLength;
\r
727 subscription.callback.pCallbackContext = NULL;
\r
728 subscription.callback.function = pCallbackWrapper[ type ];
\r
730 /* Call the MQTT operation function. */
\r
731 mqttStatus = mqttOperation( mqttConnection,
\r
735 _AwsIotJobsMqttTimeoutMs );
\r
737 /* Check the result of the MQTT operation. */
\r
738 if( mqttStatus != IOT_MQTT_SUCCESS )
\r
740 IotLogError( "Failed to %s callback for %.*s %s callback, error %s.",
\r
741 mqttOperation == IotMqtt_SubscribeSync ? "subscribe to" : "unsubscribe from",
\r
742 pSubscription->thingNameLength,
\r
743 pSubscription->pThingName,
\r
744 _pAwsIotJobsCallbackNames[ type ],
\r
745 IotMqtt_strerror( mqttStatus ) );
\r
747 /* Convert the MQTT "NO MEMORY" error to a Jobs "NO MEMORY" error. */
\r
748 if( mqttStatus == IOT_MQTT_NO_MEMORY )
\r
750 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );
\r
754 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_MQTT_ERROR );
\r
758 IotLogDebug( "Successfully %s %.*s Jobs %s callback.",
\r
759 mqttOperation == IotMqtt_SubscribeSync ? "subscribed to" : "unsubscribed from",
\r
760 pSubscription->thingNameLength,
\r
761 pSubscription->pThingName,
\r
762 _pAwsIotJobsCallbackNames[ type ] );
\r
764 IOT_FUNCTION_CLEANUP_BEGIN();
\r
766 /* MQTT subscribe should check the subscription topic buffer. */
\r
767 if( mqttOperation == IotMqtt_SubscribeSync )
\r
769 /* If the current subscription has no topic buffer, assign it the current
\r
771 if( pSubscription->pTopicBuffer == NULL )
\r
773 pSubscription->pTopicBuffer = pTopicFilter;
\r
777 IOT_FUNCTION_CLEANUP_END();
\r
780 /*-----------------------------------------------------------*/
\r
782 static void _notifyPendingCallbackWrapper( void * pArgument,
\r
783 IotMqttCallbackParam_t * pMessage )
\r
785 /* Silence warnings about unused parameters. */
\r
786 ( void ) pArgument;
\r
788 _callbackWrapperCommon( NOTIFY_PENDING_CALLBACK,
\r
792 /*-----------------------------------------------------------*/
\r
794 static void _notifyNextCallbackWrapper( void * pArgument,
\r
795 IotMqttCallbackParam_t * pMessage )
\r
797 /* Silence warnings about unused parameters. */
\r
798 ( void ) pArgument;
\r
800 _callbackWrapperCommon( NOTIFY_NEXT_CALLBACK,
\r
804 /*-----------------------------------------------------------*/
\r
806 static void _callbackWrapperCommon( _jobsCallbackType_t type,
\r
807 IotMqttCallbackParam_t * pMessage )
\r
809 int32_t callbackIndex = 0;
\r
810 AwsIotJobsCallbackInfo_t callbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
\r
811 AwsIotJobsCallbackParam_t callbackParam = { .callbackType = ( AwsIotJobsCallbackType_t ) 0 };
\r
812 _jobsSubscription_t * pSubscription = NULL;
\r
813 const char * pThingName = NULL;
\r
814 size_t thingNameLength = 0;
\r
816 /* Parse the Thing Name from the topic. */
\r
817 if( AwsIot_ParseThingName( pMessage->u.message.info.pTopicName,
\r
818 pMessage->u.message.info.topicNameLength,
\r
820 &thingNameLength ) == false )
\r
822 IOT_GOTO_CLEANUP();
\r
825 /* Search for a matching subscription. */
\r
826 IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
\r
828 pSubscription = _AwsIotJobs_FindSubscription( pThingName,
\r
832 if( pSubscription != NULL )
\r
834 /* Increment callback reference count to prevent the subscription object from being
\r
835 * freed while callbacks are running. */
\r
836 pSubscription->callbackReferences++;
\r
839 IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
\r
841 if( pSubscription != NULL )
\r
843 /* Invoke all callbacks for this Thing. */
\r
844 for( callbackIndex = 0; callbackIndex < AWS_IOT_JOBS_NOTIFY_CALLBACKS; callbackIndex++ )
\r
846 /* Copy the callback function and parameter, as it may be modified at any time. */
\r
847 IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
\r
848 callbackInfo = pSubscription->callbacks[ type ][ callbackIndex ];
\r
849 IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
\r
851 if( callbackInfo.function != NULL )
\r
853 /* Set the callback type. Jobs callbacks are enumerated after the operations. */
\r
854 callbackParam.callbackType = ( AwsIotJobsCallbackType_t ) ( type + JOBS_OPERATION_COUNT );
\r
856 /* Set the remaining members of the callback param. */
\r
857 callbackParam.mqttConnection = pMessage->mqttConnection;
\r
858 callbackParam.pThingName = pThingName;
\r
859 callbackParam.thingNameLength = thingNameLength;
\r
860 callbackParam.u.callback.pDocument = pMessage->u.message.info.pPayload;
\r
861 callbackParam.u.callback.documentLength = pMessage->u.message.info.payloadLength;
\r
863 /* Invoke the callback function. */
\r
864 callbackInfo.function( callbackInfo.pCallbackContext,
\r
869 /* Callbacks are finished. Decrement reference count and check if subscription
\r
870 * object should be destroyed. */
\r
871 IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
\r
873 pSubscription->callbackReferences--;
\r
874 AwsIotJobs_Assert( pSubscription->callbackReferences >= 0 );
\r
876 _AwsIotJobs_RemoveSubscription( pSubscription, NULL );
\r
878 IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
\r
881 /* This function uses cleanup sections to exit on error. */
\r
882 IOT_FUNCTION_CLEANUP_BEGIN();
\r
885 /*-----------------------------------------------------------*/
\r
887 AwsIotJobsError_t AwsIotJobs_Init( uint32_t mqttTimeoutMs )
\r
889 AwsIotJobsError_t status = AWS_IOT_JOBS_SUCCESS;
\r
890 bool listInitStatus = false;
\r
892 if( _initCalled == 0 )
\r
894 listInitStatus = AwsIot_InitLists( &_AwsIotJobsPendingOperations,
\r
895 &_AwsIotJobsSubscriptions,
\r
896 &_AwsIotJobsPendingOperationsMutex,
\r
897 &_AwsIotJobsSubscriptionsMutex );
\r
899 if( listInitStatus == false )
\r
901 IotLogInfo( "Failed to create Jobs lists." );
\r
903 status = AWS_IOT_JOBS_INIT_FAILED;
\r
907 /* Save the MQTT timeout option. */
\r
908 if( mqttTimeoutMs != 0 )
\r
910 _AwsIotJobsMqttTimeoutMs = mqttTimeoutMs;
\r
913 /* Set the flag that specifies initialization is complete. */
\r
916 IotLogInfo( "Jobs library successfully initialized." );
\r
921 IotLogWarn( "AwsIotJobs_Init called with library already initialized." );
\r
927 /*-----------------------------------------------------------*/
\r
929 void AwsIotJobs_Cleanup( void )
\r
931 if( _initCalled == 1 )
\r
935 /* Remove and free all items in the Jobs pending operation list. */
\r
936 IotMutex_Lock( &_AwsIotJobsPendingOperationsMutex );
\r
937 IotListDouble_RemoveAll( &_AwsIotJobsPendingOperations,
\r
938 _AwsIotJobs_DestroyOperation,
\r
939 offsetof( _jobsOperation_t, link ) );
\r
940 IotMutex_Unlock( &_AwsIotJobsPendingOperationsMutex );
\r
942 /* Remove and free all items in the Jobs subscription list. */
\r
943 IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
\r
944 IotListDouble_RemoveAll( &_AwsIotJobsSubscriptions,
\r
945 _AwsIotJobs_DestroySubscription,
\r
946 offsetof( _jobsSubscription_t, link ) );
\r
947 IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
\r
949 /* Restore the default MQTT timeout. */
\r
950 _AwsIotJobsMqttTimeoutMs = AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS;
\r
952 /* Destroy Jobs library mutexes. */
\r
953 IotMutex_Destroy( &_AwsIotJobsPendingOperationsMutex );
\r
954 IotMutex_Destroy( &_AwsIotJobsSubscriptionsMutex );
\r
956 IotLogInfo( "Jobs library cleanup done." );
\r
960 IotLogWarn( "AwsIotJobs_Init was not called before AwsIotShadow_Cleanup." );
\r
964 /*-----------------------------------------------------------*/
\r
966 AwsIotJobsError_t AwsIotJobs_GetPendingAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
\r
968 const AwsIotJobsCallbackInfo_t * pCallbackInfo,
\r
969 AwsIotJobsOperation_t * const pGetPendingOperation )
\r
971 IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_STATUS_PENDING );
\r
972 _jobsOperation_t * pOperation = NULL;
\r
974 /* Check that AwsIotJobs_Init was called. */
\r
975 if( _checkInit() == false )
\r
977 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NOT_INITIALIZED );
\r
980 /* Check request info. */
\r
981 status = _validateRequestInfo( JOBS_GET_PENDING,
\r
985 pGetPendingOperation );
\r
987 if( status != AWS_IOT_JOBS_SUCCESS )
\r
989 IOT_GOTO_CLEANUP();
\r
992 /* Allocate a new Jobs operation. */
\r
993 status = _AwsIotJobs_CreateOperation( JOBS_GET_PENDING,
\r
1000 if( status != AWS_IOT_JOBS_SUCCESS )
\r
1002 /* No memory for Jobs operation. */
\r
1003 IOT_GOTO_CLEANUP();
\r
1006 /* Set the reference if provided. This must be done before the Jobs operation
\r
1007 * is processed. */
\r
1008 if( pGetPendingOperation != NULL )
\r
1010 *pGetPendingOperation = pOperation;
\r
1013 /* Process the Jobs operation. This subscribes to any required topics and
\r
1014 * sends the MQTT message for the Jobs operation. */
\r
1015 status = _AwsIotJobs_ProcessOperation( pRequestInfo, pOperation );
\r
1017 /* If the Jobs operation failed, clear the now invalid reference. */
\r
1018 if( ( status != AWS_IOT_JOBS_STATUS_PENDING ) && ( pGetPendingOperation != NULL ) )
\r
1020 *pGetPendingOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
\r
1023 IOT_FUNCTION_EXIT_NO_CLEANUP();
\r
1026 /*-----------------------------------------------------------*/
\r
1028 AwsIotJobsError_t AwsIotJobs_GetPendingSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
\r
1030 uint32_t timeoutMs,
\r
1031 AwsIotJobsResponse_t * const pJobsResponse )
\r
1033 AwsIotJobsError_t status = AWS_IOT_JOBS_STATUS_PENDING;
\r
1034 AwsIotJobsOperation_t getPendingOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
\r
1036 /* Set the waitable flag. */
\r
1037 flags |= AWS_IOT_JOBS_FLAG_WAITABLE;
\r
1039 /* Call the asynchronous Jobs Get Pending function. */
\r
1040 status = AwsIotJobs_GetPendingAsync( pRequestInfo,
\r
1043 &getPendingOperation );
\r
1045 /* Wait for the Jobs Get Pending operation to complete. */
\r
1046 if( status == AWS_IOT_JOBS_STATUS_PENDING )
\r
1048 status = AwsIotJobs_Wait( getPendingOperation,
\r
1056 /*-----------------------------------------------------------*/
\r
1058 AwsIotJobsError_t AwsIotJobs_StartNextAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
\r
1059 const AwsIotJobsUpdateInfo_t * pUpdateInfo,
\r
1061 const AwsIotJobsCallbackInfo_t * pCallbackInfo,
\r
1062 AwsIotJobsOperation_t * const pStartNextOperation )
\r
1064 IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_STATUS_PENDING );
\r
1065 _jobsOperation_t * pOperation = NULL;
\r
1066 _jsonRequestContents_t requestContents = { 0 };
\r
1068 /* Check that AwsIotJobs_Init was called. */
\r
1069 if( _checkInit() == false )
\r
1071 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NOT_INITIALIZED );
\r
1074 /* Check request info. */
\r
1075 status = _validateRequestInfo( JOBS_START_NEXT,
\r
1079 pStartNextOperation );
\r
1081 if( status != AWS_IOT_JOBS_SUCCESS )
\r
1083 IOT_GOTO_CLEANUP();
\r
1086 /* Check update info. */
\r
1087 status = _validateUpdateInfo( JOBS_START_NEXT,
\r
1090 if( status != AWS_IOT_JOBS_SUCCESS )
\r
1092 IOT_GOTO_CLEANUP();
\r
1095 /* Allocate a new Jobs operation. */
\r
1096 requestContents.pUpdateInfo = pUpdateInfo;
\r
1098 status = _AwsIotJobs_CreateOperation( JOBS_START_NEXT,
\r
1105 if( status != AWS_IOT_JOBS_SUCCESS )
\r
1107 /* No memory for Jobs operation. */
\r
1108 IOT_GOTO_CLEANUP();
\r
1111 /* Set the reference if provided. This must be done before the Jobs operation
\r
1112 * is processed. */
\r
1113 if( pStartNextOperation != NULL )
\r
1115 *pStartNextOperation = pOperation;
\r
1118 /* Process the Jobs operation. This subscribes to any required topics and
\r
1119 * sends the MQTT message for the Jobs operation. */
\r
1120 status = _AwsIotJobs_ProcessOperation( pRequestInfo, pOperation );
\r
1122 /* If the Jobs operation failed, clear the now invalid reference. */
\r
1123 if( ( status != AWS_IOT_JOBS_STATUS_PENDING ) && ( pStartNextOperation != NULL ) )
\r
1125 *pStartNextOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
\r
1128 IOT_FUNCTION_EXIT_NO_CLEANUP();
\r
1131 /*-----------------------------------------------------------*/
\r
1133 AwsIotJobsError_t AwsIotJobs_StartNextSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
\r
1134 const AwsIotJobsUpdateInfo_t * pUpdateInfo,
\r
1136 uint32_t timeoutMs,
\r
1137 AwsIotJobsResponse_t * const pJobsResponse )
\r
1139 AwsIotJobsError_t status = AWS_IOT_JOBS_STATUS_PENDING;
\r
1140 AwsIotJobsOperation_t startNextOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
\r
1142 /* Set the waitable flag. */
\r
1143 flags |= AWS_IOT_JOBS_FLAG_WAITABLE;
\r
1145 /* Call the asynchronous Jobs Start Next function. */
\r
1146 status = AwsIotJobs_StartNextAsync( pRequestInfo,
\r
1150 &startNextOperation );
\r
1152 /* Wait for the Jobs Start Next operation to complete. */
\r
1153 if( status == AWS_IOT_JOBS_STATUS_PENDING )
\r
1155 status = AwsIotJobs_Wait( startNextOperation,
\r
1163 /*-----------------------------------------------------------*/
\r
1165 AwsIotJobsError_t AwsIotJobs_DescribeAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
\r
1166 int32_t executionNumber,
\r
1167 bool includeJobDocument,
\r
1169 const AwsIotJobsCallbackInfo_t * pCallbackInfo,
\r
1170 AwsIotJobsOperation_t * const pDescribeOperation )
\r
1172 IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_STATUS_PENDING );
\r
1173 _jobsOperation_t * pOperation = NULL;
\r
1174 _jsonRequestContents_t requestContents = { 0 };
\r
1176 /* Check that AwsIotJobs_Init was called. */
\r
1177 if( _checkInit() == false )
\r
1179 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NOT_INITIALIZED );
\r
1182 /* Check request info. */
\r
1183 status = _validateRequestInfo( JOBS_DESCRIBE,
\r
1187 pDescribeOperation );
\r
1189 if( status != AWS_IOT_JOBS_SUCCESS )
\r
1191 IOT_GOTO_CLEANUP();
\r
1194 /* Allocate a new Jobs operation. */
\r
1195 requestContents.describe.executionNumber = executionNumber;
\r
1196 requestContents.describe.includeJobDocument = includeJobDocument;
\r
1198 status = _AwsIotJobs_CreateOperation( JOBS_DESCRIBE,
\r
1205 if( status != AWS_IOT_JOBS_SUCCESS )
\r
1207 /* No memory for Jobs operation. */
\r
1208 IOT_GOTO_CLEANUP();
\r
1211 /* Set the reference if provided. This must be done before the Jobs operation
\r
1212 * is processed. */
\r
1213 if( pDescribeOperation != NULL )
\r
1215 *pDescribeOperation = pOperation;
\r
1218 /* Process the Jobs operation. This subscribes to any required topics and
\r
1219 * sends the MQTT message for the Jobs operation. */
\r
1220 status = _AwsIotJobs_ProcessOperation( pRequestInfo, pOperation );
\r
1222 /* If the Jobs operation failed, clear the now invalid reference. */
\r
1223 if( ( status != AWS_IOT_JOBS_STATUS_PENDING ) && ( pDescribeOperation != NULL ) )
\r
1225 *pDescribeOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
\r
1228 IOT_FUNCTION_EXIT_NO_CLEANUP();
\r
1231 /*-----------------------------------------------------------*/
\r
1233 AwsIotJobsError_t AwsIotJobs_DescribeSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
\r
1234 int32_t executionNumber,
\r
1235 bool includeJobDocument,
\r
1237 uint32_t timeoutMs,
\r
1238 AwsIotJobsResponse_t * const pJobsResponse )
\r
1240 AwsIotJobsError_t status = AWS_IOT_JOBS_STATUS_PENDING;
\r
1241 AwsIotJobsOperation_t describeOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
\r
1243 /* Set the waitable flag. */
\r
1244 flags |= AWS_IOT_JOBS_FLAG_WAITABLE;
\r
1246 /* Call the asynchronous Jobs Describe function. */
\r
1247 status = AwsIotJobs_DescribeAsync( pRequestInfo,
\r
1249 includeJobDocument,
\r
1252 &describeOperation );
\r
1254 /* Wait for the Jobs Describe operation to complete. */
\r
1255 if( status == AWS_IOT_JOBS_STATUS_PENDING )
\r
1257 status = AwsIotJobs_Wait( describeOperation,
\r
1265 /*-----------------------------------------------------------*/
\r
1267 AwsIotJobsError_t AwsIotJobs_UpdateAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
\r
1268 const AwsIotJobsUpdateInfo_t * pUpdateInfo,
\r
1270 const AwsIotJobsCallbackInfo_t * pCallbackInfo,
\r
1271 AwsIotJobsOperation_t * const pUpdateOperation )
\r
1273 IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_STATUS_PENDING );
\r
1274 _jobsOperation_t * pOperation = NULL;
\r
1275 _jsonRequestContents_t requestContents = { 0 };
\r
1277 /* Check that AwsIotJobs_Init was called. */
\r
1278 if( _checkInit() == false )
\r
1280 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NOT_INITIALIZED );
\r
1283 /* Check request info. */
\r
1284 status = _validateRequestInfo( JOBS_UPDATE,
\r
1288 pUpdateOperation );
\r
1290 if( status != AWS_IOT_JOBS_SUCCESS )
\r
1292 IOT_GOTO_CLEANUP();
\r
1295 /* Check update info. */
\r
1296 status = _validateUpdateInfo( JOBS_UPDATE,
\r
1299 if( status != AWS_IOT_JOBS_SUCCESS )
\r
1301 IOT_GOTO_CLEANUP();
\r
1304 /* Allocate a new Jobs operation. */
\r
1305 requestContents.pUpdateInfo = pUpdateInfo;
\r
1307 status = _AwsIotJobs_CreateOperation( JOBS_UPDATE,
\r
1314 if( status != AWS_IOT_JOBS_SUCCESS )
\r
1316 /* No memory for Jobs operation. */
\r
1317 IOT_GOTO_CLEANUP();
\r
1320 /* Set the reference if provided. This must be done before the Jobs operation
\r
1321 * is processed. */
\r
1322 if( pUpdateOperation != NULL )
\r
1324 *pUpdateOperation = pOperation;
\r
1327 /* Process the Jobs operation. This subscribes to any required topics and
\r
1328 * sends the MQTT message for the Jobs operation. */
\r
1329 status = _AwsIotJobs_ProcessOperation( pRequestInfo, pOperation );
\r
1331 /* If the Jobs operation failed, clear the now invalid reference. */
\r
1332 if( ( status != AWS_IOT_JOBS_STATUS_PENDING ) && ( pUpdateOperation != NULL ) )
\r
1334 *pUpdateOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
\r
1337 IOT_FUNCTION_EXIT_NO_CLEANUP();
\r
1340 /*-----------------------------------------------------------*/
\r
1342 AwsIotJobsError_t AwsIotJobs_UpdateSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
\r
1343 const AwsIotJobsUpdateInfo_t * pUpdateInfo,
\r
1345 uint32_t timeoutMs,
\r
1346 AwsIotJobsResponse_t * const pJobsResponse )
\r
1348 AwsIotJobsError_t status = AWS_IOT_JOBS_STATUS_PENDING;
\r
1349 AwsIotJobsOperation_t updateOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
\r
1351 /* Set the waitable flag. */
\r
1352 flags |= AWS_IOT_JOBS_FLAG_WAITABLE;
\r
1354 /* Call the asynchronous Jobs Update function. */
\r
1355 status = AwsIotJobs_UpdateAsync( pRequestInfo,
\r
1359 &updateOperation );
\r
1361 /* Wait for the Jobs Update operation to complete. */
\r
1362 if( status == AWS_IOT_JOBS_STATUS_PENDING )
\r
1364 status = AwsIotJobs_Wait( updateOperation,
\r
1372 /*-----------------------------------------------------------*/
\r
1374 AwsIotJobsError_t AwsIotJobs_Wait( AwsIotJobsOperation_t operation,
\r
1375 uint32_t timeoutMs,
\r
1376 AwsIotJobsResponse_t * const pJobsResponse )
\r
1378 IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_STATUS_PENDING );
\r
1380 /* Check that AwsIotJobs_Init was called. */
\r
1381 if( _checkInit() == false )
\r
1383 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NOT_INITIALIZED );
\r
1386 /* Check that reference is set. */
\r
1387 if( operation == NULL )
\r
1389 IotLogError( "Operation reference cannot be NULL." );
\r
1391 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
1394 /* Check that reference is waitable. */
\r
1395 if( ( operation->flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == 0 )
\r
1397 IotLogError( "Operation is not waitable." );
\r
1399 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
1402 /* Check that output parameter is set. */
\r
1403 if( pJobsResponse == NULL )
\r
1405 IotLogError( "Output Jobs response parameter must be set for Jobs %s.",
\r
1406 _pAwsIotJobsOperationNames[ operation->type ] );
\r
1408 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
\r
1411 /* Wait for a response to the Jobs operation. */
\r
1412 if( IotSemaphore_TimedWait( &( operation->notify.waitSemaphore ),
\r
1413 timeoutMs ) == true )
\r
1415 status = operation->status;
\r
1419 status = AWS_IOT_JOBS_TIMEOUT;
\r
1422 /* Remove the completed operation from the pending operation list. */
\r
1423 IotMutex_Lock( &( _AwsIotJobsPendingOperationsMutex ) );
\r
1424 IotListDouble_Remove( &( operation->link ) );
\r
1425 IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
\r
1427 /* Decrement the reference count. This also removes subscriptions if the
\r
1428 * count reaches 0. */
\r
1429 IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
\r
1430 _AwsIotJobs_DecrementReferences( operation,
\r
1431 operation->pSubscription->pTopicBuffer,
\r
1433 IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
\r
1435 /* Set the output parameters. Jobs responses are available on success or
\r
1436 * when the Jobs service returns an error document. */
\r
1437 if( ( status == AWS_IOT_JOBS_SUCCESS ) || ( status > AWS_IOT_JOBS_INVALID_TOPIC ) )
\r
1439 AwsIotJobs_Assert( operation->pJobsResponse != NULL );
\r
1440 AwsIotJobs_Assert( operation->jobsResponseLength > 0 );
\r
1442 pJobsResponse->pJobsResponse = operation->pJobsResponse;
\r
1443 pJobsResponse->jobsResponseLength = operation->jobsResponseLength;
\r
1446 /* Destroy the Jobs operation. */
\r
1447 _AwsIotJobs_DestroyOperation( operation );
\r
1449 IOT_FUNCTION_EXIT_NO_CLEANUP();
\r
1452 /*-----------------------------------------------------------*/
\r
1454 AwsIotJobsError_t AwsIotJobs_SetNotifyPendingCallback( IotMqttConnection_t mqttConnection,
\r
1455 const char * pThingName,
\r
1456 size_t thingNameLength,
\r
1458 const AwsIotJobsCallbackInfo_t * pNotifyPendingCallback )
\r
1460 /* Flags are not currently used by this function. */
\r
1463 return _setCallbackCommon( mqttConnection,
\r
1464 NOTIFY_PENDING_CALLBACK,
\r
1467 pNotifyPendingCallback );
\r
1470 /*-----------------------------------------------------------*/
\r
1472 AwsIotJobsError_t AwsIotJobs_SetNotifyNextCallback( IotMqttConnection_t mqttConnection,
\r
1473 const char * pThingName,
\r
1474 size_t thingNameLength,
\r
1476 const AwsIotJobsCallbackInfo_t * pNotifyNextCallback )
\r
1478 /* Flags are not currently used by this function. */
\r
1481 return _setCallbackCommon( mqttConnection,
\r
1482 NOTIFY_NEXT_CALLBACK,
\r
1485 pNotifyNextCallback );
\r
1488 /*-----------------------------------------------------------*/
\r
1490 const char * AwsIotJobs_strerror( AwsIotJobsError_t status )
\r
1492 const char * pMessage = NULL;
\r
1496 case AWS_IOT_JOBS_SUCCESS:
\r
1497 pMessage = "SUCCESS";
\r
1500 case AWS_IOT_JOBS_STATUS_PENDING:
\r
1501 pMessage = "STATUS PENDING";
\r
1504 case AWS_IOT_JOBS_INIT_FAILED:
\r
1505 pMessage = "INIT FAILED";
\r
1508 case AWS_IOT_JOBS_BAD_PARAMETER:
\r
1509 pMessage = "BAD PARAMETER";
\r
1512 case AWS_IOT_JOBS_NO_MEMORY:
\r
1513 pMessage = "NO MEMORY";
\r
1516 case AWS_IOT_JOBS_MQTT_ERROR:
\r
1517 pMessage = "MQTT ERROR";
\r
1520 case AWS_IOT_JOBS_BAD_RESPONSE:
\r
1521 pMessage = "BAD RESPONSE";
\r
1524 case AWS_IOT_JOBS_TIMEOUT:
\r
1525 pMessage = "TIMEOUT";
\r
1528 case AWS_IOT_JOBS_NOT_INITIALIZED:
\r
1529 pMessage = "NOT INITIALIZED";
\r
1532 case AWS_IOT_JOBS_INVALID_TOPIC:
\r
1533 pMessage = "FAILED: INVALID TOPIC";
\r
1536 case AWS_IOT_JOBS_INVALID_JSON:
\r
1537 pMessage = "FAILED: INVALID JSON";
\r
1540 case AWS_IOT_JOBS_INVALID_REQUEST:
\r
1541 pMessage = "FAILED: INVALID REQUEST";
\r
1544 case AWS_IOT_JOBS_INVALID_STATE:
\r
1545 pMessage = "FAILED: INVALID STATE TRANSITION";
\r
1548 case AWS_IOT_JOBS_NOT_FOUND:
\r
1549 pMessage = "FAILED: NOT FOUND";
\r
1552 case AWS_IOT_JOBS_VERSION_MISMATCH:
\r
1553 pMessage = "FAILED: VERSION MISMATCH";
\r
1556 case AWS_IOT_JOBS_INTERNAL_ERROR:
\r
1557 pMessage = "FAILED: INTERNAL ERROR";
\r
1560 case AWS_IOT_JOBS_THROTTLED:
\r
1561 pMessage = "FAILED: THROTTLED";
\r
1564 case AWS_IOT_JOBS_TERMINAL_STATE:
\r
1565 pMessage = "FAILED: TERMINAL STATE";
\r
1569 pMessage = "INVALID STATUS";
\r
1576 /*-----------------------------------------------------------*/
\r
1578 const char * AwsIotJobs_StateName( AwsIotJobState_t state )
\r
1580 const char * pMessage = NULL;
\r
1584 case AWS_IOT_JOB_STATE_QUEUED:
\r
1585 pMessage = "QUEUED";
\r
1588 case AWS_IOT_JOB_STATE_IN_PROGRESS:
\r
1589 pMessage = "IN PROGRESS";
\r
1592 case AWS_IOT_JOB_STATE_FAILED:
\r
1593 pMessage = "FAILED";
\r
1596 case AWS_IOT_JOB_STATE_SUCCEEDED:
\r
1597 pMessage = "SUCCEEDED";
\r
1600 case AWS_IOT_JOB_STATE_CANCELED:
\r
1601 pMessage = "CANCELED";
\r
1604 case AWS_IOT_JOB_STATE_TIMED_OUT:
\r
1605 pMessage = "TIMED OUT";
\r
1608 case AWS_IOT_JOB_STATE_REJECTED:
\r
1609 pMessage = "REJECTED";
\r
1612 case AWS_IOT_JOB_STATE_REMOVED:
\r
1613 pMessage = "REMOVED";
\r
1617 pMessage = "INVALID STATE";
\r
1624 /*-----------------------------------------------------------*/
\r