]> git.sur5r.net Git - freertos/blob - FreeRTOS-Labs/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/shadow/src/aws_iot_shadow_subscription.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Source / FreeRTOS-IoT-Libraries / c_sdk / aws / shadow / src / aws_iot_shadow_subscription.c
1 /*\r
2  * AWS IoT Shadow V2.1.0\r
3  * Copyright (C) 2018 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_shadow_subscription.c\r
25  * @brief Implements functions for interacting with the Shadow library's\r
26  * subscription list.\r
27  */\r
28 \r
29 /* The config header is always included first. */\r
30 #include "iot_config.h"\r
31 \r
32 /* Standard includes. */\r
33 #include <string.h>\r
34 \r
35 /* Shadow internal include. */\r
36 #include "private/aws_iot_shadow_internal.h"\r
37 \r
38 /* Error handling include. */\r
39 #include "iot_error.h"\r
40 \r
41 /* Platform layer includes. */\r
42 #include "platform/iot_threads.h"\r
43 \r
44 /* MQTT include. */\r
45 #include "iot_mqtt.h"\r
46 \r
47 /*-----------------------------------------------------------*/\r
48 \r
49 /**\r
50  * @brief Match two #_shadowSubscription_t by Thing Name.\r
51  *\r
52  * @param[in] pSubscriptionLink Pointer to the link member of a #_shadowSubscription_t\r
53  * containing the Thing Name to check.\r
54  * @param[in] pMatch Pointer to an `AwsIotThingName_t`.\r
55  *\r
56  * @return `true` if the Thing Names match; `false` otherwise.\r
57  */\r
58 static bool _shadowSubscription_match( const IotLink_t * pSubscriptionLink,\r
59                                        void * pMatch );\r
60 \r
61 /*-----------------------------------------------------------*/\r
62 \r
63 /**\r
64  * @brief List of active Shadow subscriptions objects.\r
65  */\r
66 IotListDouble_t _AwsIotShadowSubscriptions = { 0 };\r
67 \r
68 /**\r
69  * @brief Protects #_AwsIotShadowSubscriptions from concurrent access.\r
70  */\r
71 IotMutex_t _AwsIotShadowSubscriptionsMutex;\r
72 \r
73 /*-----------------------------------------------------------*/\r
74 \r
75 static bool _shadowSubscription_match( const IotLink_t * pSubscriptionLink,\r
76                                        void * pMatch )\r
77 {\r
78     bool match = false;\r
79 \r
80     /* Because this function is called from a container function, the given link\r
81      * must never be NULL. */\r
82     AwsIotShadow_Assert( pSubscriptionLink != NULL );\r
83 \r
84     const _shadowSubscription_t * pSubscription = IotLink_Container( _shadowSubscription_t,\r
85                                                                      pSubscriptionLink,\r
86                                                                      link );\r
87     const AwsIotThingName_t * pThingName = ( AwsIotThingName_t * ) pMatch;\r
88 \r
89     if( pThingName->thingNameLength == pSubscription->thingNameLength )\r
90     {\r
91         /* Check for matching Thing Names. */\r
92         match = ( strncmp( pThingName->pThingName,\r
93                            pSubscription->pThingName,\r
94                            pThingName->thingNameLength ) == 0 );\r
95     }\r
96 \r
97     return match;\r
98 }\r
99 \r
100 /*-----------------------------------------------------------*/\r
101 \r
102 _shadowSubscription_t * _AwsIotShadow_FindSubscription( const char * pThingName,\r
103                                                         size_t thingNameLength,\r
104                                                         bool createIfNotFound )\r
105 {\r
106     _shadowSubscription_t * pSubscription = NULL;\r
107     IotLink_t * pSubscriptionLink = NULL;\r
108     AwsIotThingName_t thingName = { 0 };\r
109 \r
110     thingName.pThingName = pThingName;\r
111     thingName.thingNameLength = thingNameLength;\r
112 \r
113     /* Search the list for an existing subscription for Thing Name. */\r
114     pSubscriptionLink = IotListDouble_FindFirstMatch( &( _AwsIotShadowSubscriptions ),\r
115                                                       NULL,\r
116                                                       _shadowSubscription_match,\r
117                                                       &thingName );\r
118 \r
119     /* Check if a subscription was found. */\r
120     if( pSubscriptionLink == NULL )\r
121     {\r
122         if( createIfNotFound == true )\r
123         {\r
124             /* No subscription found. Allocate a new subscription. */\r
125             pSubscription = AwsIotShadow_MallocSubscription( sizeof( _shadowSubscription_t ) + thingNameLength );\r
126 \r
127             if( pSubscription != NULL )\r
128             {\r
129                 /* Clear the new subscription. */\r
130                 ( void ) memset( pSubscription, 0x00, sizeof( _shadowSubscription_t ) + thingNameLength );\r
131 \r
132                 /* Set the Thing Name length and copy the Thing Name into the new subscription. */\r
133                 pSubscription->thingNameLength = thingNameLength;\r
134                 ( void ) memcpy( pSubscription->pThingName, pThingName, thingNameLength );\r
135 \r
136                 /* Add the new subscription to the subscription list. */\r
137                 IotListDouble_InsertHead( &( _AwsIotShadowSubscriptions ),\r
138                                           &( pSubscription->link ) );\r
139 \r
140                 IotLogDebug( "Created new Shadow subscriptions object for %.*s.",\r
141                              thingNameLength,\r
142                              pThingName );\r
143             }\r
144             else\r
145             {\r
146                 IotLogError( "Failed to allocate memory for %.*s Shadow subscriptions.",\r
147                              thingNameLength,\r
148                              pThingName );\r
149             }\r
150         }\r
151     }\r
152     else\r
153     {\r
154         IotLogDebug( "Found existing Shadow subscriptions object for %.*s.",\r
155                      thingNameLength,\r
156                      pThingName );\r
157 \r
158         pSubscription = IotLink_Container( _shadowSubscription_t, pSubscriptionLink, link );\r
159     }\r
160 \r
161     return pSubscription;\r
162 }\r
163 \r
164 /*-----------------------------------------------------------*/\r
165 \r
166 void _AwsIotShadow_RemoveSubscription( _shadowSubscription_t * pSubscription,\r
167                                        _shadowSubscription_t ** pRemovedSubscription )\r
168 {\r
169     int32_t i = 0;\r
170     bool removeSubscription = true;\r
171 \r
172     IotLogDebug( "Checking if subscription object for %.*s can be removed.",\r
173                  pSubscription->thingNameLength,\r
174                  pSubscription->pThingName );\r
175 \r
176     /* Check for active operations. If any Shadow operation's subscription\r
177      * reference count is not 0, then the subscription cannot be removed. */\r
178     for( i = 0; i < SHADOW_OPERATION_COUNT; i++ )\r
179     {\r
180         if( pSubscription->references[ i ] > 0 )\r
181         {\r
182             IotLogDebug( "Reference count %ld for %.*s subscription object. "\r
183                          "Subscription cannot be removed yet.",\r
184                          ( long int ) pSubscription->references[ i ],\r
185                          pSubscription->thingNameLength,\r
186                          pSubscription->pThingName );\r
187 \r
188             removeSubscription = false;\r
189         }\r
190         else if( pSubscription->references[ i ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )\r
191         {\r
192             IotLogDebug( "Subscription object for %.*s has persistent subscriptions. "\r
193                          "Subscription will not be removed.",\r
194                          pSubscription->thingNameLength,\r
195                          pSubscription->pThingName );\r
196 \r
197             removeSubscription = false;\r
198         }\r
199 \r
200         if( removeSubscription == false )\r
201         {\r
202             break;\r
203         }\r
204     }\r
205 \r
206     /* Check for active subscriptions. If any Shadow callbacks are active, then the\r
207      * subscription cannot be removed. */\r
208     if( removeSubscription == true )\r
209     {\r
210         for( i = 0; i < SHADOW_CALLBACK_COUNT; i++ )\r
211         {\r
212             if( pSubscription->callbacks[ i ].function != NULL )\r
213             {\r
214                 IotLogDebug( "Found active Shadow %s callback for %.*s subscription object. "\r
215                              "Subscription cannot be removed yet.",\r
216                              _pAwsIotShadowCallbackNames[ i ],\r
217                              pSubscription->thingNameLength,\r
218                              pSubscription->pThingName );\r
219 \r
220                 removeSubscription = false;\r
221                 break;\r
222             }\r
223         }\r
224     }\r
225 \r
226     /* Remove the subscription if unused. */\r
227     if( removeSubscription == true )\r
228     {\r
229         /* No Shadow operation subscription references or active Shadow callbacks.\r
230          * Remove the subscription object. */\r
231         IotListDouble_Remove( &( pSubscription->link ) );\r
232 \r
233         IotLogDebug( "Removed subscription object for %.*s.",\r
234                      pSubscription->thingNameLength,\r
235                      pSubscription->pThingName );\r
236 \r
237         /* If the caller requested the removed subscription, set the output parameter.\r
238          * Otherwise, free the memory used by the subscription. */\r
239         if( pRemovedSubscription != NULL )\r
240         {\r
241             *pRemovedSubscription = pSubscription;\r
242         }\r
243         else\r
244         {\r
245             _AwsIotShadow_DestroySubscription( pSubscription );\r
246         }\r
247     }\r
248 }\r
249 \r
250 /*-----------------------------------------------------------*/\r
251 \r
252 void _AwsIotShadow_DestroySubscription( void * pData )\r
253 {\r
254     _shadowSubscription_t * pSubscription = ( _shadowSubscription_t * ) pData;\r
255 \r
256     /* Free the topic buffer if allocated. */\r
257     if( pSubscription->pTopicBuffer != NULL )\r
258     {\r
259         AwsIotShadow_FreeString( pSubscription->pTopicBuffer );\r
260     }\r
261 \r
262     /* Free memory used by subscription. */\r
263     AwsIotShadow_FreeSubscription( pSubscription );\r
264 }\r
265 \r
266 /*-----------------------------------------------------------*/\r
267 \r
268 AwsIotShadowError_t _AwsIotShadow_IncrementReferences( _shadowOperation_t * pOperation,\r
269                                                        char * pTopicBuffer,\r
270                                                        uint16_t operationTopicLength,\r
271                                                        AwsIotMqttCallbackFunction_t callback )\r
272 {\r
273     IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_SUCCESS );\r
274     const _shadowOperationType_t type = pOperation->type;\r
275     _shadowSubscription_t * pSubscription = pOperation->pSubscription;\r
276     IotMqttError_t subscriptionStatus = IOT_MQTT_STATUS_PENDING;\r
277     AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };\r
278 \r
279     /* Do nothing if this operation has persistent subscriptions. */\r
280     if( pSubscription->references[ type ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )\r
281     {\r
282         IotLogDebug( "Shadow %s for %.*s has persistent subscriptions. Reference "\r
283                      "count will not be incremented.",\r
284                      _pAwsIotShadowOperationNames[ type ],\r
285                      pSubscription->thingNameLength,\r
286                      pSubscription->pThingName );\r
287 \r
288         IOT_GOTO_CLEANUP();\r
289     }\r
290 \r
291     /* When persistent subscriptions are not present, the reference count must\r
292      * not be negative. */\r
293     AwsIotShadow_Assert( pSubscription->references[ type ] >= 0 );\r
294 \r
295     /* Check if there are any existing references for this operation. */\r
296     if( pSubscription->references[ type ] == 0 )\r
297     {\r
298         /* Set the parameters needed to add subscriptions. */\r
299         subscriptionInfo.mqttConnection = pOperation->mqttConnection;\r
300         subscriptionInfo.callbackFunction = callback;\r
301         subscriptionInfo.timeout = _AwsIotShadowMqttTimeoutMs;\r
302         subscriptionInfo.pTopicFilterBase = pTopicBuffer;\r
303         subscriptionInfo.topicFilterBaseLength = operationTopicLength;\r
304 \r
305         subscriptionStatus = AwsIot_ModifySubscriptions( IotMqtt_SubscribeSync,\r
306                                                          &subscriptionInfo );\r
307 \r
308         /* Convert MQTT return code to Shadow return code. */\r
309         status = SHADOW_CONVERT_STATUS_CODE_MQTT_TO_SHADOW( subscriptionStatus );\r
310 \r
311         if( status != AWS_IOT_SHADOW_SUCCESS )\r
312         {\r
313             IOT_GOTO_CLEANUP();\r
314         }\r
315     }\r
316 \r
317     /* Increment the number of subscription references for this operation when\r
318      * the keep subscriptions flag is not set. */\r
319     if( ( pOperation->flags & AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS ) == 0 )\r
320     {\r
321         ( pSubscription->references[ type ] )++;\r
322 \r
323         IotLogDebug( "Shadow %s subscriptions for %.*s now has count %d.",\r
324                      _pAwsIotShadowOperationNames[ type ],\r
325                      pSubscription->thingNameLength,\r
326                      pSubscription->pThingName,\r
327                      pSubscription->references[ type ] );\r
328     }\r
329     /* Otherwise, set the persistent subscriptions flag. */\r
330     else\r
331     {\r
332         pSubscription->references[ type ] = AWS_IOT_PERSISTENT_SUBSCRIPTION;\r
333 \r
334         IotLogDebug( "Set persistent subscriptions flag for Shadow %s of %.*s.",\r
335                      _pAwsIotShadowOperationNames[ type ],\r
336                      pSubscription->thingNameLength,\r
337                      pSubscription->pThingName );\r
338     }\r
339 \r
340     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
341 }\r
342 \r
343 /*-----------------------------------------------------------*/\r
344 \r
345 void _AwsIotShadow_DecrementReferences( _shadowOperation_t * pOperation,\r
346                                         char * pTopicBuffer,\r
347                                         _shadowSubscription_t ** pRemovedSubscription )\r
348 {\r
349     const _shadowOperationType_t type = pOperation->type;\r
350     _shadowSubscription_t * pSubscription = pOperation->pSubscription;\r
351     uint16_t operationTopicLength = 0;\r
352     AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };\r
353 \r
354     /* Do nothing if this Shadow operation has persistent subscriptions. */\r
355     if( pSubscription->references[ type ] != AWS_IOT_PERSISTENT_SUBSCRIPTION )\r
356     {\r
357         /* Decrement the number of subscription references for this operation.\r
358          * Ensure that it's positive. */\r
359         ( pSubscription->references[ type ] )--;\r
360         AwsIotShadow_Assert( pSubscription->references[ type ] >= 0 );\r
361 \r
362         /* Check if the number of references has reached 0. */\r
363         if( pSubscription->references[ type ] == 0 )\r
364         {\r
365             IotLogDebug( "Reference count for %.*s %s is 0. Unsubscribing.",\r
366                          pSubscription->thingNameLength,\r
367                          pSubscription->pThingName,\r
368                          _pAwsIotShadowOperationNames[ type ] );\r
369 \r
370             /* Subscription must have a topic buffer. */\r
371             AwsIotShadow_Assert( pSubscription->pTopicBuffer != NULL );\r
372 \r
373             /* Generate the prefix of the Shadow topic. This function will not\r
374              * fail when given a buffer. */\r
375             ( void ) _AwsIotShadow_GenerateShadowTopic( ( _shadowOperationType_t ) type,\r
376                                                         pSubscription->pThingName,\r
377                                                         pSubscription->thingNameLength,\r
378                                                         &( pSubscription->pTopicBuffer ),\r
379                                                         &operationTopicLength );\r
380 \r
381             /* Set the parameters needed to remove subscriptions. */\r
382             subscriptionInfo.mqttConnection = pOperation->mqttConnection;\r
383             subscriptionInfo.timeout = _AwsIotShadowMqttTimeoutMs;\r
384             subscriptionInfo.pTopicFilterBase = pTopicBuffer;\r
385             subscriptionInfo.topicFilterBaseLength = operationTopicLength;\r
386 \r
387             ( void ) AwsIot_ModifySubscriptions( IotMqtt_UnsubscribeSync,\r
388                                                  &subscriptionInfo );\r
389         }\r
390 \r
391         /* Check if this subscription should be deleted. */\r
392         _AwsIotShadow_RemoveSubscription( pSubscription,\r
393                                           pRemovedSubscription );\r
394     }\r
395     else\r
396     {\r
397         IotLogDebug( "Shadow %s for %.*s has persistent subscriptions. Reference "\r
398                      "count will not be decremented.",\r
399                      _pAwsIotShadowOperationNames[ type ],\r
400                      pSubscription->thingNameLength,\r
401                      pSubscription->pThingName );\r
402     }\r
403 }\r
404 \r
405 /*-----------------------------------------------------------*/\r
406 \r
407 AwsIotShadowError_t AwsIotShadow_RemovePersistentSubscriptions( IotMqttConnection_t mqttConnection,\r
408                                                                 const char * pThingName,\r
409                                                                 size_t thingNameLength,\r
410                                                                 uint32_t flags )\r
411 {\r
412     int32_t i = 0;\r
413     uint16_t operationTopicLength = 0;\r
414     AwsIotShadowError_t status = AWS_IOT_SHADOW_STATUS_PENDING;\r
415     IotMqttError_t unsubscribeStatus = IOT_MQTT_STATUS_PENDING;\r
416     AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };\r
417     _shadowSubscription_t * pSubscription = NULL;\r
418     IotLink_t * pSubscriptionLink = NULL;\r
419     AwsIotThingName_t thingName = { 0 };\r
420 \r
421     thingName.pThingName = pThingName;\r
422     thingName.thingNameLength = thingNameLength;\r
423 \r
424     IotLogInfo( "Removing persistent subscriptions for %.*s.",\r
425                 thingNameLength,\r
426                 pThingName );\r
427 \r
428     IotMutex_Lock( &( _AwsIotShadowSubscriptionsMutex ) );\r
429 \r
430     /* Search the list for an existing subscription for Thing Name. */\r
431     pSubscriptionLink = IotListDouble_FindFirstMatch( &( _AwsIotShadowSubscriptions ),\r
432                                                       NULL,\r
433                                                       _shadowSubscription_match,\r
434                                                       &thingName );\r
435 \r
436     /* Unsubscribe from operation subscriptions if found. */\r
437     if( pSubscriptionLink != NULL )\r
438     {\r
439         IotLogDebug( "Found subscription object for %.*s. Checking for persistent "\r
440                      "subscriptions to remove.",\r
441                      thingNameLength,\r
442                      pThingName );\r
443 \r
444         pSubscription = IotLink_Container( _shadowSubscription_t, pSubscriptionLink, link );\r
445 \r
446         for( i = 0; i < SHADOW_OPERATION_COUNT; i++ )\r
447         {\r
448             if( ( flags & ( 0x1UL << i ) ) != 0 )\r
449             {\r
450                 IotLogDebug( "Removing %.*s %s persistent subscriptions.",\r
451                              thingNameLength,\r
452                              pThingName,\r
453                              _pAwsIotShadowOperationNames[ i ] );\r
454 \r
455                 /* Subscription must have a topic buffer. */\r
456                 AwsIotShadow_Assert( pSubscription->pTopicBuffer != NULL );\r
457 \r
458                 if( pSubscription->references[ i ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )\r
459                 {\r
460                     /* Generate the prefix of the Shadow topic. This function will not\r
461                      * fail when given a buffer. */\r
462                     ( void ) _AwsIotShadow_GenerateShadowTopic( ( _shadowOperationType_t ) i,\r
463                                                                 pThingName,\r
464                                                                 thingNameLength,\r
465                                                                 &( pSubscription->pTopicBuffer ),\r
466                                                                 &operationTopicLength );\r
467 \r
468                     /* Set the parameters needed to remove subscriptions. */\r
469                     subscriptionInfo.mqttConnection = mqttConnection;\r
470                     subscriptionInfo.timeout = _AwsIotShadowMqttTimeoutMs;\r
471                     subscriptionInfo.pTopicFilterBase = pSubscription->pTopicBuffer;\r
472                     subscriptionInfo.topicFilterBaseLength = operationTopicLength;\r
473 \r
474                     unsubscribeStatus = AwsIot_ModifySubscriptions( IotMqtt_UnsubscribeSync,\r
475                                                                     &subscriptionInfo );\r
476 \r
477                     /* Convert MQTT return code to Shadow return code. */\r
478                     status = SHADOW_CONVERT_STATUS_CODE_MQTT_TO_SHADOW( unsubscribeStatus );\r
479 \r
480                     if( status != AWS_IOT_SHADOW_SUCCESS )\r
481                     {\r
482                         break;\r
483                     }\r
484 \r
485                     /* Clear the persistent subscriptions flag and check if the\r
486                      * subscription can be removed. */\r
487                     pSubscription->references[ i ] = 0;\r
488                     _AwsIotShadow_RemoveSubscription( pSubscription, NULL );\r
489                 }\r
490                 else\r
491                 {\r
492                     IotLogDebug( "%.*s %s does not have persistent subscriptions.",\r
493                                  thingNameLength,\r
494                                  pThingName,\r
495                                  _pAwsIotShadowOperationNames[ i ] );\r
496                 }\r
497             }\r
498         }\r
499     }\r
500     else\r
501     {\r
502         IotLogWarn( "No subscription object found for %.*s",\r
503                     thingNameLength,\r
504                     pThingName );\r
505     }\r
506 \r
507     IotMutex_Unlock( &( _AwsIotShadowSubscriptionsMutex ) );\r
508 \r
509     return status;\r
510 }\r
511 \r
512 /*-----------------------------------------------------------*/\r