Asterisk - The Open Source Telephony Project GIT-master-6144b6b
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 ((!state->presence_sources_string && presence_state_sources) ||
661 (state->presence_sources_string && strcmp(state->presence_sources_string, presence_state_sources))) {
662 enum ast_presence_state presence_state = AST_PRESENCE_NOT_SET;
663 char *presence_subtype = NULL, *presence_message = NULL;
664
665 ast_free(state->presence_sources_string);
666
667 if (!ast_strlen_zero(presence_state_sources)) {
668 state->presence_sources_string = ast_strdup(presence_state_sources);
669 /* Presence state is also separated by & but only the presence state API can handle it and aggregate */
670 presence_state = ast_presence_state(presence_state_sources, &presence_subtype,
671 &presence_message);
672 } else {
673 state->presence_sources_string = NULL;
674 }
675
676 presence_snapshot = extension_state_presence_snapshot_create(presence_state, presence_subtype, presence_message);
677
678 ast_free(presence_subtype);
679 ast_free(presence_message);
680 }
681 if (!presence_snapshot) {
682 /* If there's no new presence snapshot we use the old one */
683 presence_snapshot = state->presence_snapshot;
684 }
685
686 /* If any snapshots have changed create an update message containing them */
687 if (state->device_snapshot != device_snapshot || state->presence_snapshot != presence_snapshot) {
688 struct ast_extension_state_update_message *update_message;
689
690 update_message = extension_state_update_message_create(state->dialplan_context, state->dialplan_extension, state->device_snapshot,
691 device_snapshot, state->presence_snapshot, presence_snapshot);
692 if (update_message) {
694
695 if (message) {
696 stasis_publish(state->extension_state_topic, message);
697 ao2_ref(message, -1);
698 }
699
700 ao2_ref(update_message, -1);
701 }
702
703 /* If applicable, update the snapshots on the state to their new version */
704 if (state->device_snapshot != device_snapshot) {
705 ao2_replace(state->device_snapshot, device_snapshot);
706 ao2_ref(device_snapshot, -1);
707 }
708 if (state->presence_snapshot != presence_snapshot) {
709 ao2_replace(state->presence_snapshot, presence_snapshot);
710 ao2_ref(presence_snapshot, -1);
711 }
712 }
713
715
716 return 0;
717}
718
719/*!
720 * \internal
721 * \brief Destroy an extension state
722 *
723 * \param obj The extension state to destroy
724 *
725 * This function destroys an extension state by cleaning up its resources.
726 */
727static void extension_state_destroy(void *obj)
728{
729 struct extension_state *state = obj;
730
731 ao2_cleanup(state->device_snapshot);
732 ao2_cleanup(state->presence_snapshot);
733 ao2_cleanup(state->extension_state_topic);
734
735 ast_free(state->presence_sources_string);
736}
737
738/*! \brief Stasis message type for extension state remove messages */
740
741/*!
742 * \internal
743 * \brief Create an extension state remove message
744 *
745 * \param context The context of the extension
746 * \param extension The extension to remove
747 * \retval A stasis message for the remove event, or NULL on failure
748 *
749 * This function creates an extension state remove message for the specified context and extension.
750 */
751static struct stasis_message *extension_state_remove_message_create(const char *context, const char *extension)
752{
753 size_t context_len = strlen(context) + 1;
754 size_t extension_len = strlen(extension) + 1;
755 struct ast_extension_state_remove_message *remove_message;
756 struct stasis_message *message;
757
758 remove_message = ao2_alloc_options(sizeof(*remove_message) + context_len + extension_len, NULL,
760 if (!remove_message) {
761 return NULL;
762 }
763
764 ast_copy_string(remove_message->extension, extension, extension_len); /* Safe */
765 remove_message->context = remove_message->extension + extension_len;
766 ast_copy_string(remove_message->context, context, context_len); /* Safe */
767
769 ao2_ref(remove_message, -1);
770
771 return message;
772}
773
774/*!
775 * \internal
776 * \brief Shut down an extension state
777 *
778 * \param state The extension state to shut down
779 *
780 * This function shuts down an extension state by publishing a remove message to its topic
781 * and cleaning up its resources.
782 */
784{
785 struct stasis_message *remove_message;
786
787 /*
788 * Shutting down an extension state requires us to publish to its topic so all subscribers
789 * know that it is going away. However, if the topic failed to be created then we have nothing
790 * to publish to and can just return.
791 */
792 if (!state->extension_state_topic) {
793 return;
794 }
795
796 /* Inform all subscribers that this extension state is being removed */
797 remove_message = extension_state_remove_message_create(state->dialplan_context,
798 state->dialplan_extension);
799 if (remove_message) {
800 stasis_publish(state->extension_state_topic, remove_message);
801 ao2_ref(remove_message, -1);
802 }
803
805 AST_VECTOR_FREE(&state->device_state_sources);
806
807 stasis_forward_cancel(state->extension_state_forwarder);
808}
809
810/*!
811 * \internal
812 * \brief Callback for presence state messages
813 *
814 * \param unused Unused parameter
815 * \param sub The stasis subscription
816 * \param msg The stasis message
817 *
818 * This callback is invoked when a presence state message is received. It updates
819 * the presence state of all extension states that are interested in the presence
820 * state provider and publishes an extension state update message if it has changed.
821 */
823 struct stasis_message *msg)
824{
825 struct ast_presence_state_message *presence_state;
826 struct ao2_iterator iter;
827 struct extension_state *state;
828
830 return;
831 }
832
833 presence_state = stasis_message_data(msg);
834
837 for (; (state = ao2_iterator_next(&iter)); ao2_ref(state, -1)) {
838 enum ast_presence_state presence_state_new;
839 char *presence_subtype, *presence_message;
840 struct ast_extension_state_presence_snapshot *presence_snapshot;
841 struct ast_extension_state_update_message *update_message;
842
844
845 /*
846 * We determine if this update is relevant to this extension state by seeing if the presence sources string
847 * even remotely contains the provider for this update. Worst case it's a substring and the calculated presence
848 * state is the same as before in which case we ignore it.
849 */
850 if (!state->presence_sources_string || !strcasestr(state->presence_sources_string, presence_state->provider)) {
852 continue;
853 }
854
855 /*
856 * Aggregation of presence state is done by requesting the current presence state with passing in a complete
857 * list of providers. This means that a presence state change message is just a notification to us to go and
858 * retrieve the new presence state. We don't just take it from the message itself. Since presence state is not
859 * as common as device state this is not a problem despite being inefficient in comparison to the device state
860 * implementation.
861 */
862 presence_state_new = ast_presence_state(state->presence_sources_string, &presence_subtype, &presence_message);
863 if (presence_state_new == AST_PRESENCE_INVALID) {
864 /* For the invalid case we just ignore this update */
866 continue;
867 }
868
869 if ((state->presence_snapshot->presence_state == presence_state_new) &&
870 ((!presence_subtype && !state->presence_snapshot->presence_subtype) ||
871 (presence_subtype && state->presence_snapshot->presence_subtype &&
872 !strcmp(presence_subtype, state->presence_snapshot->presence_subtype))) &&
873 ((!presence_message && !state->presence_snapshot->presence_message) ||
874 (presence_message && state->presence_snapshot->presence_message &&
875 !strcmp(presence_message, state->presence_snapshot->presence_message)))) {
876 /* No change in presence state, so ignore this update */
878 ast_free(presence_subtype);
879 ast_free(presence_message);
880 continue;
881 }
882
883 presence_snapshot = extension_state_presence_snapshot_create(presence_state_new, presence_subtype, presence_message);
884 if (!presence_snapshot) {
886 ast_free(presence_subtype);
887 ast_free(presence_message);
888 continue;
889 }
890
891 update_message = extension_state_update_message_create(state->dialplan_context,
892 state->dialplan_extension, state->device_snapshot, state->device_snapshot, state->presence_snapshot, presence_snapshot);
893 ao2_replace(state->presence_snapshot, presence_snapshot);
894 ao2_ref(presence_snapshot, -1);
895 if (update_message) {
897
898 if (message) {
899 stasis_publish(state->extension_state_topic, message);
900 ao2_ref(message, -1);
901 }
902
903 ao2_ref(update_message, -1);
904 }
905
907
908 ast_free(presence_subtype);
909 ast_free(presence_message);
910 }
913}
914
915/*!
916 * \internal
917 *
918 * \brief Allocate an extension state object
919 * \param exten The extension
920 * \param context The context
921 * \retval A pointer to the allocated extension state, or NULL on failure
922 *
923 * This function allocates an extension state object with the specified extension and context.
924 */
925static struct extension_state *extension_state_alloc(struct ast_exten *exten, struct ast_context *context)
926{
928 struct extension_state *state;
929 char *extension_state_topic_name;
930
931 snprintf(extension, sizeof(extension), "%s@%s", ast_get_extension_name(exten), ast_get_context_name(context));
932
933 /*
934 * Each individual extension state has its own lock to ensure that when updating it
935 * we do not cause problems for either the existing topic ingesting updates
936 * or any access to the extension state cached message.
937 */
938 state = ao2_alloc(sizeof(*state) + strlen(extension) + 1, extension_state_destroy);
939 if (!state) {
940 return NULL;
941 }
942
943 ast_copy_string(state->dialplan_context, ast_get_context_name(context), sizeof(state->dialplan_context));
944 ast_copy_string(state->dialplan_extension, ast_get_extension_name(exten), sizeof(state->dialplan_extension));
945 strcpy(state->extension, extension); /* Safe */
946 AST_VECTOR_INIT(&state->device_state_sources, 0);
947
948 /* These are the default if no sources are present */
950 &state->device_state_sources, NULL);
952 if (!state->device_snapshot || !state->presence_snapshot) {
953 ao2_ref(state, -1);
954 return NULL;
955 }
956
957 /*
958 * We don't actually access the contents of exten past guarantee of it being valid so we can safely
959 * store a pointer to just do pointer comparison.
960 */
961 state->hint_extension = exten;
962
963 /* Pattern match extensions don't have sources or a topic, so return early */
964 if (extension[0] == '_') {
965 return state;
966 }
967
968 /* We most likely have at least one device state source */
969 if (AST_VECTOR_INIT(&state->device_state_sources, 1)) {
970 ao2_ref(state, -1);
971 return NULL;
972 }
973
974 if (ast_asprintf(&extension_state_topic_name, "extension_state:extension/%s", extension) < 0) {
975 ao2_ref(state, -1);
976 return NULL;
977 }
978
979 state->extension_state_topic = stasis_topic_create(extension_state_topic_name);
980 ast_free(extension_state_topic_name);
981 if (!state->extension_state_topic) {
982 ao2_ref(state, -1);
983 return NULL;
984 }
985
986 state->extension_state_forwarder = stasis_forward_all(state->extension_state_topic, extension_state_topic_all);
987 if (!state->extension_state_forwarder) {
988 ao2_ref(state, -1);
989 return NULL;
990 }
991
992 return state;
993}
994
995/*!
996 * \internal
997 *
998 * \brief Get an extension state object
999 * \param chan The channel
1000 * \param context The context
1001 * \param extension The extension
1002 * \retval A pointer to the extension state, or NULL on failure
1003 *
1004 * This function gets an extension state object for the specified channel, context, and extension.
1005 * If the extension state does not exist due to being from a pattern match, it will be created.
1006 */
1007static struct extension_state *extension_state_get(struct ast_channel *chan, const char *context, const char *extension)
1008{
1009 struct extension_state *state;
1010 struct ast_exten *hint_exten;
1011 struct pbx_find_info q = { .stacklen = 0 };
1012 char location[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2];
1013
1014 /* We optimistically search for the extension state using the provided context and extension */
1015 snprintf(location, sizeof(location), "%s@%s", extension, context);
1017 if (state) {
1018 return state;
1019 }
1020
1021 /*
1022 * Pattern match extensions do exist within extension state for the purposes of listing them out,
1023 * but they can't resolve down to anything else
1024 */
1025 if (extension[0] == '_') {
1026 return NULL;
1027 }
1028
1030
1031 /*
1032 * We can't use the provided context and extension as-is because an include
1033 * could have resulted in the context being different than what was provided.
1034 * To handle this we query the dialplan to find where the hint actually is.
1035 * We also need to do this to determine if this is a pattern match or an explicit
1036 * extension.
1037 */
1038 hint_exten = pbx_find_extension(chan, NULL, &q, context, extension,
1039 PRIORITY_HINT, NULL, "", E_MATCH);
1040 if (!hint_exten) {
1041 /*
1042 * The extension must ALWAYS exist in the dialplan in some capacity. It is
1043 * either in the dialplan as an explicit extension or a pattern match.
1044 */
1046 return NULL;
1047 }
1048
1049 if (ast_get_extension_name(hint_exten)[0] == '_') {
1050 /*
1051 * If this resolved down to a pattern match that means this is the first request
1052 * for this explicit extension so we need to add it to the dialplan which will create
1053 * an extension state for it. It's possible for us to conflict with another thread but
1054 * in that case the ast_add_extension call will fail and be a no-op and we will return
1055 * the extension state the other thread created.
1056 */
1060 ast_get_extension_registrar(hint_exten));
1061 }
1062
1063 /* The extension state should already exist at this point */
1064 snprintf(location, sizeof(location), "%s@%s", extension, q.foundcontext);
1066
1067 return ao2_find(extension_states, location, OBJ_SEARCH_KEY);
1068}
1069
1071{
1072 enum ast_channel_state search_state = 0; /* prevent false uninit warning */
1073 char match[AST_CHANNEL_NAME];
1074 struct ast_channel_iterator *chan_iter;
1075 struct ast_channel *chan, *channel = NULL;
1076 struct timeval chantime = {0, }; /* prevent false uninit warning */
1077
1078 switch (device_state) {
1079 case AST_DEVICE_RINGING:
1081 /* find ringing channel */
1082 search_state = AST_STATE_RINGING;
1083 break;
1084 case AST_DEVICE_BUSY:
1085 /* find busy channel */
1086 search_state = AST_STATE_BUSY;
1087 break;
1088 case AST_DEVICE_ONHOLD:
1089 case AST_DEVICE_INUSE:
1090 /* find up channel */
1091 search_state = AST_STATE_UP;
1092 break;
1093 case AST_DEVICE_UNKNOWN:
1095 case AST_DEVICE_INVALID:
1097 case AST_DEVICE_TOTAL /* not a state */:
1098 /* no channels are of interest */
1099 return NULL;
1100 }
1101
1102 /* iterate over all channels of the device */
1103 snprintf(match, sizeof(match), "%s-", device);
1104 chan_iter = ast_channel_iterator_by_name_new(match, strlen(match));
1105 for (; (chan = ast_channel_iterator_next(chan_iter)); ast_channel_unref(chan)) {
1106 ast_channel_lock(chan);
1107 /* this channel's state doesn't match */
1108 if (search_state != ast_channel_state(chan)) {
1109 ast_channel_unlock(chan);
1110 continue;
1111 }
1112 /* any non-ringing channel will fit */
1113 if (search_state != AST_STATE_RINGING) {
1114 ast_channel_unlock(chan);
1115 channel = chan;
1116 break;
1117 }
1118 /* but we need the oldest ringing channel of the device to match with undirected pickup */
1119 if (!channel) {
1120 chantime = ast_channel_creationtime(chan);
1121 ast_channel_ref(chan); /* must ref it! */
1122 channel = chan;
1123 } else if (ast_tvcmp(ast_channel_creationtime(chan), chantime) < 0) {
1124 chantime = ast_channel_creationtime(chan);
1125 ast_channel_unref(channel);
1126 ast_channel_ref(chan); /* must ref it! */
1127 channel = chan;
1128 }
1129 ast_channel_unlock(chan);
1130 }
1132
1133 return channel;
1134}
1135
1137{
1138 switch (devstate) {
1139 case AST_DEVICE_ONHOLD:
1140 return AST_EXTENSION_ONHOLD;
1141 case AST_DEVICE_BUSY:
1142 return AST_EXTENSION_BUSY;
1143 case AST_DEVICE_UNKNOWN:
1146 case AST_DEVICE_INVALID:
1150 case AST_DEVICE_RINGING:
1151 return AST_EXTENSION_RINGING;
1152 case AST_DEVICE_INUSE:
1153 return AST_EXTENSION_INUSE;
1156 case AST_DEVICE_TOTAL: /* not a device state, included for completeness */
1157 break;
1158 }
1159
1161}
1162
1164{
1165 int i;
1166
1167 for (i = 0; (i < ARRAY_LEN(extension_state_mappings)); i++) {
1170 }
1171 return "Unknown";
1172}
1173
1174/*!
1175 * \internal
1176 * \brief Handle the ExtensionStateList Manager action
1177 *
1178 * \param s The manager session
1179 * \param m The manager message
1180 * \retval 0 on success, -1 on failure
1181 *
1182 * This function handles the ExtensionStateList Manager action by returning a list of all extension states.
1183 */
1184static int action_extensionstatelist(struct mansession *s, const struct message *m)
1185{
1186 const char *action_id = astman_get_header(m, "ActionID");
1187
1189 astman_send_error(s, m, "No dialplan hints are available");
1190 return 0;
1191 }
1192
1193 astman_send_listack(s, m, "Extension Statuses will follow", "start");
1194
1197 struct ao2_iterator it_states;
1198 struct extension_state *state;
1199 int count = 0;
1200
1201 it_states = ao2_iterator_init(extension_states, 0);
1202 for (; (state = ao2_iterator_next(&it_states)); ao2_ref(state, -1)) {
1203 if (state->extension[0] == '_') {
1204 continue;
1205 }
1206
1207 ++count;
1208
1209 astman_append(s, "Event: ExtensionStatus\r\n");
1210 if (!ast_strlen_zero(action_id)) {
1211 astman_append(s, "ActionID: %s\r\n", action_id);
1212 }
1213 ao2_lock(state);
1214 astman_append(s,
1215 "Exten: %s\r\n"
1216 "Context: %s\r\n"
1217 "Hint: %s\r\n"
1218 "Status: %d\r\n"
1219 "StatusText: %s\r\n\r\n",
1220 state->dialplan_extension,
1221 state->dialplan_context,
1222 state->hint_extension ? ast_get_extension_app(state->hint_extension) : "None",
1223 state->device_snapshot->state,
1224 ast_extension_state2str(state->device_snapshot->state));
1226 }
1227 ao2_iterator_destroy(&it_states);
1228 astman_send_list_complete_start(s, m, "ExtensionStateListComplete", count);
1230 } else {
1231 astman_send_error(s, m, "No dialplan hints are available");
1232 }
1233
1235
1236 return 0;
1237}
1238
1239void pbx_extension_state_hint_set(struct ast_exten *exten, struct ast_context *context)
1240{
1242 struct extension_state *state;
1243
1244 snprintf(extension, sizeof(extension), "%s@%s", ast_get_extension_name(exten), ast_get_context_name(context));
1245
1247
1249 if (!state) {
1250 state = extension_state_alloc(exten, context);
1251 if (!state) {
1252 ast_log(LOG_WARNING, "Could not create extension state for hint '%s', it will be unavailable\n",
1253 extension);
1255 return;
1256 }
1258 }
1259
1260 state->hint_extension = exten;
1261 if (extension[0] != '_') {
1263 }
1264 ao2_ref(state, -1);
1265
1267}
1268
1269void pbx_extension_state_hint_remove(struct ast_exten *exten, struct ast_context *context)
1270{
1272 struct extension_state *state;
1273
1274 snprintf(extension, sizeof(extension), "%s@%s", ast_get_extension_name(exten), ast_get_context_name(context));
1275
1277
1279
1280 /* If this is not the latest hint extension that configured this extension state it can't remove it */
1281 if (!state || state->hint_extension != exten) {
1284 return;
1285 }
1286
1289 ao2_ref(state, -1);
1290
1292}
1293
1298
1299struct stasis_topic *ast_extension_state_topic(const char *exten, const char *context)
1300{
1301 struct extension_state *state;
1302 struct stasis_topic *topic;
1303
1304 state = extension_state_get(NULL, context, exten);
1305 if (!state) {
1306 return NULL;
1307 }
1308
1309 ao2_lock(state);
1310 topic = ao2_bump(state->extension_state_topic);
1312 ao2_ref(state, -1);
1313
1314 return topic;
1315}
1316
1318 const char *exten, const char *context)
1319{
1320 struct extension_state *state;
1321 struct ast_extension_state_device_snapshot *device_snapshot;
1322
1323 state = extension_state_get(chan, context, exten);
1324 if (!state) {
1325 return NULL;
1326 }
1327
1328 ao2_lock(state);
1329 device_snapshot = ao2_bump(state->device_snapshot);
1331 ao2_ref(state, -1);
1332
1333 return device_snapshot;
1334}
1335
1337 const char *exten, const char *context)
1338{
1339 struct extension_state *state;
1340 struct ast_extension_state_presence_snapshot *presence_snapshot;
1341
1342 state = extension_state_get(chan, context, exten);
1343 if (!state) {
1344 return NULL;
1345 }
1346
1347 ao2_lock(state);
1348 presence_snapshot = ao2_bump(state->presence_snapshot);
1350 ao2_ref(state, -1);
1351
1352 return presence_snapshot;
1353}
1354
1355/*!
1356 * \internal
1357 * \brief CLI command to show hints
1358 *
1359 * \param e The CLI entry
1360 * \param cmd The command
1361 * \param a The CLI arguments
1362 *
1363 * This function shows all registered hints in the CLI.
1364 */
1365static char *handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1366{
1367 struct extension_state *state;
1368 int num = 0;
1369 struct ao2_iterator i;
1370
1371 switch (cmd) {
1372 case CLI_INIT:
1373 e->command = "core show hints";
1374 e->usage =
1375 "Usage: core show hints\n"
1376 " List registered hints.\n"
1377 " Hint details are shown in five columns. In order from left to right, they are:\n"
1378 " 1. Hint extension URI.\n"
1379 " 2. List of mapped device or presence state identifiers.\n"
1380 " 3. Current extension state. The aggregate of mapped device states.\n"
1381 " 4. Current presence state for the mapped presence state provider.\n"
1382 " 5. Watchers - number of subscriptions and other entities watching this hint.\n";
1383 return NULL;
1384 case CLI_GENERATE:
1385 return NULL;
1386 }
1387
1389 ast_cli(a->fd, "There are no registered dialplan hints\n");
1390 return CLI_SUCCESS;
1391 }
1392 /* ... we have hints ... */
1393 ast_cli(a->fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
1394
1396 for (; (state = ao2_iterator_next(&i)); ao2_ref(state, -1)) {
1397 ao2_lock(state);
1398 ast_cli(a->fd, "%-30.30s: %-60.60s State:%-15.15s Presence:%-15.15s Watchers %2zd\n",
1399 state->extension,
1400 state->hint_extension ? ast_get_extension_app(state->hint_extension) : "None",
1401 ast_extension_state2str(state->device_snapshot->state),
1402 ast_presence_state2str(state->presence_snapshot->presence_state),
1403 state->extension_state_topic ? stasis_topic_subscribers(state->extension_state_topic) : 0);
1405
1406 num++;
1407 }
1409
1410 ast_cli(a->fd, "----------------\n");
1411 ast_cli(a->fd, "- %d hints registered\n", num);
1412 return CLI_SUCCESS;
1413}
1414
1415/*!
1416 * \internal
1417 * \brief Complete the core show hint CLI command
1418 *
1419 * \param line The command line
1420 * \param word The word being completed
1421 * \param pos The position in the command
1422 * \param state The completion state
1423 *
1424 * This function completes the core show hint CLI command.
1425 */
1426static char *complete_core_show_hint(const char *line, const char *word, int pos, int state)
1427{
1428 struct extension_state *extstate;
1429 char *ret = NULL;
1430 int which = 0;
1431 int wordlen;
1432 struct ao2_iterator i;
1433
1434 if (pos != 3)
1435 return NULL;
1436
1437 wordlen = strlen(word);
1438
1439 /* walk through all hints */
1441 for (; (extstate = ao2_iterator_next(&i)); ao2_ref(extstate, -1)) {
1442 ao2_lock(extstate);
1443 if (!strncasecmp(word, extstate->dialplan_extension, wordlen) && ++which > state) {
1444 ret = ast_strdup(extstate->dialplan_extension);
1445 ao2_unlock(extstate);
1446 ao2_ref(extstate, -1);
1447 break;
1448 }
1449 ao2_unlock(extstate);
1450 }
1452
1453 return ret;
1454}
1455
1456/*!
1457 * \internal
1458 * \brief CLI support for listing registered dial plan hint
1459 *
1460 * \param e The CLI entry
1461 * \param cmd The command
1462 * \param a The CLI arguments
1463 *
1464 * This function handles the core show hint CLI command.
1465 */
1466static char *handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1467{
1468 struct extension_state *extstate;
1469 int num = 0, extenlen;
1470 struct ao2_iterator i;
1471
1472 switch (cmd) {
1473 case CLI_INIT:
1474 e->command = "core show hint";
1475 e->usage =
1476 "Usage: core show hint <exten>\n"
1477 " List registered hint.\n"
1478 " Hint details are shown in five columns. In order from left to right, they are:\n"
1479 " 1. Hint extension URI.\n"
1480 " 2. List of mapped device or presence state identifiers.\n"
1481 " 3. Current extension state. The aggregate of mapped device states.\n"
1482 " 4. Current presence state for the mapped presence state provider.\n"
1483 " 5. Watchers - number of subscriptions and other entities watching this hint.\n";
1484 return NULL;
1485 case CLI_GENERATE:
1486 return complete_core_show_hint(a->line, a->word, a->pos, a->n);
1487 }
1488
1489 if (a->argc < 4)
1490 return CLI_SHOWUSAGE;
1491
1493 ast_cli(a->fd, "There are no registered dialplan hints\n");
1494 return CLI_SUCCESS;
1495 }
1496
1497 extenlen = strlen(a->argv[3]);
1499 for (; (extstate = ao2_iterator_next(&i)); ao2_ref(extstate, -1)) {
1500 ao2_lock(extstate);
1501 if (!strncasecmp(extstate->extension, a->argv[3], extenlen)) {
1502 ast_cli(a->fd, "%-30.30s: %-60.60s State:%-15.15s Presence:%-15.15s Watchers %2zd\n",
1503 extstate->extension,
1504 extstate->hint_extension ? ast_get_extension_app(extstate->hint_extension) : "None",
1508 num++;
1509 }
1510 ao2_unlock(extstate);
1511 }
1513 if (!num)
1514 ast_cli(a->fd, "No hints matching extension %s\n", a->argv[3]);
1515 else
1516 ast_cli(a->fd, "%d hint%s matching extension %s\n", num, (num!=1 ? "s":""), a->argv[3]);
1517 return CLI_SUCCESS;
1518}
1519
1521 AST_CLI_DEFINE(handle_show_hints, "Show dialplan hints"),
1522 AST_CLI_DEFINE(handle_show_hint, "Show dialplan hint"),
1523};
1524
1525/*!
1526 * \internal
1527 * \brief Callback function to clean up an individual extension state.
1528 *
1529 * \param obj The extension state object
1530 * \param arg Additional argument (not used)
1531 * \param flags Flags for the callback
1532 * \return CMP_MATCH if the object was processed, 0 otherwise
1533 */
1534static int extension_state_cleanup_individual(void *obj, void *arg, int flags)
1535{
1536 struct extension_state *state = obj;
1537
1539
1540 return CMP_MATCH;
1541}
1542
1543/*!
1544 * \internal
1545 * \brief Clean up the extension state subsystem, called at shutdown.
1546 */
1559
1561{
1563 return -1;
1564 }
1565
1567 return -1;
1568 }
1569
1570 /* Initialize extension state subsystem */
1572 extension_state_sort_fn, extension_state_cmp_fn);
1573 if (!extension_states) {
1574 ast_log(LOG_ERROR, "Failed to allocate new states container\n");
1575 return -1;
1576 }
1577
1578 extension_state_topic_all = stasis_topic_create("extension_state:all");
1580 ast_log(LOG_ERROR, "Failed to create extension state topic\n");
1581 return -1;
1582 }
1583
1585 if (!presence_state_sub) {
1586 ast_log(LOG_ERROR, "Failed to create subscription to receive presence state updates\n");
1587 return -1;
1588 }
1591
1595
1596 return 0;
1597}
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_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:2042
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition manager.c:2000
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:2078
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition manager.c:1661
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition manager.c:2086
void astman_append(struct mansession *s, const char *fmt,...)
Definition manager.c:1921
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition manager.c:7716
#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
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:323
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:620
#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:472
#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:873
#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:691