Asterisk - The Open Source Telephony Project GIT-master-266378a
Loading...
Searching...
No Matches
extension_state.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2026, Sangoma Technologies Corporation
5 *
6 * Joshua Colp <jcolp@sangoma.com>
7 *
8 * See https://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*** MODULEINFO
20 <support_level>core</support_level>
21 ***/
22
23#include "asterisk.h"
24#include "asterisk/_private.h"
25#include "asterisk/module.h"
27#include "asterisk/pbx.h"
28#include "asterisk/stasis.h"
30#include "asterisk/astobj2.h"
31#include "asterisk/cli.h"
32#include "pbx_private.h"
33
34/*** DOCUMENTATION
35 <manager name="ExtensionStateList" language="en_US">
36 <since>
37 <version>13.0.0</version>
38 </since>
39 <synopsis>
40 List the current known extension states.
41 </synopsis>
42 <syntax>
43 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
44 </syntax>
45 <description>
46 <para>This will list out all known extension states in a
47 sequence of <replaceable>ExtensionStatus</replaceable> events.
48 When finished, a <replaceable>ExtensionStateListComplete</replaceable> event
49 will be emitted.</para>
50 </description>
51 <see-also>
52 <ref type="manager">ExtensionState</ref>
53 <ref type="function">HINT</ref>
54 <ref type="function">EXTENSION_STATE</ref>
55 </see-also>
56 <responses>
57 <list-elements>
58 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ExtensionStatus'])" />
59 </list-elements>
60 <managerEvent name="ExtensionStateListComplete" language="en_US">
61 <managerEventInstance class="EVENT_FLAG_COMMAND">
62 <since>
63 <version>13.0.0</version>
64 </since>
65 <synopsis>
66 Indicates the end of the list the current known extension states.
67 </synopsis>
68 <syntax>
69 <parameter name="EventList">
70 <para>Conveys the status of the event list.</para>
71 </parameter>
72 <parameter name="ListItems">
73 <para>Conveys the number of statuses reported.</para>
74 </parameter>
75 </syntax>
76 </managerEventInstance>
77 </managerEvent>
78 </responses>
79 </manager>
80 ***/
81
82#define HINTDEVICE_DATA_LENGTH 16
83AST_THREADSTORAGE(hintdevice_data);
84
85/*! \brief Device state source feeding an extension state */
87 /*! \brief The current state of the device - this is immutable */
89 /*! \brief Synchronous subscription to the device state topic */
91 /*! \brief The current version for this source */
92 unsigned int version;
93};
94
96
97/*! \brief Extension state information */
99 /*! \brief The current device snapshot for the extension */
101 /*! \brief The current presence snapshot for the extension */
103 /*! \brief The extension state topic for this extension */
105 /*! \brief Forwarder from per-extension topic to all topic */
107 /*! \brief Device state sources feeding the hint topic, and their forwarding */
109 /*! \brief The string representation of all presence state sources feeding this extension state */
111 /*! \brief The dialplan hint that last configured this extension state */
113 /*! \brief The dialplan context */
115 /*! \brief The dialplan extension */
117 /*! \brief The combined extension this state is for (extension@context) */
118 char extension[0];
119};
120
121/*! \brief Number of buckets for extension states */
122#ifdef LOW_MEMORY
123#define EXTENSION_STATE_BUCKETS 17
124#else
125#define EXTENSION_STATE_BUCKETS 563
126#endif
127
128static const struct cfextension_states {
130 const char * const text;
132 { AST_EXTENSION_NOT_INUSE, "Idle" },
133 { AST_EXTENSION_INUSE, "InUse" },
134 { AST_EXTENSION_BUSY, "Busy" },
135 { AST_EXTENSION_UNAVAILABLE, "Unavailable" },
136 { AST_EXTENSION_RINGING, "Ringing" },
137 { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
138 { AST_EXTENSION_ONHOLD, "Hold" },
139 { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
141
142/*! \brief The global container of extension states */
144
145/*! \brief Topic which receives all extension state updates */
147
148/*! \brief Single presence state subscription, for all extension states */
150
151/*! \brief Sort function for extension states */
153
154/*! \brief Compare function for extension states */
156
157/*! \brief Message type for extension state updates */
159
160/*!
161 * \internal
162 * \brief Destroy an extension state update message
163 * \param obj The extension state update message to destroy
164 */
166{
167 struct ast_extension_state_update_message *update_message = obj;
168
169 ao2_cleanup(update_message->old_device_snapshot);
170 ao2_cleanup(update_message->new_device_snapshot);
171 ao2_cleanup(update_message->old_presence_snapshot);
172 ao2_cleanup(update_message->new_presence_snapshot);
173}
174
175/*!
176 * \internal
177 * \brief Create an extension state update message
178 *
179 * \param context The context of the extension
180 * \param extension The extension
181 * \param old_device_snapshot The old device state snapshot
182 * \param new_device_snapshot The new device state snapshot
183 * \param old_presence_snapshot The old presence state snapshot
184 * \param new_presence_snapshot The new presence state snapshot
185 * \retval An allocated extension state update message, or NULL on failure
186 *
187 * This function creates an extension state update message for the specified context, extension,
188 * old device state snapshot, new device state snapshot, old presence state snapshot, and new presence state snapshot.
189 */
194{
195 size_t context_len = strlen(context) + 1;
196 size_t extension_len = strlen(extension) + 1;
197 struct ast_extension_state_update_message *update_message;
198
199 update_message = ao2_alloc_options(sizeof(*update_message) + context_len + extension_len,
201 if (!update_message) {
202 return NULL;
203 }
204
205 ast_copy_string(update_message->extension, extension, extension_len); /* Safe */
206 update_message->context = update_message->extension + extension_len;
207 ast_copy_string(update_message->context, context, context_len); /* Safe */
208
213
214 return update_message;
215}
216
217/*!
218 * \internal
219 * \brief Destroy an extension state device source
220 * \param source The extension state device source to destroy
221 *
222 * This function destroys an extension state device source by unsubscribing from the device state
223 * topic and cleaning up the associated resources.
224 */
226{
227 if (source->device_state_subscription) {
229 }
230 ao2_cleanup(source->info);
231 ast_free(source);
232}
233
234/*!
235 * \internal
236 * \brief Allocate an extension device state info object
237 *
238 * \param device The device name
239 * \param state The device state
240 * \retval An allocated extension device state info object, or NULL on failure
241 *
242 * This function allocates an extension device state info object with the specified device name and state.
243 */
246{
248
249 info = ao2_alloc_options(sizeof(*info) + strlen(device) + 1, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
250 if (!info) {
251 return NULL;
252 }
253
254 info->state = state;
255 strcpy(info->device, device); /* Safe */
256
257 return info;
258}
259
260/*!
261 * \internal
262 * \brief Destroy an extension state device snapshot
263 *
264 * \param obj The extension state device snapshot to destroy
265 *
266 * This function destroys an extension state device snapshot by cleaning up
267 * the causing device and additional devices.
268 */
270{
271 struct ast_extension_state_device_snapshot *device_snapshot = obj;
272
273 ao2_cleanup(device_snapshot->causing_device);
275 AST_VECTOR_FREE(&device_snapshot->additional_devices);
276}
277
278/*!
279 * \internal
280 * \brief Create an extension state device snapshot
281 *
282 * \param device_state The device state
283 * \param device_state_sources The device state sources
284 * \param causing_device The causing device
285 * \retval An allocated extension state device snapshot, or NULL on failure
286 *
287 * This function creates an extension state device snapshot with the device state,
288 * device state sources, and causing device.
289 */
291 enum ast_extension_states device_state, struct device_state_sources_vector *device_state_sources,
293{
294 struct ast_extension_state_device_snapshot *device_snapshot;
295 int i;
296
297 device_snapshot = ao2_alloc_options(sizeof(*device_snapshot),
299 if (!device_snapshot) {
300 return NULL;
301 }
302
303 device_snapshot->state = device_state;
304 device_snapshot->causing_device = ao2_bump(causing_device);
305 if (AST_VECTOR_INIT(&device_snapshot->additional_devices, AST_VECTOR_SIZE(device_state_sources))) {
306 ao2_ref(device_snapshot, -1);
307 return NULL;
308 }
309
310 for (i = 0; i < AST_VECTOR_SIZE(device_state_sources); i++) {
311 struct extension_state_device_source *source = AST_VECTOR_GET(device_state_sources, i);
312
313 if (causing_device && source->info == causing_device) {
314 continue;
315 }
316
317 AST_VECTOR_APPEND(&device_snapshot->additional_devices, ao2_bump(source->info));
318 }
319
320 return device_snapshot;
321}
322
323/*!
324 * \internal
325 * \brief Callback for device state changes
326 *
327 * \param userdata The extension state to update
328 * \param sub The subscription
329 * \param msg The device state message
330 *
331 * This function is called when a device state changes and updates the extension state
332 * accordingly by aggregating the device states and publishing the new state.
333 */
334static void extension_state_device_state_cb(void *userdata, struct stasis_subscription *sub,
335 struct stasis_message *msg)
336{
337 struct extension_state *state = userdata;
338 struct ast_device_state_message *device_state;
339 struct ast_devstate_aggregate agg;
340 struct ast_extension_state_device_state_info *extension_device_state_info;
341 int i;
342 unsigned int updated = 0;
343 enum ast_extension_states new_device_state;
344
346 return;
347 }
348
349 device_state = stasis_message_data(msg);
350
351 /* We only care about the aggregate state */
352 if (device_state->eid) {
353 return;
354 }
355
357
358 /*
359 * Alrighty, the reason that we store an extension_device_state_info is to reduce the memory allocation that
360 * has to occur every time we get a device state update and have to construct a new message. If the extension
361 * state contains only a single device source we have to do this anyway, but if there's multiple then if we
362 * didn't store the result we'd be creating new ones every message to put in the additional_devices vector.
363 */
364 extension_device_state_info = extension_state_device_state_info_alloc(device_state->device, device_state->state);
365 if (!extension_device_state_info) {
366 return;
367 }
368
370
371 for (i = 0; i < AST_VECTOR_SIZE(&state->device_state_sources); i++) {
372 struct extension_state_device_source *source = AST_VECTOR_GET(&state->device_state_sources, i);
373
374 if (!strcmp(source->info->device, device_state->device)) {
375 ao2_replace(source->info, extension_device_state_info);
376 updated = 1;
377 }
378
379 ast_devstate_aggregate_add(&agg, source->info->state);
380 }
381
382 /* We don't really care about the device state info contents now, so we can drop the reference */
383 ao2_ref(extension_device_state_info, -1);
384
385 /*
386 * It's possible for a device state update to come in for a device which is no longer feeding this
387 * extension state if it has been updated, so only actually care about the new device state if a
388 * source has actually been updated.
389 */
390 if (!updated) {
392 return;
393 }
394
395 /*
396 * We actually update things and raise a message if the state is different, or if the state is ringing
397 * as that can actually just be an update that someone else is ringing the same extension.
398 */
400 if ((state->device_snapshot->state != new_device_state) || (new_device_state & AST_EXTENSION_RINGING)) {
401 struct ast_extension_state_device_snapshot *device_snapshot;
402 struct ast_extension_state_update_message *update_message;
403 struct stasis_message *message;
404
405 /* Now above you probably noticed I dropped the reference for extension_device_state_info but now I'm
406 * passing it in here. Don't panic - a reference exists on the device state source still and since we
407 * have the state locked it can't go away.
408 */
409 device_snapshot = extension_state_device_snapshot_create(new_device_state, &state->device_state_sources,
410 extension_device_state_info);
411 if (!device_snapshot) {
413 return;
414 }
415
416 update_message = extension_state_update_message_create(state->dialplan_context, state->dialplan_extension,
417 state->device_snapshot, device_snapshot, state->presence_snapshot, state->presence_snapshot);
418
419 /* Even if we can't publish an update message we still ensure the local cached snapshot is up to date */
420 ao2_replace(state->device_snapshot, device_snapshot);
421 ao2_ref(device_snapshot, -1);
422
423 if (!update_message) {
425 return;
426 }
427
428 /* Inform any subscribers of the change to the device snapshot */
430 if (message) {
431 stasis_publish(state->extension_state_topic, message);
432 ao2_ref(message, -1);
433 }
434
435 ao2_ref(update_message, -1);
436 }
437
439}
440
441/*!
442 * \internal
443 * \brief Allocate a device source for an extension state
444 *
445 * \param state The extension state to allocate the device source for
446 * \param device The device to allocate the source for
447 * \retval An allocated device source, or NULL on failure
448 *
449 * This function allocates a device source for an extension state by creating a device state source
450 * and setting up the necessary subscriptions and references.
451 */
453{
454 struct extension_state_device_source *source;
455 struct stasis_topic *topic;
456
457 /*
458 * Ensure that we have a direct device state topic for the device, note this is returned without a reference but
459 * is guaranteed to exist regardless.
460 */
461 topic = ast_device_state_topic(device);
462 if (!topic) {
463 return NULL;
464 }
465
466 /*
467 * The device state source is only used within the extension state and is never
468 * passed around so the overhead of an ao2 object with reference counting is unnecessary.
469 */
470 source = ast_calloc(1, sizeof(*source));
471 if (!source) {
472 return NULL;
473 }
474
476 if (!source->info) {
478 return NULL;
479 }
480
481 /*
482 * We do a synchronous subscription to the device state topic, as our callback is extremely
483 * short lived and the added overhead of queueing to a taskprocessor for another thread to handle
484 * it is just not worth it.
485 */
487 if (!source->device_state_subscription) {
489 return NULL;
490 }
491
494
495 return source;
496}
497
498/*!
499 * \internal
500 * \brief Destroy an extension state presence snapshot
501 *
502 * \param obj The extension state presence snapshot to destroy
503 *
504 * This function destroys an extension state presence snapshot by cleaning up
505 * the presence snapshot.
506 */
508{
509 struct ast_extension_state_presence_snapshot *presence_snapshot = obj;
510
511 ast_free(presence_snapshot->presence_subtype);
512 ast_free(presence_snapshot->presence_message);
513}
514
515/*!
516 * \internal
517 * \brief Create an extension state presence snapshot
518 *
519 * \param presence_state The presence state
520 * \param presence_subtype The presence subtype (can be NULL)
521 * \param presence_message The presence message (can be NULL)
522 * \retval An allocated extension state presence snapshot, or NULL on failure
523 *
524 * This function creates an extension state presence snapshot for the specified presence state,
525 * presence subtype, and presence message.
526 */
528 const char *presence_subtype, const char *presence_message)
529{
530 struct ast_extension_state_presence_snapshot *presence_snapshot;
531
532 presence_snapshot = ao2_alloc_options(sizeof(*presence_snapshot), extension_state_presence_snapshot_destroy,
534 if (!presence_snapshot) {
535 return NULL;
536 }
537
538 /* To ensure that we don't give a partial snapshot we fail creation if any allocation fails */
539 presence_snapshot->presence_state = presence_state;
540 if (presence_subtype) {
541 presence_snapshot->presence_subtype = ast_strdup(presence_subtype);
542 if (!presence_snapshot->presence_subtype) {
543 ao2_ref(presence_snapshot, -1);
544 return NULL;
545 }
546 }
547 if (presence_message) {
548 presence_snapshot->presence_message = ast_strdup(presence_message);
549 if (!presence_snapshot->presence_message) {
550 ao2_ref(presence_snapshot, -1);
551 return NULL;
552 }
553 }
554
555 return presence_snapshot;
556}
557
558/*!
559 * \brief device source non-matching version comparator for AST_VECTOR_REMOVE_CMP_UNORDERED()
560 *
561 * \param elem Element to compare against
562 * \param value Value to compare with the vector element.
563 *
564 * \return 0 if element does not match.
565 * \return Non-zero if element matches.
566 */
567#define DEVICE_SOURCE_ELEM_VERSION_CMP(elem, value) ((elem)->version != (value))
568
569/*!
570 * \internal
571 * \brief Update the sources of an extension state
572 *
573 * \param state The extension state to update
574 * \param exten The extension to update
575 * \retval 0 on success, -1 on failure
576 *
577 * This function updates the sources of an extension state by parsing the app part
578 * of the extension and updating the device and presence state sources.
579 */
581{
582 struct ast_str *str = ast_str_thread_get(&hintdevice_data, HINTDEVICE_DATA_LENGTH);
583 char *devices, *device, *presence_state_sources;
584 struct ast_devstate_aggregate agg;
585 enum ast_extension_states new_device_state;
586 unsigned int version;
587 struct ast_extension_state_device_snapshot *device_snapshot = NULL;
588 struct ast_extension_state_presence_snapshot *presence_snapshot = NULL;
589
590 ast_str_set(&str, 0, "%s", ast_get_extension_app(exten));
592
594
595 /*
596 * The format of the app part of a hint is "[device[&device]],[presence[&presence]]" so
597 * we can just find the first occurrence of ',' in order to get to the presence sources.
598 */
599 presence_state_sources = strchr(devices, ',');
600 if (presence_state_sources) {
601 *presence_state_sources++ = '\0';
602 }
603
605
607
608 /* Devices are separated by '&' */
609 while ((device = strsep(&devices, "&"))) {
610 struct extension_state_device_source *source = NULL;
611 int i;
612
613 /* Skip any device names that are empty, as we can do nothing */
614 if (ast_strlen_zero(device)) {
615 continue;
616 }
617
618 for (i = 0; i < AST_VECTOR_SIZE(&state->device_state_sources); i++) {
619 struct extension_state_device_source *existing_source = AST_VECTOR_GET(&state->device_state_sources, i);
620
621 if (!strcmp(existing_source->info->device, device)) {
622 source = existing_source;
623 break;
624 }
625 }
626 if (!source) {
628 if (!source) {
630 return -1;
631 }
632 AST_VECTOR_APPEND(&state->device_state_sources, source);
633 }
634
635 ast_devstate_aggregate_add(&agg, source->info->state);
636 source->version = version;
637 }
638
639 /* Do a pass and remove all old device sources */
640 AST_VECTOR_REMOVE_ALL_CMP_UNORDERED(&state->device_state_sources, version,
642
643 /* If device state sources exist it is what produces the device state, otherwise we use the default */
644 if (AST_VECTOR_SIZE(&state->device_state_sources)) {
646 } else {
647 new_device_state = AST_EXTENSION_UNAVAILABLE;
648 }
649
650 /* If the device state has changed, create a new snapshot */
651 if (state->device_snapshot->state != new_device_state) {
652 device_snapshot = extension_state_device_snapshot_create(new_device_state, &state->device_state_sources, NULL);
653 }
654 if (!device_snapshot) {
655 /* If there's no new device snapshot we use the old one */
656 device_snapshot = state->device_snapshot;
657 }
658
659 /* If the presence state has changed, create a new snapshot */
660 if (strcmp(S_OR(state->presence_sources_string, ""), S_OR(presence_state_sources, ""))) {
661 enum ast_presence_state presence_state = AST_PRESENCE_NOT_SET;
662 char *presence_subtype = NULL, *presence_message = NULL;
663
664 ast_free(state->presence_sources_string);
665
666 if (!ast_strlen_zero(presence_state_sources)) {
667 state->presence_sources_string = ast_strdup(presence_state_sources);
668 /* Presence state is also separated by & but only the presence state API can handle it and aggregate */
669 presence_state = ast_presence_state(presence_state_sources, &presence_subtype,
670 &presence_message);
671 } else {
672 state->presence_sources_string = NULL;
673 }
674
675 presence_snapshot = extension_state_presence_snapshot_create(presence_state, presence_subtype, presence_message);
676
677 ast_free(presence_subtype);
678 ast_free(presence_message);
679 }
680 if (!presence_snapshot) {
681 /* If there's no new presence snapshot we use the old one */
682 presence_snapshot = state->presence_snapshot;
683 }
684
685 /* If any snapshots have changed create an update message containing them */
686 if (state->device_snapshot != device_snapshot || state->presence_snapshot != presence_snapshot) {
687 struct ast_extension_state_update_message *update_message;
688
689 update_message = extension_state_update_message_create(state->dialplan_context, state->dialplan_extension, state->device_snapshot,
690 device_snapshot, state->presence_snapshot, presence_snapshot);
691 if (update_message) {
693
694 if (message) {
695 stasis_publish(state->extension_state_topic, message);
696 ao2_ref(message, -1);
697 }
698
699 ao2_ref(update_message, -1);
700 }
701
702 /* If applicable, update the snapshots on the state to their new version */
703 if (state->device_snapshot != device_snapshot) {
704 ao2_replace(state->device_snapshot, device_snapshot);
705 ao2_ref(device_snapshot, -1);
706 }
707 if (state->presence_snapshot != presence_snapshot) {
708 ao2_replace(state->presence_snapshot, presence_snapshot);
709 ao2_ref(presence_snapshot, -1);
710 }
711 }
712
714
715 return 0;
716}
717
718/*!
719 * \internal
720 * \brief Destroy an extension state
721 *
722 * \param obj The extension state to destroy
723 *
724 * This function destroys an extension state by cleaning up its resources.
725 */
726static void extension_state_destroy(void *obj)
727{
728 struct extension_state *state = obj;
729
730 ao2_cleanup(state->device_snapshot);
731 ao2_cleanup(state->presence_snapshot);
732 ao2_cleanup(state->extension_state_topic);
733
734 ast_free(state->presence_sources_string);
735}
736
737/*! \brief Stasis message type for extension state remove messages */
739
740/*!
741 * \internal
742 * \brief Create an extension state remove message
743 *
744 * \param context The context of the extension
745 * \param extension The extension to remove
746 * \retval A stasis message for the remove event, or NULL on failure
747 *
748 * This function creates an extension state remove message for the specified context and extension.
749 */
750static struct stasis_message *extension_state_remove_message_create(const char *context, const char *extension)
751{
752 size_t context_len = strlen(context) + 1;
753 size_t extension_len = strlen(extension) + 1;
754 struct ast_extension_state_remove_message *remove_message;
755 struct stasis_message *message;
756
757 remove_message = ao2_alloc_options(sizeof(*remove_message) + context_len + extension_len, NULL,
759 if (!remove_message) {
760 return NULL;
761 }
762
763 ast_copy_string(remove_message->extension, extension, extension_len); /* Safe */
764 remove_message->context = remove_message->extension + extension_len;
765 ast_copy_string(remove_message->context, context, context_len); /* Safe */
766
768 ao2_ref(remove_message, -1);
769
770 return message;
771}
772
773/*!
774 * \internal
775 * \brief Shut down an extension state
776 *
777 * \param state The extension state to shut down
778 *
779 * This function shuts down an extension state by publishing a remove message to its topic
780 * and cleaning up its resources.
781 */
783{
784 struct stasis_message *remove_message;
785
786 /*
787 * Shutting down an extension state requires us to publish to its topic so all subscribers
788 * know that it is going away. However, if the topic failed to be created then we have nothing
789 * to publish to and can just return.
790 */
791 if (!state->extension_state_topic) {
792 return;
793 }
794
795 /* Inform all subscribers that this extension state is being removed */
796 remove_message = extension_state_remove_message_create(state->dialplan_context,
797 state->dialplan_extension);
798 if (remove_message) {
799 stasis_publish(state->extension_state_topic, remove_message);
800 ao2_ref(remove_message, -1);
801 }
802
804 AST_VECTOR_FREE(&state->device_state_sources);
805
806 stasis_forward_cancel(state->extension_state_forwarder);
807}
808
809/*!
810 * \internal
811 * \brief Callback for presence state messages
812 *
813 * \param unused Unused parameter
814 * \param sub The stasis subscription
815 * \param msg The stasis message
816 *
817 * This callback is invoked when a presence state message is received. It updates
818 * the presence state of all extension states that are interested in the presence
819 * state provider and publishes an extension state update message if it has changed.
820 */
822 struct stasis_message *msg)
823{
824 struct ast_presence_state_message *presence_state;
825 struct ao2_iterator iter;
826 struct extension_state *state;
827
829 return;
830 }
831
832 presence_state = stasis_message_data(msg);
833
836 for (; (state = ao2_iterator_next(&iter)); ao2_ref(state, -1)) {
837 enum ast_presence_state presence_state_new;
838 char *presence_subtype, *presence_message;
839 struct ast_extension_state_presence_snapshot *presence_snapshot;
840 struct ast_extension_state_update_message *update_message;
841
843
844 /*
845 * We determine if this update is relevant to this extension state by seeing if the presence sources string
846 * even remotely contains the provider for this update. Worst case it's a substring and the calculated presence
847 * state is the same as before in which case we ignore it.
848 */
849 if (!state->presence_sources_string || !strcasestr(state->presence_sources_string, presence_state->provider)) {
851 continue;
852 }
853
854 /*
855 * Aggregation of presence state is done by requesting the current presence state with passing in a complete
856 * list of providers. This means that a presence state change message is just a notification to us to go and
857 * retrieve the new presence state. We don't just take it from the message itself. Since presence state is not
858 * as common as device state this is not a problem despite being inefficient in comparison to the device state
859 * implementation.
860 */
861 presence_state_new = ast_presence_state(state->presence_sources_string, &presence_subtype, &presence_message);
862 if (presence_state_new == AST_PRESENCE_INVALID) {
863 /* For the invalid case we just ignore this update */
865 continue;
866 }
867
868 if ((state->presence_snapshot->presence_state == presence_state_new) &&
869 ((!presence_subtype && !state->presence_snapshot->presence_subtype) ||
870 (presence_subtype && state->presence_snapshot->presence_subtype &&
871 !strcmp(presence_subtype, state->presence_snapshot->presence_subtype))) &&
872 ((!presence_message && !state->presence_snapshot->presence_message) ||
873 (presence_message && state->presence_snapshot->presence_message &&
874 !strcmp(presence_message, state->presence_snapshot->presence_message)))) {
875 /* No change in presence state, so ignore this update */
877 ast_free(presence_subtype);
878 ast_free(presence_message);
879 continue;
880 }
881
882 presence_snapshot = extension_state_presence_snapshot_create(presence_state_new, presence_subtype, presence_message);
883 if (!presence_snapshot) {
885 ast_free(presence_subtype);
886 ast_free(presence_message);
887 continue;
888 }
889
890 update_message = extension_state_update_message_create(state->dialplan_context,
891 state->dialplan_extension, state->device_snapshot, state->device_snapshot, state->presence_snapshot, presence_snapshot);
892 ao2_replace(state->presence_snapshot, presence_snapshot);
893 ao2_ref(presence_snapshot, -1);
894 if (update_message) {
896
897 if (message) {
898 stasis_publish(state->extension_state_topic, message);
899 ao2_ref(message, -1);
900 }
901
902 ao2_ref(update_message, -1);
903 }
904
906
907 ast_free(presence_subtype);
908 ast_free(presence_message);
909 }
912}
913
914/*!
915 * \internal
916 *
917 * \brief Allocate an extension state object
918 * \param exten The extension
919 * \param context The context
920 * \retval A pointer to the allocated extension state, or NULL on failure
921 *
922 * This function allocates an extension state object with the specified extension and context.
923 */
924static struct extension_state *extension_state_alloc(struct ast_exten *exten, struct ast_context *context)
925{
927 struct extension_state *state;
928 char *extension_state_topic_name;
929
930 snprintf(extension, sizeof(extension), "%s@%s", ast_get_extension_name(exten), ast_get_context_name(context));
931
932 /*
933 * Each individual extension state has its own lock to ensure that when updating it
934 * we do not cause problems for either the existing topic ingesting updates
935 * or any access to the extension state cached message.
936 */
937 state = ao2_alloc(sizeof(*state) + strlen(extension) + 1, extension_state_destroy);
938 if (!state) {
939 return NULL;
940 }
941
942 ast_copy_string(state->dialplan_context, ast_get_context_name(context), sizeof(state->dialplan_context));
943 ast_copy_string(state->dialplan_extension, ast_get_extension_name(exten), sizeof(state->dialplan_extension));
944 strcpy(state->extension, extension); /* Safe */
945 AST_VECTOR_INIT(&state->device_state_sources, 0);
946
947 /* These are the default if no sources are present */
949 &state->device_state_sources, NULL);
951 if (!state->device_snapshot || !state->presence_snapshot) {
952 ao2_ref(state, -1);
953 return NULL;
954 }
955
956 /*
957 * We don't actually access the contents of exten past guarantee of it being valid so we can safely
958 * store a pointer to just do pointer comparison.
959 */
960 state->hint_extension = exten;
961
962 /* Pattern match extensions don't have sources or a topic, so return early */
963 if (extension[0] == '_') {
964 return state;
965 }
966
967 /* We most likely have at least one device state source */
968 if (AST_VECTOR_INIT(&state->device_state_sources, 1)) {
969 ao2_ref(state, -1);
970 return NULL;
971 }
972
973 if (ast_asprintf(&extension_state_topic_name, "extension_state:extension/%s", extension) < 0) {
974 ao2_ref(state, -1);
975 return NULL;
976 }
977
978 state->extension_state_topic = stasis_topic_create(extension_state_topic_name);
979 ast_free(extension_state_topic_name);
980 if (!state->extension_state_topic) {
981 ao2_ref(state, -1);
982 return NULL;
983 }
984
985 state->extension_state_forwarder = stasis_forward_all(state->extension_state_topic, extension_state_topic_all);
986 if (!state->extension_state_forwarder) {
987 ao2_ref(state, -1);
988 return NULL;
989 }
990
991 return state;
992}
993
994/*!
995 * \internal
996 *
997 * \brief Get an extension state object
998 * \param chan The channel
999 * \param context The context
1000 * \param extension The extension
1001 * \retval A pointer to the extension state, or NULL on failure
1002 *
1003 * This function gets an extension state object for the specified channel, context, and extension.
1004 * If the extension state does not exist due to being from a pattern match, it will be created.
1005 */
1006static struct extension_state *extension_state_get(struct ast_channel *chan, const char *context, const char *extension)
1007{
1008 struct extension_state *state;
1009 struct ast_exten *hint_exten;
1010 struct pbx_find_info q = { .stacklen = 0 };
1011 char location[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2];
1012
1013 /* We optimistically search for the extension state using the provided context and extension */
1014 snprintf(location, sizeof(location), "%s@%s", extension, context);
1016 if (state) {
1017 return state;
1018 }
1019
1020 /*
1021 * Pattern match extensions do exist within extension state for the purposes of listing them out,
1022 * but they can't resolve down to anything else
1023 */
1024 if (extension[0] == '_') {
1025 return NULL;
1026 }
1027
1029
1030 /*
1031 * We can't use the provided context and extension as-is because an include
1032 * could have resulted in the context being different than what was provided.
1033 * To handle this we query the dialplan to find where the hint actually is.
1034 * We also need to do this to determine if this is a pattern match or an explicit
1035 * extension.
1036 */
1037 hint_exten = pbx_find_extension(chan, NULL, &q, context, extension,
1038 PRIORITY_HINT, NULL, "", E_MATCH);
1039 if (!hint_exten) {
1040 /*
1041 * The extension must ALWAYS exist in the dialplan in some capacity. It is
1042 * either in the dialplan as an explicit extension or a pattern match.
1043 */
1045 return NULL;
1046 }
1047
1048 if (ast_get_extension_name(hint_exten)[0] == '_') {
1049 /*
1050 * If this resolved down to a pattern match that means this is the first request
1051 * for this explicit extension so we need to add it to the dialplan which will create
1052 * an extension state for it. It's possible for us to conflict with another thread but
1053 * in that case the ast_add_extension call will fail and be a no-op and we will return
1054 * the extension state the other thread created.
1055 */
1059 ast_get_extension_registrar(hint_exten));
1060 }
1061
1062 /* The extension state should already exist at this point */
1063 snprintf(location, sizeof(location), "%s@%s", extension, q.foundcontext);
1065
1066 return ao2_find(extension_states, location, OBJ_SEARCH_KEY);
1067}
1068
1070{
1071 enum ast_channel_state search_state = 0; /* prevent false uninit warning */
1072 char match[AST_CHANNEL_NAME];
1073 struct ast_channel_iterator *chan_iter;
1074 struct ast_channel *chan, *channel = NULL;
1075 struct timeval chantime = {0, }; /* prevent false uninit warning */
1076
1077 switch (device_state) {
1078 case AST_DEVICE_RINGING:
1080 /* find ringing channel */
1081 search_state = AST_STATE_RINGING;
1082 break;
1083 case AST_DEVICE_BUSY:
1084 /* find busy channel */
1085 search_state = AST_STATE_BUSY;
1086 break;
1087 case AST_DEVICE_ONHOLD:
1088 case AST_DEVICE_INUSE:
1089 /* find up channel */
1090 search_state = AST_STATE_UP;
1091 break;
1092 case AST_DEVICE_UNKNOWN:
1094 case AST_DEVICE_INVALID:
1096 case AST_DEVICE_TOTAL /* not a state */:
1097 /* no channels are of interest */
1098 return NULL;
1099 }
1100
1101 /* iterate over all channels of the device */
1102 snprintf(match, sizeof(match), "%s-", device);
1103 chan_iter = ast_channel_iterator_by_name_new(match, strlen(match));
1104 for (; (chan = ast_channel_iterator_next(chan_iter)); ast_channel_unref(chan)) {
1105 ast_channel_lock(chan);
1106 /* this channel's state doesn't match */
1107 if (search_state != ast_channel_state(chan)) {
1108 ast_channel_unlock(chan);
1109 continue;
1110 }
1111 /* any non-ringing channel will fit */
1112 if (search_state != AST_STATE_RINGING) {
1113 ast_channel_unlock(chan);
1114 channel = chan;
1115 break;
1116 }
1117 /* but we need the oldest ringing channel of the device to match with undirected pickup */
1118 if (!channel) {
1119 chantime = ast_channel_creationtime(chan);
1120 ast_channel_ref(chan); /* must ref it! */
1121 channel = chan;
1122 } else if (ast_tvcmp(ast_channel_creationtime(chan), chantime) < 0) {
1123 chantime = ast_channel_creationtime(chan);
1124 ast_channel_unref(channel);
1125 ast_channel_ref(chan); /* must ref it! */
1126 channel = chan;
1127 }
1128 ast_channel_unlock(chan);
1129 }
1131
1132 return channel;
1133}
1134
1136{
1137 switch (devstate) {
1138 case AST_DEVICE_ONHOLD:
1139 return AST_EXTENSION_ONHOLD;
1140 case AST_DEVICE_BUSY:
1141 return AST_EXTENSION_BUSY;
1142 case AST_DEVICE_UNKNOWN:
1145 case AST_DEVICE_INVALID:
1149 case AST_DEVICE_RINGING:
1150 return AST_EXTENSION_RINGING;
1151 case AST_DEVICE_INUSE:
1152 return AST_EXTENSION_INUSE;
1155 case AST_DEVICE_TOTAL: /* not a device state, included for completeness */
1156 break;
1157 }
1158
1160}
1161
1163{
1164 int i;
1165
1166 for (i = 0; (i < ARRAY_LEN(extension_state_mappings)); i++) {
1169 }
1170 return "Unknown";
1171}
1172
1173/*!
1174 * \internal
1175 * \brief Handle the ExtensionStateList Manager action
1176 *
1177 * \param s The manager session
1178 * \param m The manager message
1179 * \retval 0 on success, -1 on failure
1180 *
1181 * This function handles the ExtensionStateList Manager action by returning a list of all extension states.
1182 */
1183static int action_extensionstatelist(struct mansession *s, const struct message *m)
1184{
1185 const char *action_id = astman_get_header(m, "ActionID");
1186
1188 astman_send_error(s, m, "No dialplan hints are available");
1189 return 0;
1190 }
1191
1192 astman_send_listack(s, m, "Extension Statuses will follow", "start");
1193
1196 struct ao2_iterator it_states;
1197 struct extension_state *state;
1198 int count = 0;
1199
1200 it_states = ao2_iterator_init(extension_states, 0);
1201 for (; (state = ao2_iterator_next(&it_states)); ao2_ref(state, -1)) {
1202 if (state->extension[0] == '_') {
1203 continue;
1204 }
1205
1206 ++count;
1207
1208 astman_append(s, "Event: ExtensionStatus\r\n");
1209 if (!ast_strlen_zero(action_id)) {
1210 astman_append(s, "ActionID: %s\r\n", action_id);
1211 }
1212 ao2_lock(state);
1213 astman_append(s,
1214 "Exten: %s\r\n"
1215 "Context: %s\r\n"
1216 "Hint: %s\r\n"
1217 "Status: %d\r\n"
1218 "StatusText: %s\r\n\r\n",
1219 state->dialplan_extension,
1220 state->dialplan_context,
1221 state->hint_extension ? ast_get_extension_app(state->hint_extension) : "None",
1222 state->device_snapshot->state,
1223 ast_extension_state2str(state->device_snapshot->state));
1225 }
1226 ao2_iterator_destroy(&it_states);
1227 astman_send_list_complete_start(s, m, "ExtensionStateListComplete", count);
1229 } else {
1230 astman_send_error(s, m, "No dialplan hints are available");
1231 }
1232
1234
1235 return 0;
1236}
1237
1238void pbx_extension_state_hint_set(struct ast_exten *exten, struct ast_context *context)
1239{
1241 struct extension_state *state;
1242
1243 snprintf(extension, sizeof(extension), "%s@%s", ast_get_extension_name(exten), ast_get_context_name(context));
1244
1246
1248 if (!state) {
1249 state = extension_state_alloc(exten, context);
1250 if (!state) {
1251 ast_log(LOG_WARNING, "Could not create extension state for hint '%s', it will be unavailable\n",
1252 extension);
1254 return;
1255 }
1257 }
1258
1259 state->hint_extension = exten;
1260 if (extension[0] != '_') {
1262 }
1263 ao2_ref(state, -1);
1264
1266}
1267
1268void pbx_extension_state_hint_remove(struct ast_exten *exten, struct ast_context *context)
1269{
1271 struct extension_state *state;
1272
1273 snprintf(extension, sizeof(extension), "%s@%s", ast_get_extension_name(exten), ast_get_context_name(context));
1274
1276
1278
1279 /* If this is not the latest hint extension that configured this extension state it can't remove it */
1280 if (!state || state->hint_extension != exten) {
1283 return;
1284 }
1285
1288 ao2_ref(state, -1);
1289
1291}
1292
1297
1298struct stasis_topic *ast_extension_state_topic(const char *exten, const char *context)
1299{
1300 struct extension_state *state;
1301 struct stasis_topic *topic;
1302
1303 state = extension_state_get(NULL, context, exten);
1304 if (!state) {
1305 return NULL;
1306 }
1307
1308 ao2_lock(state);
1309 topic = ao2_bump(state->extension_state_topic);
1311 ao2_ref(state, -1);
1312
1313 return topic;
1314}
1315
1316/*!
1317 * \internal
1318 * \brief Refresh the device snapshot of an extension state if it contains invalid device states
1319 *
1320 * \param state The extension state to refresh
1321 *
1322 * \pre The extension state must be locked by the caller before calling this function.
1323 *
1324 * This function checks whether any of the cached device states of the extension state are invalid
1325 * and if so attempts to retrieve the latest device state. If a valid device state is retrieved then
1326 * the source is updated and an update message is published to any subscribers.
1327 */
1329{
1330 struct ast_devstate_aggregate agg;
1331 enum ast_extension_states new_device_state;
1332 int i;
1333 int invalid = 0;
1334
1335 /* If no cached device state is invalid then there is nothing to refresh */
1336 for (i = 0; i < AST_VECTOR_SIZE(&state->device_state_sources); i++) {
1337 struct extension_state_device_source *source = AST_VECTOR_GET(&state->device_state_sources, i);
1338
1339 if (source->info->state == AST_DEVICE_INVALID) {
1340 invalid = 1;
1341 break;
1342 }
1343 }
1344 if (!invalid) {
1345 return;
1346 }
1347
1349
1350 for (i = 0; i < AST_VECTOR_SIZE(&state->device_state_sources); i++) {
1351 struct extension_state_device_source *source = AST_VECTOR_GET(&state->device_state_sources, i);
1352
1353 /*
1354 * An invalid state may be because the device state provider was not available when the
1355 * state was last queried, such as when the hint was created before the provider was
1356 * loaded. Since a caller is explicitly requesting the state we make another attempt to
1357 * retrieve it and if it has changed update it and notify any subscribers.
1358 */
1359 if (source->info->state == AST_DEVICE_INVALID) {
1360 enum ast_device_state device_state = ast_device_state(source->info->device);
1361
1362 if (device_state != source->info->state) {
1364
1365 info = extension_state_device_state_info_alloc(source->info->device, device_state);
1366 if (info) {
1367 ao2_replace(source->info, info);
1368 ao2_ref(info, -1);
1369 }
1370 }
1371 }
1372
1373 ast_devstate_aggregate_add(&agg, source->info->state);
1374 }
1375
1377 if (new_device_state != state->device_snapshot->state) {
1378 struct ast_extension_state_device_snapshot *device_snapshot;
1379 struct ast_extension_state_update_message *update_message;
1380
1381 device_snapshot = extension_state_device_snapshot_create(new_device_state, &state->device_state_sources, NULL);
1382 if (!device_snapshot) {
1383 return;
1384 }
1385
1386 update_message = extension_state_update_message_create(state->dialplan_context, state->dialplan_extension,
1387 state->device_snapshot, device_snapshot, state->presence_snapshot, state->presence_snapshot);
1388
1389 /* Even if we can't publish an update message we still ensure the local cached snapshot is up to date */
1390 ao2_replace(state->device_snapshot, device_snapshot);
1391 ao2_ref(device_snapshot, -1);
1392
1393 if (update_message) {
1395
1396 if (message) {
1397 stasis_publish(state->extension_state_topic, message);
1398 ao2_ref(message, -1);
1399 }
1400
1401 ao2_ref(update_message, -1);
1402 }
1403 }
1404}
1405
1407 const char *exten, const char *context)
1408{
1409 struct extension_state *state;
1410 struct ast_extension_state_device_snapshot *device_snapshot;
1411
1412 state = extension_state_get(chan, context, exten);
1413 if (!state) {
1414 return NULL;
1415 }
1416
1417 ao2_lock(state);
1419 device_snapshot = ao2_bump(state->device_snapshot);
1421 ao2_ref(state, -1);
1422
1423 return device_snapshot;
1424}
1425
1426/*!
1427 * \internal
1428 * \brief Refresh the presence snapshot of an extension state if it is invalid
1429 *
1430 * \param state The extension state to refresh
1431 *
1432 * \pre The extension state must be locked by the caller before calling this function.
1433 *
1434 * This function checks whether the cached presence snapshot of the extension state is invalid
1435 * and if so attempts to retrieve the latest presence state. If a valid presence state is
1436 * retrieved then the snapshot is updated and an update message is published to any subscribers.
1437 */
1439{
1440 enum ast_presence_state presence_state_new;
1441 char *presence_subtype;
1442 char *presence_message;
1443
1444 /*
1445 * If the cached presence state is invalid it may be because the presence state provider
1446 * was unable to be reached when it was last queried, or because the resulting update was
1447 * ignored. Since a caller is explicitly requesting the presence state we make another
1448 * attempt to retrieve it and if it has changed update it and notify any subscribers.
1449 */
1450 if (state->presence_snapshot->presence_state != AST_PRESENCE_INVALID || !state->presence_sources_string) {
1451 return;
1452 }
1453
1454 presence_state_new = ast_presence_state(state->presence_sources_string, &presence_subtype, &presence_message);
1455 if (presence_state_new != AST_PRESENCE_INVALID) {
1456 struct ast_extension_state_presence_snapshot *new_presence_snapshot;
1457
1458 new_presence_snapshot = extension_state_presence_snapshot_create(presence_state_new, presence_subtype,
1460 if (new_presence_snapshot) {
1461 struct ast_extension_state_update_message *update_message;
1462
1463 update_message = extension_state_update_message_create(state->dialplan_context, state->dialplan_extension,
1464 state->device_snapshot, state->device_snapshot, state->presence_snapshot, new_presence_snapshot);
1465 ao2_replace(state->presence_snapshot, new_presence_snapshot);
1467
1468 if (update_message) {
1470
1471 if (message) {
1472 stasis_publish(state->extension_state_topic, message);
1473 ao2_ref(message, -1);
1474 }
1475
1476 ao2_ref(update_message, -1);
1477 }
1478 }
1479 }
1480
1481 ast_free(presence_subtype);
1482 ast_free(presence_message);
1483}
1484
1486 const char *exten, const char *context)
1487{
1488 struct extension_state *state;
1489 struct ast_extension_state_presence_snapshot *presence_snapshot;
1490
1491 state = extension_state_get(chan, context, exten);
1492 if (!state) {
1493 return NULL;
1494 }
1495
1496 ao2_lock(state);
1498 presence_snapshot = ao2_bump(state->presence_snapshot);
1500 ao2_ref(state, -1);
1501
1502 return presence_snapshot;
1503}
1504
1505/*!
1506 * \internal
1507 * \brief CLI command to show hints
1508 *
1509 * \param e The CLI entry
1510 * \param cmd The command
1511 * \param a The CLI arguments
1512 *
1513 * This function shows all registered hints in the CLI.
1514 */
1515static char *handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1516{
1517 struct extension_state *state;
1518 int num = 0;
1519 struct ao2_iterator i;
1520
1521 switch (cmd) {
1522 case CLI_INIT:
1523 e->command = "core show hints";
1524 e->usage =
1525 "Usage: core show hints\n"
1526 " List registered hints.\n"
1527 " Hint details are shown in five columns. In order from left to right, they are:\n"
1528 " 1. Hint extension URI.\n"
1529 " 2. List of mapped device or presence state identifiers.\n"
1530 " 3. Current extension state. The aggregate of mapped device states.\n"
1531 " 4. Current presence state for the mapped presence state provider.\n"
1532 " 5. Watchers - number of subscriptions and other entities watching this hint.\n";
1533 return NULL;
1534 case CLI_GENERATE:
1535 return NULL;
1536 }
1537
1539 ast_cli(a->fd, "There are no registered dialplan hints\n");
1540 return CLI_SUCCESS;
1541 }
1542 /* ... we have hints ... */
1543 ast_cli(a->fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
1544
1546 for (; (state = ao2_iterator_next(&i)); ao2_ref(state, -1)) {
1547 ao2_lock(state);
1550 ast_cli(a->fd, "%-30.30s: %-60.60s State:%-15.15s Presence:%-15.15s Watchers %2zd\n",
1551 state->extension,
1552 state->hint_extension ? ast_get_extension_app(state->hint_extension) : "None",
1553 ast_extension_state2str(state->device_snapshot->state),
1554 ast_presence_state2str(state->presence_snapshot->presence_state),
1555 state->extension_state_topic ? stasis_topic_subscribers(state->extension_state_topic) : 0);
1557
1558 num++;
1559 }
1561
1562 ast_cli(a->fd, "----------------\n");
1563 ast_cli(a->fd, "- %d hints registered\n", num);
1564 return CLI_SUCCESS;
1565}
1566
1567/*!
1568 * \internal
1569 * \brief Complete the core show hint CLI command
1570 *
1571 * \param line The command line
1572 * \param word The word being completed
1573 * \param pos The position in the command
1574 * \param state The completion state
1575 *
1576 * This function completes the core show hint CLI command.
1577 */
1578static char *complete_core_show_hint(const char *line, const char *word, int pos, int state)
1579{
1580 struct extension_state *extstate;
1581 char *ret = NULL;
1582 int which = 0;
1583 int wordlen;
1584 struct ao2_iterator i;
1585
1586 if (pos != 3)
1587 return NULL;
1588
1589 wordlen = strlen(word);
1590
1591 /* walk through all hints */
1593 for (; (extstate = ao2_iterator_next(&i)); ao2_ref(extstate, -1)) {
1594 ao2_lock(extstate);
1595 if (!strncasecmp(word, extstate->dialplan_extension, wordlen) && ++which > state) {
1596 ret = ast_strdup(extstate->dialplan_extension);
1597 ao2_unlock(extstate);
1598 ao2_ref(extstate, -1);
1599 break;
1600 }
1601 ao2_unlock(extstate);
1602 }
1604
1605 return ret;
1606}
1607
1608/*!
1609 * \internal
1610 * \brief CLI support for listing registered dial plan hint
1611 *
1612 * \param e The CLI entry
1613 * \param cmd The command
1614 * \param a The CLI arguments
1615 *
1616 * This function handles the core show hint CLI command.
1617 */
1618static char *handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1619{
1620 struct extension_state *extstate;
1621 int num = 0, extenlen;
1622 struct ao2_iterator i;
1623
1624 switch (cmd) {
1625 case CLI_INIT:
1626 e->command = "core show hint";
1627 e->usage =
1628 "Usage: core show hint <exten>\n"
1629 " List registered hint.\n"
1630 " Hint details are shown in five columns. In order from left to right, they are:\n"
1631 " 1. Hint extension URI.\n"
1632 " 2. List of mapped device or presence state identifiers.\n"
1633 " 3. Current extension state. The aggregate of mapped device states.\n"
1634 " 4. Current presence state for the mapped presence state provider.\n"
1635 " 5. Watchers - number of subscriptions and other entities watching this hint.\n";
1636 return NULL;
1637 case CLI_GENERATE:
1638 return complete_core_show_hint(a->line, a->word, a->pos, a->n);
1639 }
1640
1641 if (a->argc < 4)
1642 return CLI_SHOWUSAGE;
1643
1645 ast_cli(a->fd, "There are no registered dialplan hints\n");
1646 return CLI_SUCCESS;
1647 }
1648
1649 extenlen = strlen(a->argv[3]);
1651 for (; (extstate = ao2_iterator_next(&i)); ao2_ref(extstate, -1)) {
1652 ao2_lock(extstate);
1653 if (!strncasecmp(extstate->extension, a->argv[3], extenlen)) {
1656 ast_cli(a->fd, "%-30.30s: %-60.60s State:%-15.15s Presence:%-15.15s Watchers %2zd\n",
1657 extstate->extension,
1658 extstate->hint_extension ? ast_get_extension_app(extstate->hint_extension) : "None",
1662 num++;
1663 }
1664 ao2_unlock(extstate);
1665 }
1667 if (!num)
1668 ast_cli(a->fd, "No hints matching extension %s\n", a->argv[3]);
1669 else
1670 ast_cli(a->fd, "%d hint%s matching extension %s\n", num, (num!=1 ? "s":""), a->argv[3]);
1671 return CLI_SUCCESS;
1672}
1673
1675 AST_CLI_DEFINE(handle_show_hints, "Show dialplan hints"),
1676 AST_CLI_DEFINE(handle_show_hint, "Show dialplan hint"),
1677};
1678
1679/*!
1680 * \internal
1681 * \brief Callback function to clean up an individual extension state.
1682 *
1683 * \param obj The extension state object
1684 * \param arg Additional argument (not used)
1685 * \param flags Flags for the callback
1686 * \return CMP_MATCH if the object was processed, 0 otherwise
1687 */
1688static int extension_state_cleanup_individual(void *obj, void *arg, int flags)
1689{
1690 struct extension_state *state = obj;
1691
1693
1694 return CMP_MATCH;
1695}
1696
1697/*!
1698 * \internal
1699 * \brief Clean up the extension state subsystem, called at shutdown.
1700 */
1713
1715{
1717 return -1;
1718 }
1719
1721 return -1;
1722 }
1723
1724 /* Initialize extension state subsystem */
1726 extension_state_sort_fn, extension_state_cmp_fn);
1727 if (!extension_states) {
1728 ast_log(LOG_ERROR, "Failed to allocate new states container\n");
1729 return -1;
1730 }
1731
1732 extension_state_topic_all = stasis_topic_create("extension_state:all");
1734 ast_log(LOG_ERROR, "Failed to create extension state topic\n");
1735 return -1;
1736 }
1737
1739 if (!presence_state_sub) {
1740 ast_log(LOG_ERROR, "Failed to create subscription to receive presence state updates\n");
1741 return -1;
1742 }
1745
1749
1750 return 0;
1751}
Prototypes for public functions only of internal interest,.
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
const char * str
Definition app_jack.c:150
char * strsep(char **str, const char *delims)
char * strcasestr(const char *, const char *)
Asterisk main include file. File version handling, generic pbx functions.
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition clicompat.c:19
#define ast_free(a)
Definition astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
void ast_free_ptr(void *ptr)
free() wrapper
Definition astmm.c:1739
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition astmm.h:267
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
#define ast_log
Definition astobj2.c:42
#define ao2_iterator_next(iter)
Definition astobj2.h:1911
@ CMP_MATCH
Definition astobj2.h:1027
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition astobj2.h:367
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition astobj2.h:363
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition astobj2.h:1693
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define AO2_STRING_FIELD_CMP_FN(stype, field)
Creates a compare function for a structure string field.
Definition astobj2.h:2048
#define ao2_cleanup(obj)
Definition astobj2.h:1934
#define ao2_unlink_flags(container, obj, flags)
Remove an object from a container.
Definition astobj2.h:1600
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
Definition astobj2.h:1554
#define ao2_find(container, arg, flags)
Definition astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define AO2_STRING_FIELD_SORT_FN(stype, field)
Creates a sort function for a structure string field.
Definition astobj2.h:2064
#define ao2_container_alloc_rbtree(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a red-black tree container.
Definition astobj2.h:1349
#define ao2_unlock(a)
Definition astobj2.h:729
#define ao2_replace(dst, src)
Replace one object reference with another cleaning up the original.
Definition astobj2.h:501
#define ao2_lock(a)
Definition astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition astobj2.h:459
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition astobj2.h:404
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition astobj2.h:480
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_NOLOCK
Assume that the ao2_container is already locked.
Definition astobj2.h:1063
@ OBJ_NODATA
Definition astobj2.h:1044
@ OBJ_MULTIPLE
Definition astobj2.h:1049
@ OBJ_UNLINK
Definition astobj2.h:1039
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition astobj2.h:1101
#define ao2_alloc(data_size, destructor_fn)
Definition astobj2.h:409
static int match(struct ast_sockaddr *addr, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur, int check_dcallno)
Definition chan_iax2.c:2401
static char version[AST_MAX_EXTENSION]
struct ast_channel_iterator * ast_channel_iterator_by_name_new(const char *name, size_t name_len)
Create a new channel iterator based on name.
Definition channel.c:1368
struct ast_channel_iterator * ast_channel_iterator_destroy(struct ast_channel_iterator *i)
Destroy a channel iterator.
Definition channel.c:1349
#define ast_channel_lock(chan)
Definition channel.h:2983
struct ast_channel * ast_channel_iterator_next(struct ast_channel_iterator *i)
Get the next channel for a channel iterator.
Definition channel.c:1388
#define ast_channel_ref(c)
Increase channel reference count.
Definition channel.h:3008
struct timeval ast_channel_creationtime(struct ast_channel *chan)
#define AST_CHANNEL_NAME
Definition channel.h:173
#define ast_channel_unref(c)
Decrease channel reference count.
Definition channel.h:3019
#define AST_MAX_CONTEXT
Definition channel.h:135
#define ast_channel_unlock(chan)
Definition channel.h:2984
#define AST_MAX_EXTENSION
Definition channel.h:134
ast_channel_state
ast_channel states
@ AST_STATE_RINGING
@ AST_STATE_BUSY
@ AST_STATE_UP
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition cli.h:45
#define CLI_SUCCESS
Definition cli.h:44
#define AST_CLI_DEFINE(fn, txt,...)
Definition cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition clicompat.c:6
@ CLI_INIT
Definition cli.h:152
@ CLI_GENERATE
Definition cli.h:153
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition cli.h:265
short word
struct stasis_message_type * ast_device_state_message_type(void)
Get the Stasis message type for device state messages.
void ast_devstate_aggregate_add(struct ast_devstate_aggregate *agg, enum ast_device_state state)
Add a device state to the aggregate device state.
void ast_devstate_aggregate_init(struct ast_devstate_aggregate *agg)
Initialize aggregate device state.
enum ast_device_state ast_devstate_aggregate_result(struct ast_devstate_aggregate *agg)
Get the aggregate device state result.
struct stasis_topic * ast_device_state_topic(const char *device)
Get the Stasis topic for device state messages for a specific device.
ast_device_state
Device States.
Definition devicestate.h:52
@ AST_DEVICE_RINGINUSE
Definition devicestate.h:60
@ AST_DEVICE_INUSE
Definition devicestate.h:55
@ AST_DEVICE_UNKNOWN
Definition devicestate.h:53
@ AST_DEVICE_ONHOLD
Definition devicestate.h:61
@ AST_DEVICE_RINGING
Definition devicestate.h:59
@ AST_DEVICE_INVALID
Definition devicestate.h:57
@ AST_DEVICE_BUSY
Definition devicestate.h:56
@ AST_DEVICE_NOT_INUSE
Definition devicestate.h:54
@ AST_DEVICE_TOTAL
Definition devicestate.h:62
@ AST_DEVICE_UNAVAILABLE
Definition devicestate.h:58
@ E_MATCH
Definition extconf.h:217
static void extension_state_destroy(void *obj)
static struct stasis_topic * extension_state_topic_all
Topic which receives all extension state updates.
static void extension_state_device_source_destroy(struct extension_state_device_source *source)
const char * ast_extension_state2str(int extension_state)
Return string representation of the state of an extension.
static struct ast_cli_entry extension_state_cli[]
static void extension_state_update_message_destroy(void *obj)
static int action_extensionstatelist(struct mansession *s, const struct message *m)
static struct stasis_subscription * presence_state_sub
Single presence state subscription, for all extension states.
static void extension_state_device_snapshot_destroy(void *obj)
static int extension_state_update_sources(struct extension_state *state, struct ast_exten *exten)
static void extension_state_presence_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
static void extension_state_device_state_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
enum ast_extension_states ast_devstate_to_extenstate(enum ast_device_state devstate)
Map devstate to an extension state.
static const struct cfextension_states extension_state_mappings[]
static char * complete_core_show_hint(const char *line, const char *word, int pos, int state)
void pbx_extension_state_hint_set(struct ast_exten *exten, struct ast_context *context)
struct ast_extension_state_device_snapshot * ast_extension_state_get_latest_device_snapshot(struct ast_channel *chan, const char *exten, const char *context)
Get the latest device state message for an extension.
static char * handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static struct ast_extension_state_update_message * extension_state_update_message_create(const char *context, const char *extension, struct ast_extension_state_device_snapshot *old_device_snapshot, struct ast_extension_state_device_snapshot *new_device_snapshot, struct ast_extension_state_presence_snapshot *old_presence_snapshot, struct ast_extension_state_presence_snapshot *new_presence_snapshot)
int ast_extension_state_init(void)
static struct extension_state_device_source * extension_state_device_source_alloc(struct extension_state *state, const char *device)
#define HINTDEVICE_DATA_LENGTH
#define DEVICE_SOURCE_ELEM_VERSION_CMP(elem, value)
device source non-matching version comparator for AST_VECTOR_REMOVE_CMP_UNORDERED()
static struct extension_state * extension_state_alloc(struct ast_exten *exten, struct ast_context *context)
struct ast_extension_state_presence_snapshot * ast_extension_state_get_latest_presence_snapshot(struct ast_channel *chan, const char *exten, const char *context)
Get the latest presence state message for an extension.
static char * handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static struct extension_state * extension_state_get(struct ast_channel *chan, const char *context, const char *extension)
static struct ast_extension_state_presence_snapshot * extension_state_presence_snapshot_create(enum ast_presence_state presence_state, const char *presence_subtype, const char *presence_message)
static void extension_state_cleanup(void)
static void extension_state_device_snapshot_refresh(struct extension_state *state)
static void extension_state_presence_snapshot_refresh(struct extension_state *state)
static void extension_state_shutdown(struct extension_state *state)
static struct stasis_message * extension_state_remove_message_create(const char *context, const char *extension)
static void extension_state_presence_snapshot_destroy(void *obj)
struct stasis_topic * ast_extension_state_topic(const char *exten, const char *context)
Get the Stasis topic to receive extension state messages for a specific extension.
struct stasis_topic * ast_extension_state_topic_all(void)
Get the Stasis topic to receive all extension state messages.
static struct ast_extension_state_device_snapshot * extension_state_device_snapshot_create(enum ast_extension_states device_state, struct device_state_sources_vector *device_state_sources, struct ast_extension_state_device_state_info *causing_device)
static struct ast_extension_state_device_state_info * extension_state_device_state_info_alloc(const char *device, enum ast_device_state state)
static int extension_state_cleanup_individual(void *obj, void *arg, int flags)
struct ast_channel * ast_extension_state_get_device_causing_channel(const char *device, enum ast_device_state device_state)
Get the channel that is causing the device to be in the given state, if any.
void pbx_extension_state_hint_remove(struct ast_exten *exten, struct ast_context *context)
static struct ao2_container * extension_states
The global container of extension states.
struct stasis_message_type * ast_extension_state_update_message_type(void)
Get extension state update message type.
struct stasis_message_type * ast_extension_state_remove_message_type(void)
Get extension state remove message type.
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
Definition manager.c:1963
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition manager.c:1921
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
Definition manager.c:1999
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition manager.c:1582
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition manager.c:2007
void astman_append(struct mansession *s, const char *fmt,...)
Definition manager.c:1842
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition manager.c:7792
#define LOG_ERROR
#define LOG_WARNING
#define EVENT_FLAG_REPORTING
Definition manager.h:84
#define ast_manager_register_xml_core(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition manager.h:204
#define EVENT_FLAG_CALL
Definition manager.h:76
Asterisk module definitions.
Core PBX routines and definitions.
void * ast_get_extension_app_data(struct ast_exten *e)
Definition pbx.c:6720
int ast_add_extension(const char *context, int replace, const char *extension, int priority, const char *label, const char *callerid, const char *application, void *data, void(*datad)(void *), const char *registrar)
Add and extension to an extension context.
Definition pbx.c:5206
int ast_wrlock_contexts(void)
Write locks the context list.
Definition pbx.c:6621
int ast_get_extension_matchcid(struct ast_exten *e)
Definition pbx.c:6705
const char * ast_get_extension_app(struct ast_exten *e)
Definition pbx.c:6715
const char * ast_get_extension_cidmatch(struct ast_exten *e)
Definition pbx.c:6710
const char * ast_get_extension_label(struct ast_exten *e)
Definition pbx.c:6672
ast_extension_states
Extension states.
Definition pbx.h:61
@ AST_EXTENSION_RINGING
Definition pbx.h:68
@ AST_EXTENSION_NOT_INUSE
Definition pbx.h:64
@ AST_EXTENSION_INUSE
Definition pbx.h:65
@ AST_EXTENSION_UNAVAILABLE
Definition pbx.h:67
@ AST_EXTENSION_ONHOLD
Definition pbx.h:69
@ AST_EXTENSION_BUSY
Definition pbx.h:66
const char * ast_get_context_name(struct ast_context *con)
Definition ael_main.c:421
#define PRIORITY_HINT
Definition pbx.h:54
struct ast_exten * pbx_find_extension(struct ast_channel *chan, struct ast_context *bypass, struct pbx_find_info *q, const char *context, const char *exten, int priority, const char *label, const char *callerid, enum ext_match_t action)
Definition ael_main.c:152
int ast_unlock_contexts(void)
Unlocks contexts.
Definition pbx.c:6631
const char * ast_get_extension_name(struct ast_exten *exten)
Definition pbx.c:6667
const char * ast_get_extension_registrar(struct ast_exten *e)
Definition pbx.c:6690
Private include file for pbx.
struct stasis_topic * ast_presence_state_topic_all(void)
Get presence state topic.
ast_presence_state
@ AST_PRESENCE_INVALID
@ AST_PRESENCE_NOT_SET
const char * ast_presence_state2str(enum ast_presence_state state)
Convert presence state to text string for output.
struct stasis_message_type * ast_presence_state_message_type(void)
Get presence state message type.
static struct stasis_subscription * sub
Statsd channel stats. Exmaple of how to subscribe to Stasis events.
#define NULL
Definition resample.c:96
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
Definition stasis.h:1546
@ STASIS_SUBSCRIPTION_FILTER_SELECTIVE
Definition stasis.h:297
struct stasis_forward * stasis_forward_cancel(struct stasis_forward *forward)
Definition stasis.c:1626
struct stasis_topic * stasis_topic_create(const char *name)
Create a new topic.
Definition stasis.c:684
#define stasis_subscribe_synchronous(topic, callback, data)
Definition stasis.h:711
size_t stasis_topic_subscribers(const struct stasis_topic *topic)
Return the number of subscribers of a topic.
Definition stasis.c:710
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
Definition stasis.c:1101
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
Definition stasis.c:1155
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
Definition stasis.h:1524
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Definition stasis.c:1212
#define STASIS_MESSAGE_TYPE_DEFN(name,...)
Boiler-plate messaging macro for defining public message types.
Definition stasis.h:1471
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition stasis.c:1049
struct stasis_forward * stasis_forward_all(struct stasis_topic *from_topic, struct stasis_topic *to_topic)
Create a subscription which forwards all messages from one topic to another.
Definition stasis.c:1656
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition stasis.c:1589
#define stasis_subscribe(topic, callback, data)
Definition stasis.h:649
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition strings.h:80
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition strings.h:1113
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition strings.h:909
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition astobj2.h:1821
Main Channel structure associated with a channel.
descriptor for a cli entry.
Definition cli.h:171
char * command
Definition cli.h:186
const char * usage
Definition cli.h:177
ast_context: An extension context
Definition pbx.c:254
The structure that contains device state.
enum ast_device_state state
const struct ast_eid * eid
The EID of the server where this message originated.
You shouldn't care about the contents of this struct.
ast_exten: An extension The dialplan is saved as a linked list with each context having it's own link...
Definition pbx.c:207
Device snapshot for an extension state.
struct ast_extension_state_device_snapshot::@232 additional_devices
Vector of additional device states that contributed to update.
struct ast_extension_state_device_state_info * causing_device
The device that caused this update.
enum ast_extension_states state
The state of the extension.
Individual device states that contributed to snapshot.
enum ast_device_state state
The state of the device.
char device[0]
The name of the device.
Presence snapshot for an extension state.
char * presence_subtype
The subtype of the presence state.
char * presence_message
An optional message for the presence.
enum ast_presence_state presence_state
The presence state of the extension.
Stasis message for extension state removal message.
char * context
The dialplan context.
char extension[0]
The dialplan extension.
Stasis message for extension state update message.
char * context
The dialplan context.
struct ast_extension_state_presence_snapshot * old_presence_snapshot
The old presence snapshot.
char extension[0]
The dialplan extension.
struct ast_extension_state_device_snapshot * old_device_snapshot
The old device snapshot.
struct ast_extension_state_presence_snapshot * new_presence_snapshot
The new presence snapshot - will be pointer equivalent to old if unchanged.
struct ast_extension_state_device_snapshot * new_device_snapshot
The new device snapshot - will be pointer equivalent to old if unchanged.
Stasis message payload representing a presence state update.
Support for dynamic strings.
Definition strings.h:623
const char *const text
Device state source feeding an extension state.
struct stasis_subscription * device_state_subscription
Synchronous subscription to the device state topic.
unsigned int version
The current version for this source.
struct ast_extension_state_device_state_info * info
The current state of the device - this is immutable.
Extension state information.
struct ast_extension_state_device_snapshot * device_snapshot
The current device snapshot for the extension.
struct stasis_forward * extension_state_forwarder
Forwarder from per-extension topic to all topic.
struct device_state_sources_vector device_state_sources
Device state sources feeding the hint topic, and their forwarding.
char * presence_sources_string
The string representation of all presence state sources feeding this extension state.
char extension[0]
The combined extension this state is for (extension@context)
char dialplan_extension[AST_MAX_EXTENSION]
The dialplan extension.
struct stasis_topic * extension_state_topic
The extension state topic for this extension.
char dialplan_context[AST_MAX_CONTEXT]
The dialplan context.
struct ast_extension_state_presence_snapshot * presence_snapshot
The current presence snapshot for the extension.
struct ast_exten * hint_extension
The dialplan hint that last configured this extension state.
structure to hold extensions
In case you didn't read that giant block of text above the mansession_session struct,...
Definition manager.c:314
const char * foundcontext
Definition extconf.h:241
Forwarding information.
Definition stasis.c:1609
static struct test_val a
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
int ast_tvcmp(struct timeval _a, struct timeval _b)
Compress two struct timeval instances returning -1, 0, 1 if the first arg is smaller,...
Definition time.h:137
long int ast_random(void)
Definition utils.c:2346
#define ARRAY_LEN(a)
Definition utils.h:706
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition vector.h:637
#define AST_VECTOR_REMOVE_ALL_CMP_UNORDERED(vec, value, cmp, cleanup)
Remove all elements from a vector that matches the given comparison.
Definition vector.h:489
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition vector.h:185
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition vector.h:124
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition vector.h:267
#define AST_VECTOR_CALLBACK_VOID(vec, callback,...)
Execute a callback on every element in a vector disregarding callback return.
Definition vector.h:890
#define AST_VECTOR(name, type)
Define a vector structure.
Definition vector.h:44
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition vector.h:708