]> git.sur5r.net Git - freertos/blob - FreeRTOS-Labs/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/jobs/src/aws_iot_jobs_api.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Source / FreeRTOS-IoT-Libraries / c_sdk / aws / jobs / src / aws_iot_jobs_api.c
1 /*\r
2  * AWS IoT Jobs V1.0.0\r
3  * Copyright (C) 2019 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\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
11  *\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
14  *\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
21  */\r
22 \r
23 /**\r
24  * @file aws_iot_jobs_api.c\r
25  * @brief Implements the user-facing functions of the Jobs library.\r
26  */\r
27 \r
28 /* The config header is always included first. */\r
29 #include "iot_config.h"\r
30 \r
31 /* Standard includes. */\r
32 #include <string.h>\r
33 \r
34 /* Platform threads include. */\r
35 #include "platform/iot_threads.h"\r
36 \r
37 /* Jobs internal include. */\r
38 #include "private/aws_iot_jobs_internal.h"\r
39 \r
40 /* Error handling include. */\r
41 #include "iot_error.h"\r
42 \r
43 /* MQTT include. */\r
44 #include "iot_mqtt.h"\r
45 \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
49 #endif\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
52 #endif\r
53 #if AWS_IOT_JOBS_NOTIFY_CALLBACKS <= 0\r
54     #error "AWS_IOT_JOBS_NOTIFY_CALLBACKS cannot be 0 or negative."\r
55 #endif\r
56 \r
57 /**\r
58  * @brief Returned by @ref _getCallbackIndex when there's no space in the callback array.\r
59  */\r
60 #define NO_SPACE_FOR_CALLBACK     ( -1 )\r
61 \r
62 /**\r
63  * @brief Returned by @ref _getCallbackIndex when a searching for an oldCallback that\r
64  * does not exist.\r
65  */\r
66 #define OLD_CALLBACK_NOT_FOUND    ( -2 )\r
67 \r
68 /*-----------------------------------------------------------*/\r
69 \r
70 /**\r
71  * @brief Check if the library is initialized.\r
72  *\r
73  * @return `true` if AwsIotJobs_Init was called; `false` otherwise.\r
74  */\r
75 static bool _checkInit( void );\r
76 \r
77 /**\r
78  * @brief Validate the #AwsIotJobsRequestInfo_t passed to a Jobs API function.\r
79  *\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
85  *\r
86  * @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_BAD_PARAMETER.\r
87  */\r
88 static AwsIotJobsError_t _validateRequestInfo( _jobsOperationType_t type,\r
89                                                const AwsIotJobsRequestInfo_t * pRequestInfo,\r
90                                                uint32_t flags,\r
91                                                const AwsIotJobsCallbackInfo_t * pCallbackInfo,\r
92                                                const AwsIotJobsOperation_t * pOperation );\r
93 \r
94 /**\r
95  * @brief Validate the #AwsIotJobsUpdateInfo_t passed to a Jobs API function.\r
96  *\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
99  *\r
100  * @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_BAD_PARAMETER.\r
101  */\r
102 static AwsIotJobsError_t _validateUpdateInfo( _jobsOperationType_t type,\r
103                                               const AwsIotJobsUpdateInfo_t * pUpdateInfo );\r
104 \r
105 /**\r
106  * @brief Gets an element of the callback array to modify.\r
107  *\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
111  *\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
115  *\r
116  * @note This function should be called with the subscription list mutex locked.\r
117  */\r
118 static int32_t _getCallbackIndex( _jobsCallbackType_t type,\r
119                                   _jobsSubscription_t * pSubscription,\r
120                                   const AwsIotJobsCallbackInfo_t * pCallbackInfo );\r
121 \r
122 /**\r
123  * @brief Common function for setting Jobs callbacks.\r
124  *\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
130  *\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
133  */\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
139 \r
140 /**\r
141  * @brief Change the subscriptions for Jobs callbacks, either by subscribing\r
142  * or unsubscribing.\r
143  *\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
149  *\r
150  * @return #AWS_IOT_JOBS_SUCCESS, #AWS_IOT_JOBS_NO_MEMORY, or\r
151  * #AWS_IOT_JOBS_MQTT_ERROR.\r
152  */\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
157 \r
158 /**\r
159  * @brief Invoked when a document is received on the Jobs NOTIFY PENDING callback.\r
160  *\r
161  * @param[in] pArgument Ignored.\r
162  * @param[in] pMessage The received Jobs document (as an MQTT PUBLISH message).\r
163  */\r
164 static void _notifyPendingCallbackWrapper( void * pArgument,\r
165                                            IotMqttCallbackParam_t * pMessage );\r
166 \r
167 /**\r
168  * @brief Invoked when a document is received on the Jobs NOTIFY NEXT callback.\r
169  *\r
170  * @param[in] pArgument Ignored.\r
171  * @param[in] pMessage The received Jobs document (as an MQTT PUBLISH message).\r
172  */\r
173 static void _notifyNextCallbackWrapper( void * pArgument,\r
174                                         IotMqttCallbackParam_t * pMessage );\r
175 \r
176 /**\r
177  * @brief Common function for incoming Jobs callbacks.\r
178  *\r
179  * @param[in] type Jobs callback type.\r
180  * @param[in] pMessage The received Jobs callback document (as an MQTT PUBLISH\r
181  * message).\r
182  */\r
183 static void _callbackWrapperCommon( _jobsCallbackType_t type,\r
184                                     IotMqttCallbackParam_t * pMessage );\r
185 \r
186 /*-----------------------------------------------------------*/\r
187 \r
188 /**\r
189  * @brief Tracks whether @ref jobs_function_init has been called.\r
190  *\r
191  * API functions will fail if @ref jobs_function_init was not called.\r
192  */\r
193 static uint32_t _initCalled = 0;\r
194 \r
195 /**\r
196  * @brief Timeout used for MQTT operations.\r
197  */\r
198 uint32_t _AwsIotJobsMqttTimeoutMs = AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS;\r
199 \r
200 #if LIBRARY_LOG_LEVEL > IOT_LOG_NONE\r
201 \r
202 /**\r
203  * @brief Printable names for the Jobs callbacks.\r
204  */\r
205     const char * const _pAwsIotJobsCallbackNames[] =\r
206     {\r
207         "NOTIFY PENDING",\r
208         "NOTIFY NEXT"\r
209     };\r
210 #endif\r
211 \r
212 /*-----------------------------------------------------------*/\r
213 \r
214 static bool _checkInit( void )\r
215 {\r
216     bool status = true;\r
217 \r
218     if( _initCalled == 0 )\r
219     {\r
220         IotLogError( "AwsIotJobs_Init was not called." );\r
221 \r
222         status = false;\r
223     }\r
224 \r
225     return status;\r
226 }\r
227 \r
228 /*-----------------------------------------------------------*/\r
229 \r
230 static AwsIotJobsError_t _validateRequestInfo( _jobsOperationType_t type,\r
231                                                const AwsIotJobsRequestInfo_t * pRequestInfo,\r
232                                                uint32_t flags,\r
233                                                const AwsIotJobsCallbackInfo_t * pCallbackInfo,\r
234                                                const AwsIotJobsOperation_t * pOperation )\r
235 {\r
236     IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );\r
237 \r
238     /* Check that the given MQTT connection is valid. */\r
239     if( pRequestInfo->mqttConnection == IOT_MQTT_CONNECTION_INITIALIZER )\r
240     {\r
241         IotLogError( "MQTT connection is not initialized for Jobs %s.",\r
242                      _pAwsIotJobsOperationNames[ type ] );\r
243 \r
244         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
245     }\r
246 \r
247     /* Check Thing Name. */\r
248     if( AwsIot_ValidateThingName( pRequestInfo->pThingName,\r
249                                   pRequestInfo->thingNameLength ) == false )\r
250     {\r
251         IotLogError( "Thing Name is not valid for Jobs %s.",\r
252                      _pAwsIotJobsOperationNames[ type ] );\r
253 \r
254         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
255     }\r
256 \r
257     /* Checks for waitable operations. */\r
258     if( ( flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )\r
259     {\r
260         if( pOperation == NULL )\r
261         {\r
262             IotLogError( "Reference must be provided for a waitable Jobs %s.",\r
263                          _pAwsIotJobsOperationNames[ type ] );\r
264 \r
265             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
266         }\r
267 \r
268         if( pRequestInfo->mallocResponse == NULL )\r
269         {\r
270             IotLogError( "Memory allocation function must be set for waitable Jobs %s.",\r
271                          _pAwsIotJobsOperationNames[ type ] );\r
272 \r
273             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
274         }\r
275 \r
276         if( pCallbackInfo != NULL )\r
277         {\r
278             IotLogError( "Callback should not be set for a waitable Jobs %s.",\r
279                          _pAwsIotJobsOperationNames[ type ] );\r
280 \r
281             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
282         }\r
283     }\r
284 \r
285     /* Check that a callback function is set. */\r
286     if( pCallbackInfo != NULL )\r
287     {\r
288         if( pCallbackInfo->function == NULL )\r
289         {\r
290             IotLogError( "Callback function must be set for Jobs %s callback.",\r
291                          _pAwsIotJobsOperationNames[ type ] );\r
292 \r
293             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
294         }\r
295     }\r
296 \r
297     /* Check client token length. */\r
298     if( pRequestInfo->pClientToken != AWS_IOT_JOBS_CLIENT_TOKEN_AUTOGENERATE )\r
299     {\r
300         if( pRequestInfo->clientTokenLength == 0 )\r
301         {\r
302             IotLogError( "Client token length must be set for Jobs %s.",\r
303                          _pAwsIotJobsOperationNames[ type ] );\r
304 \r
305             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
306         }\r
307 \r
308         if( pRequestInfo->clientTokenLength > AWS_IOT_CLIENT_TOKEN_MAX_LENGTH )\r
309         {\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
313 \r
314             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
315         }\r
316     }\r
317 \r
318     /* Check Job ID for DESCRIBE and UPDATE. */\r
319     if( ( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) )\r
320     {\r
321         if( ( pRequestInfo->pJobId == NULL ) || ( pRequestInfo->jobIdLength == 0 ) )\r
322         {\r
323             IotLogError( "Job ID must be set for Jobs %s.",\r
324                          _pAwsIotJobsOperationNames[ type ] );\r
325 \r
326             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
327         }\r
328 \r
329         if( pRequestInfo->jobIdLength > JOBS_MAX_ID_LENGTH )\r
330         {\r
331             IotLogError( "Job ID for Jobs %s cannot be longer than %d.",\r
332                          _pAwsIotJobsOperationNames[ type ],\r
333                          JOBS_MAX_ID_LENGTH );\r
334 \r
335             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
336         }\r
337     }\r
338 \r
339     /* A Job ID (not $next job) must be specified for UPDATE. */\r
340     if( type == JOBS_UPDATE )\r
341     {\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
346         {\r
347             IotLogError( "Job ID $next is not valid for Jobs UPDATE." );\r
348 \r
349             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
350         }\r
351     }\r
352 \r
353     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
354 }\r
355 \r
356 /*-----------------------------------------------------------*/\r
357 \r
358 static AwsIotJobsError_t _validateUpdateInfo( _jobsOperationType_t type,\r
359                                               const AwsIotJobsUpdateInfo_t * pUpdateInfo )\r
360 {\r
361     IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );\r
362 \r
363     /* Only START NEXT and UPDATE operations need an update info. */\r
364     AwsIotJobs_Assert( ( type == JOBS_START_NEXT ) || ( type == JOBS_UPDATE ) );\r
365 \r
366     /* Check that Job status to report is valid for Jobs UPDATE. */\r
367     if( type == JOBS_UPDATE )\r
368     {\r
369         switch( pUpdateInfo->newStatus )\r
370         {\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
375                 break;\r
376 \r
377             default:\r
378                 IotLogError( "Job state %s is not valid for Jobs UPDATE.",\r
379                              AwsIotJobs_StateName( pUpdateInfo->newStatus ) );\r
380 \r
381                 IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
382         }\r
383     }\r
384 \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
388     {\r
389         if( pUpdateInfo->stepTimeoutInMinutes < 1 )\r
390         {\r
391             IotLogError( "Step timeout for Jobs %s must be at least 1.",\r
392                          _pAwsIotJobsOperationNames[ type ] );\r
393 \r
394             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
395         }\r
396         else if( pUpdateInfo->stepTimeoutInMinutes > JOBS_MAX_TIMEOUT )\r
397         {\r
398             IotLogError( "Step timeout for Jobs %s cannot exceed %d.",\r
399                          _pAwsIotJobsOperationNames[ type ],\r
400                          JOBS_MAX_TIMEOUT );\r
401 \r
402             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
403         }\r
404     }\r
405 \r
406     /* Check status details. */\r
407     if( pUpdateInfo->pStatusDetails != AWS_IOT_JOBS_NO_STATUS_DETAILS )\r
408     {\r
409         if( pUpdateInfo->statusDetailsLength == 0 )\r
410         {\r
411             IotLogError( "Status details length not set for Jobs %s.",\r
412                          _pAwsIotJobsOperationNames[ type ] );\r
413 \r
414             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
415         }\r
416 \r
417         if( pUpdateInfo->statusDetailsLength > JOBS_MAX_STATUS_DETAILS_LENGTH )\r
418         {\r
419             IotLogError( "Status details length for Jobs %s cannot exceed %d.",\r
420                          _pAwsIotJobsOperationNames[ type ],\r
421                          JOBS_MAX_STATUS_DETAILS_LENGTH );\r
422 \r
423             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
424         }\r
425     }\r
426 \r
427     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
428 }\r
429 \r
430 /*-----------------------------------------------------------*/\r
431 \r
432 static int32_t _getCallbackIndex( _jobsCallbackType_t type,\r
433                                   _jobsSubscription_t * pSubscription,\r
434                                   const AwsIotJobsCallbackInfo_t * pCallbackInfo )\r
435 {\r
436     int32_t callbackIndex = 0;\r
437 \r
438     /* Find the matching oldFunction. */\r
439     if( pCallbackInfo->oldFunction != NULL )\r
440     {\r
441         for( callbackIndex = 0; callbackIndex < AWS_IOT_JOBS_NOTIFY_CALLBACKS; callbackIndex++ )\r
442         {\r
443             if( pSubscription->callbacks[ type ][ callbackIndex ].function == pCallbackInfo->oldFunction )\r
444             {\r
445                 /* oldFunction found. */\r
446                 break;\r
447             }\r
448         }\r
449 \r
450         if( callbackIndex == AWS_IOT_JOBS_NOTIFY_CALLBACKS )\r
451         {\r
452             /* oldFunction not found. */\r
453             callbackIndex = OLD_CALLBACK_NOT_FOUND;\r
454         }\r
455     }\r
456     /* Find space for a new callback. */\r
457     else\r
458     {\r
459         for( callbackIndex = 0; callbackIndex < AWS_IOT_JOBS_NOTIFY_CALLBACKS; callbackIndex++ )\r
460         {\r
461             if( pSubscription->callbacks[ type ][ callbackIndex ].function == NULL )\r
462             {\r
463                 break;\r
464             }\r
465         }\r
466 \r
467         if( callbackIndex == AWS_IOT_JOBS_NOTIFY_CALLBACKS )\r
468         {\r
469             /* No memory for new callback. */\r
470             callbackIndex = NO_SPACE_FOR_CALLBACK;\r
471         }\r
472     }\r
473 \r
474     return callbackIndex;\r
475 }\r
476 \r
477 /*-----------------------------------------------------------*/\r
478 \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
484 {\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
489 \r
490     /* Check that AwsIotJobs_Init was called. */\r
491     if( _checkInit() == false )\r
492     {\r
493         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NOT_INITIALIZED );\r
494     }\r
495 \r
496     /* Validate Thing Name. */\r
497     if( AwsIot_ValidateThingName( pThingName, thingNameLength ) == false )\r
498     {\r
499         IotLogError( "Thing Name for Jobs %s callback is not valid.",\r
500                      _pAwsIotJobsCallbackNames[ type ] );\r
501 \r
502         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
503     }\r
504 \r
505     /* Check that a callback parameter is provided. */\r
506     if( pCallbackInfo == NULL )\r
507     {\r
508         IotLogError( "Callback parameter must be provided for Jobs %s callback.",\r
509                      _pAwsIotJobsCallbackNames[ type ] );\r
510 \r
511         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
512     }\r
513 \r
514     /* The oldFunction member must be set when removing or replacing a callback. */\r
515     if( pCallbackInfo->function == NULL )\r
516     {\r
517         if( pCallbackInfo->oldFunction == NULL )\r
518         {\r
519             IotLogError( "Both oldFunction and function pointers cannot be NULL for Jobs %s callback.",\r
520                          _pAwsIotJobsCallbackNames[ type ] );\r
521 \r
522             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
523         }\r
524     }\r
525 \r
526     IotLogInfo( "(%.*s) Modifying Jobs %s callback.",\r
527                 thingNameLength,\r
528                 pThingName,\r
529                 _pAwsIotJobsCallbackNames[ type ] );\r
530 \r
531     /* Lock the subscription list mutex to check for an existing subscription\r
532      * object. */\r
533     IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );\r
534     subscriptionMutexLocked = true;\r
535 \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
539 \r
540     if( pSubscription == NULL )\r
541     {\r
542         /* No existing subscription was found, and no new subscription could be\r
543          * allocated. */\r
544         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );\r
545     }\r
546 \r
547     /* Get the index of the callback element to modify. */\r
548     callbackIndex = _getCallbackIndex( type, pSubscription, pCallbackInfo );\r
549 \r
550     switch( callbackIndex )\r
551     {\r
552         case NO_SPACE_FOR_CALLBACK:\r
553             IotLogError( "No memory for a new Jobs %s callback.",\r
554                          _pAwsIotJobsCallbackNames[ type ] );\r
555 \r
556             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );\r
557 \r
558         case OLD_CALLBACK_NOT_FOUND:\r
559             IotLogWarn( "Requested replacement function for Jobs %s callback not found.",\r
560                         _pAwsIotJobsCallbackNames[ type ] );\r
561 \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
565 \r
566             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
567 \r
568         default:\r
569             break;\r
570     }\r
571 \r
572     /* Check for an existing callback. */\r
573     if( pSubscription->callbacks[ type ][ callbackIndex ].function != NULL )\r
574     {\r
575         /* Replace existing callback. */\r
576         if( pCallbackInfo->function != NULL )\r
577         {\r
578             IotLogInfo( "(%.*s) Found existing %s callback. Replacing callback.",\r
579                         thingNameLength,\r
580                         pThingName,\r
581                         _pAwsIotJobsCallbackNames[ type ] );\r
582 \r
583             pSubscription->callbacks[ type ][ callbackIndex ] = *pCallbackInfo;\r
584         }\r
585         /* Remove existing callback. */\r
586         else\r
587         {\r
588             IotLogInfo( "(%.*s) Removing existing %s callback.",\r
589                         thingNameLength,\r
590                         pThingName,\r
591                         _pAwsIotJobsCallbackNames[ type ] );\r
592 \r
593             /* Clear the callback information and unsubscribe. */\r
594             ( void ) memset( &( pSubscription->callbacks[ type ][ callbackIndex ] ),\r
595                              0x00,\r
596                              sizeof( AwsIotJobsCallbackInfo_t ) );\r
597             ( void ) _modifyCallbackSubscriptions( mqttConnection,\r
598                                                    type,\r
599                                                    pSubscription,\r
600                                                    IotMqtt_UnsubscribeSync );\r
601 \r
602             /* Check if this subscription object can be removed. */\r
603             _AwsIotJobs_RemoveSubscription( pSubscription, NULL );\r
604         }\r
605     }\r
606     /* No existing callback. */\r
607     else\r
608     {\r
609         /* Add new callback. */\r
610         IotLogInfo( "(%.*s) Adding new %s callback.",\r
611                     thingNameLength,\r
612                     pThingName,\r
613                     _pAwsIotJobsCallbackNames[ type ] );\r
614 \r
615         status = _modifyCallbackSubscriptions( mqttConnection,\r
616                                                type,\r
617                                                pSubscription,\r
618                                                IotMqtt_SubscribeSync );\r
619 \r
620         if( status == AWS_IOT_JOBS_SUCCESS )\r
621         {\r
622             pSubscription->callbacks[ type ][ callbackIndex ] = *pCallbackInfo;\r
623         }\r
624         else\r
625         {\r
626             /* On failure, check if this subscription can be removed. */\r
627             _AwsIotJobs_RemoveSubscription( pSubscription, NULL );\r
628         }\r
629     }\r
630 \r
631     IOT_FUNCTION_CLEANUP_BEGIN();\r
632 \r
633     if( subscriptionMutexLocked == true )\r
634     {\r
635         IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );\r
636     }\r
637 \r
638     IotLogInfo( "(%.*s) Jobs %s callback operation complete with result %s.",\r
639                 thingNameLength,\r
640                 pThingName,\r
641                 _pAwsIotJobsCallbackNames[ type ],\r
642                 AwsIotJobs_strerror( status ) );\r
643 \r
644     IOT_FUNCTION_CLEANUP_END();\r
645 }\r
646 \r
647 /*-----------------------------------------------------------*/\r
648 \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
653 {\r
654     IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );\r
655     int32_t i = 0;\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
661 \r
662     /* Lookup table for Jobs callback suffixes. */\r
663     const char * const pCallbackSuffix[ JOBS_CALLBACK_COUNT ] =\r
664     {\r
665         JOBS_NOTIFY_PENDING_CALLBACK_STRING, /* Notify pending callback. */\r
666         JOBS_NOTIFY_NEXT_CALLBACK_STRING     /* Notify next callback. */\r
667     };\r
668 \r
669     /* Lookup table for Jobs callback suffix lengths. */\r
670     const uint16_t pCallbackSuffixLength[ JOBS_CALLBACK_COUNT ] =\r
671     {\r
672         JOBS_NOTIFY_PENDING_CALLBACK_STRING_LENGTH, /* Notify pending callback. */\r
673         JOBS_NOTIFY_NEXT_CALLBACK_STRING_LENGTH     /* Notify next callback. */\r
674     };\r
675 \r
676     /* Lookup table for Jobs callback function wrappers. */\r
677     const AwsIotMqttCallbackFunction_t pCallbackWrapper[ JOBS_CALLBACK_COUNT ] =\r
678     {\r
679         _notifyPendingCallbackWrapper, /* Notify pending callback. */\r
680         _notifyNextCallbackWrapper,    /* Notify next callback. */\r
681     };\r
682 \r
683     /* MQTT operation may only be subscribe or unsubscribe. */\r
684     AwsIotJobs_Assert( ( mqttOperation == IotMqtt_SubscribeSync ) ||\r
685                        ( mqttOperation == IotMqtt_UnsubscribeSync ) );\r
686 \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
689     {\r
690         if( pSubscription->callbacks[ type ][ i ].function != NULL )\r
691         {\r
692             /* No action is needed when another callback exists. */\r
693             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_SUCCESS );\r
694         }\r
695     }\r
696 \r
697     /* Use the subscription's topic buffer if available. */\r
698     if( pSubscription->pTopicBuffer != NULL )\r
699     {\r
700         pTopicFilter = pSubscription->pTopicBuffer;\r
701     }\r
702 \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
710 \r
711     if( AwsIot_GenerateOperationTopic( &topicInfo,\r
712                                        &pTopicFilter,\r
713                                        &topicFilterLength ) == false )\r
714     {\r
715         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );\r
716     }\r
717 \r
718     IotLogDebug( "%s subscription for %.*s",\r
719                  mqttOperation == IotMqtt_SubscribeSync ? "Adding" : "Removing",\r
720                  topicFilterLength,\r
721                  pTopicFilter );\r
722 \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
729 \r
730     /* Call the MQTT operation function. */\r
731     mqttStatus = mqttOperation( mqttConnection,\r
732                                 &subscription,\r
733                                 1,\r
734                                 0,\r
735                                 _AwsIotJobsMqttTimeoutMs );\r
736 \r
737     /* Check the result of the MQTT operation. */\r
738     if( mqttStatus != IOT_MQTT_SUCCESS )\r
739     {\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
746 \r
747         /* Convert the MQTT "NO MEMORY" error to a Jobs "NO MEMORY" error. */\r
748         if( mqttStatus == IOT_MQTT_NO_MEMORY )\r
749         {\r
750             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );\r
751         }\r
752         else\r
753         {\r
754             IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_MQTT_ERROR );\r
755         }\r
756     }\r
757 \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
763 \r
764     IOT_FUNCTION_CLEANUP_BEGIN();\r
765 \r
766     /* MQTT subscribe should check the subscription topic buffer. */\r
767     if( mqttOperation == IotMqtt_SubscribeSync )\r
768     {\r
769         /* If the current subscription has no topic buffer, assign it the current\r
770          * topic buffer. */\r
771         if( pSubscription->pTopicBuffer == NULL )\r
772         {\r
773             pSubscription->pTopicBuffer = pTopicFilter;\r
774         }\r
775     }\r
776 \r
777     IOT_FUNCTION_CLEANUP_END();\r
778 }\r
779 \r
780 /*-----------------------------------------------------------*/\r
781 \r
782 static void _notifyPendingCallbackWrapper( void * pArgument,\r
783                                            IotMqttCallbackParam_t * pMessage )\r
784 {\r
785     /* Silence warnings about unused parameters. */\r
786     ( void ) pArgument;\r
787 \r
788     _callbackWrapperCommon( NOTIFY_PENDING_CALLBACK,\r
789                             pMessage );\r
790 }\r
791 \r
792 /*-----------------------------------------------------------*/\r
793 \r
794 static void _notifyNextCallbackWrapper( void * pArgument,\r
795                                         IotMqttCallbackParam_t * pMessage )\r
796 {\r
797     /* Silence warnings about unused parameters. */\r
798     ( void ) pArgument;\r
799 \r
800     _callbackWrapperCommon( NOTIFY_NEXT_CALLBACK,\r
801                             pMessage );\r
802 }\r
803 \r
804 /*-----------------------------------------------------------*/\r
805 \r
806 static void _callbackWrapperCommon( _jobsCallbackType_t type,\r
807                                     IotMqttCallbackParam_t * pMessage )\r
808 {\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
815 \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
819                                &pThingName,\r
820                                &thingNameLength ) == false )\r
821     {\r
822         IOT_GOTO_CLEANUP();\r
823     }\r
824 \r
825     /* Search for a matching subscription. */\r
826     IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );\r
827 \r
828     pSubscription = _AwsIotJobs_FindSubscription( pThingName,\r
829                                                   thingNameLength,\r
830                                                   false );\r
831 \r
832     if( pSubscription != NULL )\r
833     {\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
837     }\r
838 \r
839     IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );\r
840 \r
841     if( pSubscription != NULL )\r
842     {\r
843         /* Invoke all callbacks for this Thing. */\r
844         for( callbackIndex = 0; callbackIndex < AWS_IOT_JOBS_NOTIFY_CALLBACKS; callbackIndex++ )\r
845         {\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
850 \r
851             if( callbackInfo.function != NULL )\r
852             {\r
853                 /* Set the callback type. Jobs callbacks are enumerated after the operations. */\r
854                 callbackParam.callbackType = ( AwsIotJobsCallbackType_t ) ( type + JOBS_OPERATION_COUNT );\r
855 \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
862 \r
863                 /* Invoke the callback function. */\r
864                 callbackInfo.function( callbackInfo.pCallbackContext,\r
865                                        &callbackParam );\r
866             }\r
867         }\r
868 \r
869         /* Callbacks are finished. Decrement reference count and check if subscription\r
870          * object should be destroyed. */\r
871         IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );\r
872 \r
873         pSubscription->callbackReferences--;\r
874         AwsIotJobs_Assert( pSubscription->callbackReferences >= 0 );\r
875 \r
876         _AwsIotJobs_RemoveSubscription( pSubscription, NULL );\r
877 \r
878         IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );\r
879     }\r
880 \r
881     /* This function uses cleanup sections to exit on error. */\r
882     IOT_FUNCTION_CLEANUP_BEGIN();\r
883 }\r
884 \r
885 /*-----------------------------------------------------------*/\r
886 \r
887 AwsIotJobsError_t AwsIotJobs_Init( uint32_t mqttTimeoutMs )\r
888 {\r
889     AwsIotJobsError_t status = AWS_IOT_JOBS_SUCCESS;\r
890     bool listInitStatus = false;\r
891 \r
892     if( _initCalled == 0 )\r
893     {\r
894         listInitStatus = AwsIot_InitLists( &_AwsIotJobsPendingOperations,\r
895                                            &_AwsIotJobsSubscriptions,\r
896                                            &_AwsIotJobsPendingOperationsMutex,\r
897                                            &_AwsIotJobsSubscriptionsMutex );\r
898 \r
899         if( listInitStatus == false )\r
900         {\r
901             IotLogInfo( "Failed to create Jobs lists." );\r
902 \r
903             status = AWS_IOT_JOBS_INIT_FAILED;\r
904         }\r
905         else\r
906         {\r
907             /* Save the MQTT timeout option. */\r
908             if( mqttTimeoutMs != 0 )\r
909             {\r
910                 _AwsIotJobsMqttTimeoutMs = mqttTimeoutMs;\r
911             }\r
912 \r
913             /* Set the flag that specifies initialization is complete. */\r
914             _initCalled = 1;\r
915 \r
916             IotLogInfo( "Jobs library successfully initialized." );\r
917         }\r
918     }\r
919     else\r
920     {\r
921         IotLogWarn( "AwsIotJobs_Init called with library already initialized." );\r
922     }\r
923 \r
924     return status;\r
925 }\r
926 \r
927 /*-----------------------------------------------------------*/\r
928 \r
929 void AwsIotJobs_Cleanup( void )\r
930 {\r
931     if( _initCalled == 1 )\r
932     {\r
933         _initCalled = 0;\r
934 \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
941 \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
948 \r
949         /* Restore the default MQTT timeout. */\r
950         _AwsIotJobsMqttTimeoutMs = AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS;\r
951 \r
952         /* Destroy Jobs library mutexes. */\r
953         IotMutex_Destroy( &_AwsIotJobsPendingOperationsMutex );\r
954         IotMutex_Destroy( &_AwsIotJobsSubscriptionsMutex );\r
955 \r
956         IotLogInfo( "Jobs library cleanup done." );\r
957     }\r
958     else\r
959     {\r
960         IotLogWarn( "AwsIotJobs_Init was not called before AwsIotShadow_Cleanup." );\r
961     }\r
962 }\r
963 \r
964 /*-----------------------------------------------------------*/\r
965 \r
966 AwsIotJobsError_t AwsIotJobs_GetPendingAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
967                                               uint32_t flags,\r
968                                               const AwsIotJobsCallbackInfo_t * pCallbackInfo,\r
969                                               AwsIotJobsOperation_t * const pGetPendingOperation )\r
970 {\r
971     IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_STATUS_PENDING );\r
972     _jobsOperation_t * pOperation = NULL;\r
973 \r
974     /* Check that AwsIotJobs_Init was called. */\r
975     if( _checkInit() == false )\r
976     {\r
977         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NOT_INITIALIZED );\r
978     }\r
979 \r
980     /* Check request info. */\r
981     status = _validateRequestInfo( JOBS_GET_PENDING,\r
982                                    pRequestInfo,\r
983                                    flags,\r
984                                    pCallbackInfo,\r
985                                    pGetPendingOperation );\r
986 \r
987     if( status != AWS_IOT_JOBS_SUCCESS )\r
988     {\r
989         IOT_GOTO_CLEANUP();\r
990     }\r
991 \r
992     /* Allocate a new Jobs operation. */\r
993     status = _AwsIotJobs_CreateOperation( JOBS_GET_PENDING,\r
994                                           pRequestInfo,\r
995                                           NULL,\r
996                                           flags,\r
997                                           pCallbackInfo,\r
998                                           &pOperation );\r
999 \r
1000     if( status != AWS_IOT_JOBS_SUCCESS )\r
1001     {\r
1002         /* No memory for Jobs operation. */\r
1003         IOT_GOTO_CLEANUP();\r
1004     }\r
1005 \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
1009     {\r
1010         *pGetPendingOperation = pOperation;\r
1011     }\r
1012 \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
1016 \r
1017     /* If the Jobs operation failed, clear the now invalid reference. */\r
1018     if( ( status != AWS_IOT_JOBS_STATUS_PENDING ) && ( pGetPendingOperation != NULL ) )\r
1019     {\r
1020         *pGetPendingOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;\r
1021     }\r
1022 \r
1023     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
1024 }\r
1025 \r
1026 /*-----------------------------------------------------------*/\r
1027 \r
1028 AwsIotJobsError_t AwsIotJobs_GetPendingSync( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
1029                                              uint32_t flags,\r
1030                                              uint32_t timeoutMs,\r
1031                                              AwsIotJobsResponse_t * const pJobsResponse )\r
1032 {\r
1033     AwsIotJobsError_t status = AWS_IOT_JOBS_STATUS_PENDING;\r
1034     AwsIotJobsOperation_t getPendingOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;\r
1035 \r
1036     /* Set the waitable flag. */\r
1037     flags |= AWS_IOT_JOBS_FLAG_WAITABLE;\r
1038 \r
1039     /* Call the asynchronous Jobs Get Pending function. */\r
1040     status = AwsIotJobs_GetPendingAsync( pRequestInfo,\r
1041                                          flags,\r
1042                                          NULL,\r
1043                                          &getPendingOperation );\r
1044 \r
1045     /* Wait for the Jobs Get Pending operation to complete. */\r
1046     if( status == AWS_IOT_JOBS_STATUS_PENDING )\r
1047     {\r
1048         status = AwsIotJobs_Wait( getPendingOperation,\r
1049                                   timeoutMs,\r
1050                                   pJobsResponse );\r
1051     }\r
1052 \r
1053     return status;\r
1054 }\r
1055 \r
1056 /*-----------------------------------------------------------*/\r
1057 \r
1058 AwsIotJobsError_t AwsIotJobs_StartNextAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
1059                                              const AwsIotJobsUpdateInfo_t * pUpdateInfo,\r
1060                                              uint32_t flags,\r
1061                                              const AwsIotJobsCallbackInfo_t * pCallbackInfo,\r
1062                                              AwsIotJobsOperation_t * const pStartNextOperation )\r
1063 {\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
1067 \r
1068     /* Check that AwsIotJobs_Init was called. */\r
1069     if( _checkInit() == false )\r
1070     {\r
1071         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NOT_INITIALIZED );\r
1072     }\r
1073 \r
1074     /* Check request info. */\r
1075     status = _validateRequestInfo( JOBS_START_NEXT,\r
1076                                    pRequestInfo,\r
1077                                    flags,\r
1078                                    pCallbackInfo,\r
1079                                    pStartNextOperation );\r
1080 \r
1081     if( status != AWS_IOT_JOBS_SUCCESS )\r
1082     {\r
1083         IOT_GOTO_CLEANUP();\r
1084     }\r
1085 \r
1086     /* Check update info. */\r
1087     status = _validateUpdateInfo( JOBS_START_NEXT,\r
1088                                   pUpdateInfo );\r
1089 \r
1090     if( status != AWS_IOT_JOBS_SUCCESS )\r
1091     {\r
1092         IOT_GOTO_CLEANUP();\r
1093     }\r
1094 \r
1095     /* Allocate a new Jobs operation. */\r
1096     requestContents.pUpdateInfo = pUpdateInfo;\r
1097 \r
1098     status = _AwsIotJobs_CreateOperation( JOBS_START_NEXT,\r
1099                                           pRequestInfo,\r
1100                                           &requestContents,\r
1101                                           flags,\r
1102                                           pCallbackInfo,\r
1103                                           &pOperation );\r
1104 \r
1105     if( status != AWS_IOT_JOBS_SUCCESS )\r
1106     {\r
1107         /* No memory for Jobs operation. */\r
1108         IOT_GOTO_CLEANUP();\r
1109     }\r
1110 \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
1114     {\r
1115         *pStartNextOperation = pOperation;\r
1116     }\r
1117 \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
1121 \r
1122     /* If the Jobs operation failed, clear the now invalid reference. */\r
1123     if( ( status != AWS_IOT_JOBS_STATUS_PENDING ) && ( pStartNextOperation != NULL ) )\r
1124     {\r
1125         *pStartNextOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;\r
1126     }\r
1127 \r
1128     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
1129 }\r
1130 \r
1131 /*-----------------------------------------------------------*/\r
1132 \r
1133 AwsIotJobsError_t AwsIotJobs_StartNextSync( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
1134                                             const AwsIotJobsUpdateInfo_t * pUpdateInfo,\r
1135                                             uint32_t flags,\r
1136                                             uint32_t timeoutMs,\r
1137                                             AwsIotJobsResponse_t * const pJobsResponse )\r
1138 {\r
1139     AwsIotJobsError_t status = AWS_IOT_JOBS_STATUS_PENDING;\r
1140     AwsIotJobsOperation_t startNextOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;\r
1141 \r
1142     /* Set the waitable flag. */\r
1143     flags |= AWS_IOT_JOBS_FLAG_WAITABLE;\r
1144 \r
1145     /* Call the asynchronous Jobs Start Next function. */\r
1146     status = AwsIotJobs_StartNextAsync( pRequestInfo,\r
1147                                         pUpdateInfo,\r
1148                                         flags,\r
1149                                         NULL,\r
1150                                         &startNextOperation );\r
1151 \r
1152     /* Wait for the Jobs Start Next operation to complete. */\r
1153     if( status == AWS_IOT_JOBS_STATUS_PENDING )\r
1154     {\r
1155         status = AwsIotJobs_Wait( startNextOperation,\r
1156                                   timeoutMs,\r
1157                                   pJobsResponse );\r
1158     }\r
1159 \r
1160     return status;\r
1161 }\r
1162 \r
1163 /*-----------------------------------------------------------*/\r
1164 \r
1165 AwsIotJobsError_t AwsIotJobs_DescribeAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
1166                                             int32_t executionNumber,\r
1167                                             bool includeJobDocument,\r
1168                                             uint32_t flags,\r
1169                                             const AwsIotJobsCallbackInfo_t * pCallbackInfo,\r
1170                                             AwsIotJobsOperation_t * const pDescribeOperation )\r
1171 {\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
1175 \r
1176     /* Check that AwsIotJobs_Init was called. */\r
1177     if( _checkInit() == false )\r
1178     {\r
1179         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NOT_INITIALIZED );\r
1180     }\r
1181 \r
1182     /* Check request info. */\r
1183     status = _validateRequestInfo( JOBS_DESCRIBE,\r
1184                                    pRequestInfo,\r
1185                                    flags,\r
1186                                    pCallbackInfo,\r
1187                                    pDescribeOperation );\r
1188 \r
1189     if( status != AWS_IOT_JOBS_SUCCESS )\r
1190     {\r
1191         IOT_GOTO_CLEANUP();\r
1192     }\r
1193 \r
1194     /* Allocate a new Jobs operation. */\r
1195     requestContents.describe.executionNumber = executionNumber;\r
1196     requestContents.describe.includeJobDocument = includeJobDocument;\r
1197 \r
1198     status = _AwsIotJobs_CreateOperation( JOBS_DESCRIBE,\r
1199                                           pRequestInfo,\r
1200                                           &requestContents,\r
1201                                           flags,\r
1202                                           pCallbackInfo,\r
1203                                           &pOperation );\r
1204 \r
1205     if( status != AWS_IOT_JOBS_SUCCESS )\r
1206     {\r
1207         /* No memory for Jobs operation. */\r
1208         IOT_GOTO_CLEANUP();\r
1209     }\r
1210 \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
1214     {\r
1215         *pDescribeOperation = pOperation;\r
1216     }\r
1217 \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
1221 \r
1222     /* If the Jobs operation failed, clear the now invalid reference. */\r
1223     if( ( status != AWS_IOT_JOBS_STATUS_PENDING ) && ( pDescribeOperation != NULL ) )\r
1224     {\r
1225         *pDescribeOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;\r
1226     }\r
1227 \r
1228     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
1229 }\r
1230 \r
1231 /*-----------------------------------------------------------*/\r
1232 \r
1233 AwsIotJobsError_t AwsIotJobs_DescribeSync( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
1234                                            int32_t executionNumber,\r
1235                                            bool includeJobDocument,\r
1236                                            uint32_t flags,\r
1237                                            uint32_t timeoutMs,\r
1238                                            AwsIotJobsResponse_t * const pJobsResponse )\r
1239 {\r
1240     AwsIotJobsError_t status = AWS_IOT_JOBS_STATUS_PENDING;\r
1241     AwsIotJobsOperation_t describeOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;\r
1242 \r
1243     /* Set the waitable flag. */\r
1244     flags |= AWS_IOT_JOBS_FLAG_WAITABLE;\r
1245 \r
1246     /* Call the asynchronous Jobs Describe function. */\r
1247     status = AwsIotJobs_DescribeAsync( pRequestInfo,\r
1248                                        executionNumber,\r
1249                                        includeJobDocument,\r
1250                                        flags,\r
1251                                        NULL,\r
1252                                        &describeOperation );\r
1253 \r
1254     /* Wait for the Jobs Describe operation to complete. */\r
1255     if( status == AWS_IOT_JOBS_STATUS_PENDING )\r
1256     {\r
1257         status = AwsIotJobs_Wait( describeOperation,\r
1258                                   timeoutMs,\r
1259                                   pJobsResponse );\r
1260     }\r
1261 \r
1262     return status;\r
1263 }\r
1264 \r
1265 /*-----------------------------------------------------------*/\r
1266 \r
1267 AwsIotJobsError_t AwsIotJobs_UpdateAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
1268                                           const AwsIotJobsUpdateInfo_t * pUpdateInfo,\r
1269                                           uint32_t flags,\r
1270                                           const AwsIotJobsCallbackInfo_t * pCallbackInfo,\r
1271                                           AwsIotJobsOperation_t * const pUpdateOperation )\r
1272 {\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
1276 \r
1277     /* Check that AwsIotJobs_Init was called. */\r
1278     if( _checkInit() == false )\r
1279     {\r
1280         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NOT_INITIALIZED );\r
1281     }\r
1282 \r
1283     /* Check request info. */\r
1284     status = _validateRequestInfo( JOBS_UPDATE,\r
1285                                    pRequestInfo,\r
1286                                    flags,\r
1287                                    pCallbackInfo,\r
1288                                    pUpdateOperation );\r
1289 \r
1290     if( status != AWS_IOT_JOBS_SUCCESS )\r
1291     {\r
1292         IOT_GOTO_CLEANUP();\r
1293     }\r
1294 \r
1295     /* Check update info. */\r
1296     status = _validateUpdateInfo( JOBS_UPDATE,\r
1297                                   pUpdateInfo );\r
1298 \r
1299     if( status != AWS_IOT_JOBS_SUCCESS )\r
1300     {\r
1301         IOT_GOTO_CLEANUP();\r
1302     }\r
1303 \r
1304     /* Allocate a new Jobs operation. */\r
1305     requestContents.pUpdateInfo = pUpdateInfo;\r
1306 \r
1307     status = _AwsIotJobs_CreateOperation( JOBS_UPDATE,\r
1308                                           pRequestInfo,\r
1309                                           &requestContents,\r
1310                                           flags,\r
1311                                           pCallbackInfo,\r
1312                                           &pOperation );\r
1313 \r
1314     if( status != AWS_IOT_JOBS_SUCCESS )\r
1315     {\r
1316         /* No memory for Jobs operation. */\r
1317         IOT_GOTO_CLEANUP();\r
1318     }\r
1319 \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
1323     {\r
1324         *pUpdateOperation = pOperation;\r
1325     }\r
1326 \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
1330 \r
1331     /* If the Jobs operation failed, clear the now invalid reference. */\r
1332     if( ( status != AWS_IOT_JOBS_STATUS_PENDING ) && ( pUpdateOperation != NULL ) )\r
1333     {\r
1334         *pUpdateOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;\r
1335     }\r
1336 \r
1337     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
1338 }\r
1339 \r
1340 /*-----------------------------------------------------------*/\r
1341 \r
1342 AwsIotJobsError_t AwsIotJobs_UpdateSync( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
1343                                          const AwsIotJobsUpdateInfo_t * pUpdateInfo,\r
1344                                          uint32_t flags,\r
1345                                          uint32_t timeoutMs,\r
1346                                          AwsIotJobsResponse_t * const pJobsResponse )\r
1347 {\r
1348     AwsIotJobsError_t status = AWS_IOT_JOBS_STATUS_PENDING;\r
1349     AwsIotJobsOperation_t updateOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;\r
1350 \r
1351     /* Set the waitable flag. */\r
1352     flags |= AWS_IOT_JOBS_FLAG_WAITABLE;\r
1353 \r
1354     /* Call the asynchronous Jobs Update function. */\r
1355     status = AwsIotJobs_UpdateAsync( pRequestInfo,\r
1356                                      pUpdateInfo,\r
1357                                      flags,\r
1358                                      NULL,\r
1359                                      &updateOperation );\r
1360 \r
1361     /* Wait for the Jobs Update operation to complete. */\r
1362     if( status == AWS_IOT_JOBS_STATUS_PENDING )\r
1363     {\r
1364         status = AwsIotJobs_Wait( updateOperation,\r
1365                                   timeoutMs,\r
1366                                   pJobsResponse );\r
1367     }\r
1368 \r
1369     return status;\r
1370 }\r
1371 \r
1372 /*-----------------------------------------------------------*/\r
1373 \r
1374 AwsIotJobsError_t AwsIotJobs_Wait( AwsIotJobsOperation_t operation,\r
1375                                    uint32_t timeoutMs,\r
1376                                    AwsIotJobsResponse_t * const pJobsResponse )\r
1377 {\r
1378     IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_STATUS_PENDING );\r
1379 \r
1380     /* Check that AwsIotJobs_Init was called. */\r
1381     if( _checkInit() == false )\r
1382     {\r
1383         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NOT_INITIALIZED );\r
1384     }\r
1385 \r
1386     /* Check that reference is set. */\r
1387     if( operation == NULL )\r
1388     {\r
1389         IotLogError( "Operation reference cannot be NULL." );\r
1390 \r
1391         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
1392     }\r
1393 \r
1394     /* Check that reference is waitable. */\r
1395     if( ( operation->flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == 0 )\r
1396     {\r
1397         IotLogError( "Operation is not waitable." );\r
1398 \r
1399         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
1400     }\r
1401 \r
1402     /* Check that output parameter is set. */\r
1403     if( pJobsResponse == NULL )\r
1404     {\r
1405         IotLogError( "Output Jobs response parameter must be set for Jobs %s.",\r
1406                      _pAwsIotJobsOperationNames[ operation->type ] );\r
1407 \r
1408         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );\r
1409     }\r
1410 \r
1411     /* Wait for a response to the Jobs operation. */\r
1412     if( IotSemaphore_TimedWait( &( operation->notify.waitSemaphore ),\r
1413                                 timeoutMs ) == true )\r
1414     {\r
1415         status = operation->status;\r
1416     }\r
1417     else\r
1418     {\r
1419         status = AWS_IOT_JOBS_TIMEOUT;\r
1420     }\r
1421 \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
1426 \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
1432                                      NULL );\r
1433     IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );\r
1434 \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
1438     {\r
1439         AwsIotJobs_Assert( operation->pJobsResponse != NULL );\r
1440         AwsIotJobs_Assert( operation->jobsResponseLength > 0 );\r
1441 \r
1442         pJobsResponse->pJobsResponse = operation->pJobsResponse;\r
1443         pJobsResponse->jobsResponseLength = operation->jobsResponseLength;\r
1444     }\r
1445 \r
1446     /* Destroy the Jobs operation. */\r
1447     _AwsIotJobs_DestroyOperation( operation );\r
1448 \r
1449     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
1450 }\r
1451 \r
1452 /*-----------------------------------------------------------*/\r
1453 \r
1454 AwsIotJobsError_t AwsIotJobs_SetNotifyPendingCallback( IotMqttConnection_t mqttConnection,\r
1455                                                        const char * pThingName,\r
1456                                                        size_t thingNameLength,\r
1457                                                        uint32_t flags,\r
1458                                                        const AwsIotJobsCallbackInfo_t * pNotifyPendingCallback )\r
1459 {\r
1460     /* Flags are not currently used by this function. */\r
1461     ( void ) flags;\r
1462 \r
1463     return _setCallbackCommon( mqttConnection,\r
1464                                NOTIFY_PENDING_CALLBACK,\r
1465                                pThingName,\r
1466                                thingNameLength,\r
1467                                pNotifyPendingCallback );\r
1468 }\r
1469 \r
1470 /*-----------------------------------------------------------*/\r
1471 \r
1472 AwsIotJobsError_t AwsIotJobs_SetNotifyNextCallback( IotMqttConnection_t mqttConnection,\r
1473                                                     const char * pThingName,\r
1474                                                     size_t thingNameLength,\r
1475                                                     uint32_t flags,\r
1476                                                     const AwsIotJobsCallbackInfo_t * pNotifyNextCallback )\r
1477 {\r
1478     /* Flags are not currently used by this function. */\r
1479     ( void ) flags;\r
1480 \r
1481     return _setCallbackCommon( mqttConnection,\r
1482                                NOTIFY_NEXT_CALLBACK,\r
1483                                pThingName,\r
1484                                thingNameLength,\r
1485                                pNotifyNextCallback );\r
1486 }\r
1487 \r
1488 /*-----------------------------------------------------------*/\r
1489 \r
1490 const char * AwsIotJobs_strerror( AwsIotJobsError_t status )\r
1491 {\r
1492     const char * pMessage = NULL;\r
1493 \r
1494     switch( status )\r
1495     {\r
1496         case AWS_IOT_JOBS_SUCCESS:\r
1497             pMessage = "SUCCESS";\r
1498             break;\r
1499 \r
1500         case AWS_IOT_JOBS_STATUS_PENDING:\r
1501             pMessage = "STATUS PENDING";\r
1502             break;\r
1503 \r
1504         case AWS_IOT_JOBS_INIT_FAILED:\r
1505             pMessage = "INIT FAILED";\r
1506             break;\r
1507 \r
1508         case AWS_IOT_JOBS_BAD_PARAMETER:\r
1509             pMessage = "BAD PARAMETER";\r
1510             break;\r
1511 \r
1512         case AWS_IOT_JOBS_NO_MEMORY:\r
1513             pMessage = "NO MEMORY";\r
1514             break;\r
1515 \r
1516         case AWS_IOT_JOBS_MQTT_ERROR:\r
1517             pMessage = "MQTT ERROR";\r
1518             break;\r
1519 \r
1520         case AWS_IOT_JOBS_BAD_RESPONSE:\r
1521             pMessage = "BAD RESPONSE";\r
1522             break;\r
1523 \r
1524         case AWS_IOT_JOBS_TIMEOUT:\r
1525             pMessage = "TIMEOUT";\r
1526             break;\r
1527 \r
1528         case AWS_IOT_JOBS_NOT_INITIALIZED:\r
1529             pMessage = "NOT INITIALIZED";\r
1530             break;\r
1531 \r
1532         case AWS_IOT_JOBS_INVALID_TOPIC:\r
1533             pMessage = "FAILED: INVALID TOPIC";\r
1534             break;\r
1535 \r
1536         case AWS_IOT_JOBS_INVALID_JSON:\r
1537             pMessage = "FAILED: INVALID JSON";\r
1538             break;\r
1539 \r
1540         case AWS_IOT_JOBS_INVALID_REQUEST:\r
1541             pMessage = "FAILED: INVALID REQUEST";\r
1542             break;\r
1543 \r
1544         case AWS_IOT_JOBS_INVALID_STATE:\r
1545             pMessage = "FAILED: INVALID STATE TRANSITION";\r
1546             break;\r
1547 \r
1548         case AWS_IOT_JOBS_NOT_FOUND:\r
1549             pMessage = "FAILED: NOT FOUND";\r
1550             break;\r
1551 \r
1552         case AWS_IOT_JOBS_VERSION_MISMATCH:\r
1553             pMessage = "FAILED: VERSION MISMATCH";\r
1554             break;\r
1555 \r
1556         case AWS_IOT_JOBS_INTERNAL_ERROR:\r
1557             pMessage = "FAILED: INTERNAL ERROR";\r
1558             break;\r
1559 \r
1560         case AWS_IOT_JOBS_THROTTLED:\r
1561             pMessage = "FAILED: THROTTLED";\r
1562             break;\r
1563 \r
1564         case AWS_IOT_JOBS_TERMINAL_STATE:\r
1565             pMessage = "FAILED: TERMINAL STATE";\r
1566             break;\r
1567 \r
1568         default:\r
1569             pMessage = "INVALID STATUS";\r
1570             break;\r
1571     }\r
1572 \r
1573     return pMessage;\r
1574 }\r
1575 \r
1576 /*-----------------------------------------------------------*/\r
1577 \r
1578 const char * AwsIotJobs_StateName( AwsIotJobState_t state )\r
1579 {\r
1580     const char * pMessage = NULL;\r
1581 \r
1582     switch( state )\r
1583     {\r
1584         case AWS_IOT_JOB_STATE_QUEUED:\r
1585             pMessage = "QUEUED";\r
1586             break;\r
1587 \r
1588         case AWS_IOT_JOB_STATE_IN_PROGRESS:\r
1589             pMessage = "IN PROGRESS";\r
1590             break;\r
1591 \r
1592         case AWS_IOT_JOB_STATE_FAILED:\r
1593             pMessage = "FAILED";\r
1594             break;\r
1595 \r
1596         case AWS_IOT_JOB_STATE_SUCCEEDED:\r
1597             pMessage = "SUCCEEDED";\r
1598             break;\r
1599 \r
1600         case AWS_IOT_JOB_STATE_CANCELED:\r
1601             pMessage = "CANCELED";\r
1602             break;\r
1603 \r
1604         case AWS_IOT_JOB_STATE_TIMED_OUT:\r
1605             pMessage = "TIMED OUT";\r
1606             break;\r
1607 \r
1608         case AWS_IOT_JOB_STATE_REJECTED:\r
1609             pMessage = "REJECTED";\r
1610             break;\r
1611 \r
1612         case AWS_IOT_JOB_STATE_REMOVED:\r
1613             pMessage = "REMOVED";\r
1614             break;\r
1615 \r
1616         default:\r
1617             pMessage = "INVALID STATE";\r
1618             break;\r
1619     }\r
1620 \r
1621     return pMessage;\r
1622 }\r
1623 \r
1624 /*-----------------------------------------------------------*/\r