Asterisk - The Open Source Telephony Project GIT-master-266378a
Loading...
Searching...
No Matches
Data Structures | Macros | Functions | Variables
extension_state.c File Reference
#include "asterisk.h"
#include "asterisk/_private.h"
#include "asterisk/module.h"
#include "asterisk/extension_state.h"
#include "asterisk/pbx.h"
#include "asterisk/stasis.h"
#include "asterisk/stasis_message_router.h"
#include "asterisk/astobj2.h"
#include "asterisk/cli.h"
#include "pbx_private.h"
Include dependency graph for extension_state.c:

Go to the source code of this file.

Data Structures

struct  cfextension_states
 
struct  device_state_sources_vector
 
struct  extension_state
 Extension state information. More...
 
struct  extension_state_device_source
 Device state source feeding an extension state. More...
 

Macros

#define DEVICE_SOURCE_ELEM_VERSION_CMP(elem, value)   ((elem)->version != (value))
 device source non-matching version comparator for AST_VECTOR_REMOVE_CMP_UNORDERED()
 
#define EXTENSION_STATE_BUCKETS   563
 Number of buckets for extension states.
 
#define HINTDEVICE_DATA_LENGTH   16
 

Functions

static int action_extensionstatelist (struct mansession *s, const struct message *m)
 
enum ast_extension_states ast_devstate_to_extenstate (enum ast_device_state devstate)
 Map devstate to an extension state.
 
const char * ast_extension_state2str (int extension_state)
 Return string representation of the state of an extension.
 
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.
 
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.
 
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.
 
int ast_extension_state_init (void)
 
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.
 
 AST_THREADSTORAGE_CUSTOM_SCOPE (hintdevice_data, NULL, ast_free_ptr, static)
 
static char * complete_core_show_hint (const char *line, const char *word, int pos, int state)
 
static struct extension_state * extension_state_alloc (struct ast_exten *exten, struct ast_context *context)
 
static void extension_state_cleanup (void)
 
static int extension_state_cleanup_individual (void *obj, void *arg, int flags)
 
static void extension_state_destroy (void *obj)
 
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 void extension_state_device_snapshot_destroy (void *obj)
 
static void extension_state_device_snapshot_refresh (struct extension_state *state)
 
static struct extension_state_device_source * extension_state_device_source_alloc (struct extension_state *state, const char *device)
 
static void extension_state_device_source_destroy (struct extension_state_device_source *source)
 
static void extension_state_device_state_cb (void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
 
static struct ast_extension_state_device_state_info * extension_state_device_state_info_alloc (const char *device, enum ast_device_state state)
 
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_presence_snapshot_destroy (void *obj)
 
static void extension_state_presence_snapshot_refresh (struct extension_state *state)
 
static void extension_state_presence_state_cb (void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
 
static struct stasis_message * extension_state_remove_message_create (const char *context, const char *extension)
 
static void extension_state_shutdown (struct extension_state *state)
 
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)
 
static void extension_state_update_message_destroy (void *obj)
 
static int extension_state_update_sources (struct extension_state *state, struct ast_exten *exten)
 
static char * handle_show_hint (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * handle_show_hints (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
void pbx_extension_state_hint_remove (struct ast_exten *exten, struct ast_context *context)
 
void pbx_extension_state_hint_set (struct ast_exten *exten, struct ast_context *context)
 
 STASIS_MESSAGE_TYPE_DEFN (ast_extension_state_remove_message_type)
 Stasis message type for extension state remove messages.
 
 STASIS_MESSAGE_TYPE_DEFN (ast_extension_state_update_message_type)
 Sort function for extension states.
 

Variables

static struct ast_cli_entry extension_state_cli []
 
static const struct cfextension_states extension_state_mappings []
 
static struct stasis_topic * extension_state_topic_all
 Topic which receives all extension state updates.
 
static struct ao2_container * extension_states
 The global container of extension states.
 
static struct stasis_subscription * presence_state_sub
 Single presence state subscription, for all extension states.
 

Macro Definition Documentation

◆ DEVICE_SOURCE_ELEM_VERSION_CMP

#define DEVICE_SOURCE_ELEM_VERSION_CMP (   elem,
  value 
)    ((elem)->version != (value))

device source non-matching version comparator for AST_VECTOR_REMOVE_CMP_UNORDERED()

Parameters
elemElement to compare against
valueValue to compare with the vector element.
Returns
0 if element does not match.
Non-zero if element matches.

Definition at line 567 of file extension_state.c.

◆ EXTENSION_STATE_BUCKETS

#define EXTENSION_STATE_BUCKETS   563

Number of buckets for extension states.

Definition at line 125 of file extension_state.c.

◆ HINTDEVICE_DATA_LENGTH

#define HINTDEVICE_DATA_LENGTH   16

Definition at line 82 of file extension_state.c.

Function Documentation

◆ action_extensionstatelist()

static int action_extensionstatelist ( struct mansession *  s,
const struct message *  m 
)
static

Definition at line 1183 of file extension_state.c.

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",
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}
#define ao2_iterator_next(iter)
Definition astobj2.h:1911
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_unlock(a)
Definition astobj2.h:729
#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
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
const char * ast_extension_state2str(int extension_state)
Return string representation of the state of an extension.
static struct ao2_container * extension_states
The global container of extension states.
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
const char * ast_get_extension_app(struct ast_exten *e)
Definition pbx.c:6715
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition astobj2.h:1821
Extension state information.
struct ast_extension_state_device_snapshot * device_snapshot
The current device snapshot for the extension.

References ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_extension_state2str(), ast_get_extension_app(), ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_error(), astman_send_list_complete_end(), astman_send_list_complete_start(), astman_send_listack(), and extension_states.

Referenced by ast_extension_state_init().

◆ ast_devstate_to_extenstate()

enum ast_extension_states ast_devstate_to_extenstate ( enum ast_device_state  devstate)

Map devstate to an extension state.

Parameters
[in]devstatedevice state
Returns
the extension state mapping.

Definition at line 1135 of file extension_state.c.

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}
@ 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
@ 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

References AST_DEVICE_BUSY, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_ONHOLD, AST_DEVICE_RINGING, AST_DEVICE_RINGINUSE, AST_DEVICE_TOTAL, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, AST_EXTENSION_BUSY, AST_EXTENSION_INUSE, AST_EXTENSION_NOT_INUSE, AST_EXTENSION_ONHOLD, AST_EXTENSION_RINGING, and AST_EXTENSION_UNAVAILABLE.

Referenced by AST_TEST_DEFINE(), extension_state_device_snapshot_refresh(), extension_state_device_state_cb(), and extension_state_update_sources().

◆ ast_extension_state2str()

const char * ast_extension_state2str ( int  extension_state)

Return string representation of the state of an extension.

Parameters
extension_stateis the numerical state delivered by ast_extension_state
Returns
the state of an extension as string

Definition at line 1162 of file extension_state.c.

1163{
1164 int i;
1165
1166 for (i = 0; (i < ARRAY_LEN(extension_state_mappings)); i++) {
1169 }
1170 return "Unknown";
1171}
static const struct cfextension_states extension_state_mappings[]
const char *const text
#define ARRAY_LEN(a)
Definition utils.h:706

References ARRAY_LEN, extension_state_mappings, and cfextension_states::text.

Referenced by action_extensionstate(), action_extensionstatelist(), AST_TEST_DEFINE(), exten_state_publisher_state_cb(), handle_show_hint(), handle_show_hints(), manager_state_cb(), and to_ami().

◆ ast_extension_state_get_device_causing_channel()

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.

Since
23.5.0
22.11.0
20.21.0
Parameters
deviceThe device itself
device_stateThe state of the device
Returns
The channel that is causing the device to be in the given state
Return values
NULLif there is no channel causing the device to be in the given state

Definition at line 1069 of file extension_state.c.

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}
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
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_channel_unlock(chan)
Definition channel.h:2984
ast_channel_state
ast_channel states
@ AST_STATE_RINGING
@ AST_STATE_BUSY
@ AST_STATE_UP
#define NULL
Definition resample.c:96
Main Channel structure associated with a channel.
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

References ast_channel_creationtime(), ast_channel_iterator_by_name_new(), ast_channel_iterator_destroy(), ast_channel_iterator_next(), ast_channel_lock, AST_CHANNEL_NAME, ast_channel_ref, ast_channel_unlock, ast_channel_unref, AST_DEVICE_BUSY, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_ONHOLD, AST_DEVICE_RINGING, AST_DEVICE_RINGINUSE, AST_DEVICE_TOTAL, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, AST_STATE_BUSY, AST_STATE_RINGING, AST_STATE_UP, ast_tvcmp(), match(), and NULL.

Referenced by extension_state_legacy_add_device_state_info().

◆ ast_extension_state_get_latest_device_snapshot()

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.

Since
23.5.0
22.11.0
20.21.0
Parameters
chanThe optional channel to get the underlying hint from, if it needs to be created
extenThe extension to get the device state message for
contextThe context of the extension
Returns
The latest device snapshot for the extension
Return values
NULLif the extension does not have a configured hint

Definition at line 1406 of file extension_state.c.

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}
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition astobj2.h:480
static struct extension_state * extension_state_get(struct ast_channel *chan, const char *context, const char *extension)
static void extension_state_device_snapshot_refresh(struct extension_state *state)
Device snapshot for an extension state.

References ao2_bump, ao2_lock, ao2_ref, ao2_unlock, extension_state_device_snapshot_refresh(), extension_state_get(), and NULL.

Referenced by ast_extension_state(), and ast_extension_state_extended().

◆ ast_extension_state_get_latest_presence_snapshot()

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.

Since
23.5.0
22.11.0
20.21.0
Parameters
chanThe optional channel to get the underlying hint from, if it needs to be created
extenThe extension to get the presence state message for
contextThe context of the extension
Returns
The latest presence snapshot for the extension
Return values
NULLif the extension does not have a configured hint

Definition at line 1485 of file extension_state.c.

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}
static void extension_state_presence_snapshot_refresh(struct extension_state *state)
Presence snapshot for an extension state.

References ao2_bump, ao2_lock, ao2_ref, ao2_unlock, extension_state_get(), extension_state_presence_snapshot_refresh(), and NULL.

Referenced by ast_hint_presence_state().

◆ ast_extension_state_init()

int ast_extension_state_init ( void  )

Provided by extension_state.c

Definition at line 1714 of file extension_state.c.

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}
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition clicompat.c:19
#define ast_log
Definition astobj2.c:42
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition astobj2.h:363
#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 ast_cli_register_multiple(e, len)
Register multiple commands.
Definition cli.h:265
static struct stasis_topic * extension_state_topic_all
Topic which receives all extension state updates.
static struct ast_cli_entry extension_state_cli[]
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_presence_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
static void extension_state_cleanup(void)
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.
#define LOG_ERROR
#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
struct stasis_topic * ast_presence_state_topic_all(void)
Get presence state topic.
struct stasis_message_type * ast_presence_state_message_type(void)
Get presence state message type.
@ STASIS_SUBSCRIPTION_FILTER_SELECTIVE
Definition stasis.h:297
struct stasis_topic * stasis_topic_create(const char *name)
Create a new topic.
Definition stasis.c:684
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
#define stasis_subscribe(topic, callback, data)
Definition stasis.h:649

References action_extensionstatelist(), AO2_ALLOC_OPT_LOCK_MUTEX, ao2_container_alloc_rbtree, ARRAY_LEN, ast_cli_register_multiple, ast_extension_state_remove_message_type(), ast_extension_state_update_message_type(), ast_log, ast_manager_register_xml_core, ast_presence_state_message_type(), ast_presence_state_topic_all(), ast_register_cleanup(), EVENT_FLAG_CALL, EVENT_FLAG_REPORTING, extension_state_cleanup(), extension_state_cli, extension_state_presence_state_cb(), extension_state_topic_all, extension_states, LOG_ERROR, NULL, presence_state_sub, STASIS_MESSAGE_TYPE_INIT, stasis_subscribe, stasis_subscription_accept_message_type(), STASIS_SUBSCRIPTION_FILTER_SELECTIVE, stasis_subscription_set_filter(), and stasis_topic_create().

Referenced by asterisk_daemon().

◆ ast_extension_state_topic()

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.

Since
23.5.0
22.11.0
20.21.0
Parameters
extenThe extension to receive extension state messages for
contextThe context of the extension
Returns
The topic for extension state messages
Return values
NULLif it has not been allocated

Definition at line 1298 of file extension_state.c.

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}

References ao2_bump, ao2_lock, ao2_ref, ao2_unlock, extension_state_get(), and NULL.

Referenced by extension_state_legacy_add_destroy().

◆ ast_extension_state_topic_all()

struct stasis_topic * ast_extension_state_topic_all ( void  )

Get the Stasis topic to receive all extension state messages.

Since
23.5.0
22.11.0
20.21.0
Returns
The topic for extension state messages
Return values
NULLif it has not been allocated

Definition at line 1293 of file extension_state.c.

1294{
1296}

References extension_state_topic_all.

Referenced by extension_state_legacy_add_destroy().

◆ AST_THREADSTORAGE_CUSTOM_SCOPE()

AST_THREADSTORAGE_CUSTOM_SCOPE ( hintdevice_data  ,
NULL  ,
ast_free_ptr  ,
static   
)

◆ complete_core_show_hint()

static char * complete_core_show_hint ( const char *  line,
const char *  word,
int  pos,
int  state 
)
static

Definition at line 1578 of file extension_state.c.

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}
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
short word
char dialplan_extension[AST_MAX_EXTENSION]
The dialplan extension.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_strdup, extension_state::dialplan_extension, extension_states, and NULL.

Referenced by handle_show_hint().

◆ extension_state_alloc()

static struct extension_state * extension_state_alloc ( struct ast_exten *  exten,
struct ast_context *  context 
)
static

Definition at line 924 of file extension_state.c.

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}
#define ast_free(a)
Definition astmm.h:180
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition astmm.h:267
#define ao2_alloc(data_size, destructor_fn)
Definition astobj2.h:409
#define AST_MAX_CONTEXT
Definition channel.h:135
#define AST_MAX_EXTENSION
Definition channel.h:134
static void extension_state_destroy(void *obj)
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 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)
const char * ast_get_context_name(struct ast_context *con)
Definition ael_main.c:421
const char * ast_get_extension_name(struct ast_exten *exten)
Definition pbx.c:6667
@ AST_PRESENCE_NOT_SET
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 ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
structure to hold extensions
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition vector.h:124

References ao2_alloc, ao2_ref, ast_asprintf, ast_copy_string(), AST_EXTENSION_UNAVAILABLE, ast_free, ast_get_context_name(), ast_get_extension_name(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, AST_PRESENCE_NOT_SET, AST_VECTOR_INIT, extension_state_destroy(), extension_state_device_snapshot_create(), extension_state_presence_snapshot_create(), extension_state_topic_all, NULL, stasis_forward_all(), and stasis_topic_create().

Referenced by pbx_extension_state_hint_set().

◆ extension_state_cleanup()

static void extension_state_cleanup ( void  )
static

Definition at line 1701 of file extension_state.c.

1702{
1704 ast_manager_unregister("ExtensionStateList");
1705 if (extension_states) {
1708 }
1712}
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
#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
@ OBJ_NODATA
Definition astobj2.h:1044
@ OBJ_MULTIPLE
Definition astobj2.h:1049
@ OBJ_UNLINK
Definition astobj2.h:1039
static int extension_state_cleanup_individual(void *obj, void *arg, int flags)
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition manager.c:7792
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
Definition stasis.h:1546
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

References ao2_callback, ao2_ref, ARRAY_LEN, ast_cli_unregister_multiple(), ast_extension_state_remove_message_type(), ast_extension_state_update_message_type(), ast_manager_unregister(), extension_state_cleanup_individual(), extension_state_cli, extension_states, NULL, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, presence_state_sub, STASIS_MESSAGE_TYPE_CLEANUP, and stasis_unsubscribe_and_join().

Referenced by ast_extension_state_init().

◆ extension_state_cleanup_individual()

static int extension_state_cleanup_individual ( void *  obj,
void *  arg,
int  flags 
)
static

Definition at line 1688 of file extension_state.c.

1689{
1690 struct extension_state *state = obj;
1691
1693
1694 return CMP_MATCH;
1695}
@ CMP_MATCH
Definition astobj2.h:1027
static void extension_state_shutdown(struct extension_state *state)

References CMP_MATCH, and extension_state_shutdown().

Referenced by extension_state_cleanup().

◆ extension_state_destroy()

static void extension_state_destroy ( void *  obj)
static

Definition at line 726 of file extension_state.c.

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}
#define ao2_cleanup(obj)
Definition astobj2.h:1934

References ao2_cleanup, and ast_free.

Referenced by extension_state_alloc().

◆ extension_state_device_snapshot_create()

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

Definition at line 290 of file extension_state.c.

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}
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition astobj2.h:367
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition astobj2.h:404
static void extension_state_device_snapshot_destroy(void *obj)
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.
Device state source feeding an extension state.
struct ast_extension_state_device_state_info * info
The current state of the device - this is immutable.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition vector.h:637
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition vector.h:267
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition vector.h:708

References ast_extension_state_device_snapshot::additional_devices, AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, ao2_bump, ao2_ref, AST_VECTOR_APPEND, AST_VECTOR_GET, AST_VECTOR_INIT, AST_VECTOR_SIZE, ast_extension_state_device_snapshot::causing_device, extension_state_device_snapshot_destroy(), extension_state_device_source::info, NULL, and ast_extension_state_device_snapshot::state.

Referenced by extension_state_alloc(), extension_state_device_snapshot_refresh(), extension_state_device_state_cb(), and extension_state_update_sources().

◆ extension_state_device_snapshot_destroy()

static void extension_state_device_snapshot_destroy ( void *  obj)
static

Definition at line 269 of file extension_state.c.

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}
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition vector.h:185
#define AST_VECTOR_CALLBACK_VOID(vec, callback,...)
Execute a callback on every element in a vector disregarding callback return.
Definition vector.h:890

References ast_extension_state_device_snapshot::additional_devices, ao2_cleanup, AST_VECTOR_CALLBACK_VOID, AST_VECTOR_FREE, and ast_extension_state_device_snapshot::causing_device.

Referenced by extension_state_device_snapshot_create().

◆ extension_state_device_snapshot_refresh()

static void extension_state_device_snapshot_refresh ( struct extension_state *  state)
static

Definition at line 1328 of file extension_state.c.

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}
#define ao2_replace(dst, src)
Replace one object reference with another cleaning up the original.
Definition astobj2.h:501
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.
ast_device_state
Device States.
Definition devicestate.h:52
enum ast_extension_states ast_devstate_to_extenstate(enum ast_device_state devstate)
Map devstate to an extension state.
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)
static struct ast_extension_state_device_state_info * extension_state_device_state_info_alloc(const char *device, enum ast_device_state state)
ast_extension_states
Extension states.
Definition pbx.h:61
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition stasis.c:1589
You shouldn't care about the contents of this struct.
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.
Stasis message for extension state update message.

References ao2_ref, ao2_replace, AST_DEVICE_INVALID, ast_devstate_aggregate_add(), ast_devstate_aggregate_init(), ast_devstate_aggregate_result(), ast_devstate_to_extenstate(), ast_extension_state_update_message_type(), AST_VECTOR_GET, AST_VECTOR_SIZE, ast_extension_state_device_state_info::device, extension_state_device_snapshot_create(), extension_state_device_state_info_alloc(), extension_state_update_message_create(), extension_state_device_source::info, NULL, stasis_message_create(), stasis_publish(), and ast_extension_state_device_state_info::state.

Referenced by ast_extension_state_get_latest_device_snapshot(), handle_show_hint(), and handle_show_hints().

◆ extension_state_device_source_alloc()

static struct extension_state_device_source * extension_state_device_source_alloc ( struct extension_state *  state,
const char *  device 
)
static

Definition at line 452 of file extension_state.c.

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}
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
struct stasis_message_type * ast_device_state_message_type(void)
Get the Stasis message type for device state messages.
struct stasis_topic * ast_device_state_topic(const char *device)
Get the Stasis topic for device state messages for a specific device.
static void extension_state_device_source_destroy(struct extension_state_device_source *source)
static void extension_state_device_state_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
#define stasis_subscribe_synchronous(topic, callback, data)
Definition stasis.h:711
struct stasis_subscription * device_state_subscription
Synchronous subscription to the device state topic.

References ast_calloc, ast_device_state_message_type(), ast_device_state_topic(), extension_state_device_source::device_state_subscription, extension_state_device_source_destroy(), extension_state_device_state_cb(), extension_state_device_state_info_alloc(), extension_state_device_source::info, NULL, stasis_subscribe_synchronous, stasis_subscription_accept_message_type(), STASIS_SUBSCRIPTION_FILTER_SELECTIVE, and stasis_subscription_set_filter().

Referenced by extension_state_update_sources().

◆ extension_state_device_source_destroy()

static void extension_state_device_source_destroy ( struct extension_state_device_source *  source)
static

Definition at line 225 of file extension_state.c.

226{
227 if (source->device_state_subscription) {
229 }
230 ao2_cleanup(source->info);
231 ast_free(source);
232}
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition stasis.c:1049

References ao2_cleanup, ast_free, extension_state_device_source::device_state_subscription, extension_state_device_source::info, and stasis_unsubscribe().

Referenced by extension_state_device_source_alloc(), extension_state_shutdown(), and extension_state_update_sources().

◆ extension_state_device_state_cb()

static void extension_state_device_state_cb ( void *  userdata,
struct stasis_subscription *  sub,
struct stasis_message *  msg 
)
static

Definition at line 334 of file extension_state.c.

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}
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
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.

References ao2_lock, ao2_ref, ao2_replace, ao2_unlock, ast_device_state_message_type(), ast_devstate_aggregate_add(), ast_devstate_aggregate_init(), ast_devstate_aggregate_result(), ast_devstate_to_extenstate(), AST_EXTENSION_RINGING, ast_extension_state_update_message_type(), AST_VECTOR_GET, AST_VECTOR_SIZE, ast_device_state_message::device, ast_extension_state_device_state_info::device, ast_device_state_message::eid, extension_state_device_snapshot_create(), extension_state_device_state_info_alloc(), extension_state_update_message_create(), extension_state_device_source::info, stasis_message_create(), stasis_message_data(), stasis_publish(), ast_device_state_message::state, and ast_extension_state_device_state_info::state.

Referenced by extension_state_device_source_alloc().

◆ extension_state_device_state_info_alloc()

static struct ast_extension_state_device_state_info * extension_state_device_state_info_alloc ( const char *  device,
enum ast_device_state  state 
)
static

Definition at line 244 of file extension_state.c.

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}

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, ast_extension_state_device_state_info::device, NULL, and ast_extension_state_device_state_info::state.

Referenced by extension_state_device_snapshot_refresh(), extension_state_device_source_alloc(), and extension_state_device_state_cb().

◆ extension_state_get()

static struct extension_state * extension_state_get ( struct ast_channel *  chan,
const char *  context,
const char *  extension 
)
static

Definition at line 1006 of file extension_state.c.

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}
void ast_free_ptr(void *ptr)
free() wrapper
Definition astmm.c:1739
#define ao2_find(container, arg, flags)
Definition astobj2.h:1736
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition astobj2.h:1101
@ E_MATCH
Definition extconf.h:217
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_cidmatch(struct ast_exten *e)
Definition pbx.c:6710
const char * ast_get_extension_label(struct ast_exten *e)
Definition pbx.c:6672
#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_registrar(struct ast_exten *e)
Definition pbx.c:6690
ast_exten: An extension The dialplan is saved as a linked list with each context having it's own link...
Definition pbx.c:207
const char * foundcontext
Definition extconf.h:241

References ao2_find, ast_add_extension(), ast_free_ptr(), ast_get_extension_app(), ast_get_extension_app_data(), ast_get_extension_cidmatch(), ast_get_extension_label(), ast_get_extension_matchcid(), ast_get_extension_name(), ast_get_extension_registrar(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_strdup, ast_unlock_contexts(), ast_wrlock_contexts(), E_MATCH, extension_states, pbx_find_info::foundcontext, NULL, OBJ_SEARCH_KEY, pbx_find_extension(), PRIORITY_HINT, and pbx_find_info::stacklen.

Referenced by ast_extension_state_get_latest_device_snapshot(), ast_extension_state_get_latest_presence_snapshot(), and ast_extension_state_topic().

◆ extension_state_presence_snapshot_create()

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

Definition at line 527 of file extension_state.c.

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}
static void extension_state_presence_snapshot_destroy(void *obj)
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.

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, ao2_ref, ast_strdup, extension_state_presence_snapshot_destroy(), NULL, ast_extension_state_presence_snapshot::presence_message, ast_extension_state_presence_snapshot::presence_state, and ast_extension_state_presence_snapshot::presence_subtype.

Referenced by extension_state_alloc(), extension_state_presence_snapshot_refresh(), extension_state_presence_state_cb(), and extension_state_update_sources().

◆ extension_state_presence_snapshot_destroy()

static void extension_state_presence_snapshot_destroy ( void *  obj)
static

◆ extension_state_presence_snapshot_refresh()

static void extension_state_presence_snapshot_refresh ( struct extension_state *  state)
static

Definition at line 1438 of file extension_state.c.

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}
ast_presence_state
@ AST_PRESENCE_INVALID
struct ast_extension_state_presence_snapshot * new_presence_snapshot
The new presence snapshot - will be pointer equivalent to old if unchanged.

References ao2_ref, ao2_replace, ast_extension_state_update_message_type(), ast_free, AST_PRESENCE_INVALID, extension_state_presence_snapshot_create(), extension_state_update_message_create(), ast_extension_state_update_message::new_presence_snapshot, ast_extension_state_presence_snapshot::presence_message, ast_extension_state_presence_snapshot::presence_subtype, stasis_message_create(), and stasis_publish().

Referenced by ast_extension_state_get_latest_presence_snapshot(), handle_show_hint(), and handle_show_hints().

◆ extension_state_presence_state_cb()

static void extension_state_presence_state_cb ( void *  unused,
struct stasis_subscription *  sub,
struct stasis_message *  msg 
)
static

Definition at line 821 of file extension_state.c.

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}
char * strcasestr(const char *, const char *)
Stasis message payload representing a presence state update.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_replace, ao2_unlock, ast_extension_state_update_message_type(), ast_free, AST_PRESENCE_INVALID, ast_presence_state_message_type(), extension_state_presence_snapshot_create(), extension_state_update_message_create(), extension_states, stasis_message_create(), stasis_message_data(), stasis_publish(), and strcasestr().

Referenced by ast_extension_state_init().

◆ extension_state_remove_message_create()

static struct stasis_message * extension_state_remove_message_create ( const char *  context,
const char *  extension 
)
static

Definition at line 750 of file extension_state.c.

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}
Stasis message for extension state removal message.
char * context
The dialplan context.
char extension[0]
The dialplan extension.

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, ao2_ref, ast_copy_string(), ast_extension_state_remove_message_type(), ast_extension_state_remove_message::context, ast_extension_state_remove_message::extension, NULL, and stasis_message_create().

Referenced by extension_state_shutdown().

◆ extension_state_shutdown()

static void extension_state_shutdown ( struct extension_state *  state)
static

Definition at line 782 of file extension_state.c.

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}
static struct stasis_message * extension_state_remove_message_create(const char *context, const char *extension)
struct stasis_forward * stasis_forward_cancel(struct stasis_forward *forward)
Definition stasis.c:1626

References ao2_ref, AST_VECTOR_CALLBACK_VOID, AST_VECTOR_FREE, extension_state_device_source_destroy(), extension_state_remove_message_create(), stasis_forward_cancel(), and stasis_publish().

Referenced by extension_state_cleanup_individual(), and pbx_extension_state_hint_remove().

◆ extension_state_update_message_create()

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 
)
static

Definition at line 190 of file extension_state.c.

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}
static void extension_state_update_message_destroy(void *obj)
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_device_snapshot * new_device_snapshot
The new device snapshot - will be pointer equivalent to old if unchanged.

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, ao2_bump, ast_copy_string(), ast_extension_state_update_message::context, ast_extension_state_update_message::extension, extension_state_update_message_destroy(), ast_extension_state_update_message::new_device_snapshot, ast_extension_state_update_message::new_presence_snapshot, NULL, ast_extension_state_update_message::old_device_snapshot, and ast_extension_state_update_message::old_presence_snapshot.

Referenced by extension_state_device_snapshot_refresh(), extension_state_device_state_cb(), extension_state_presence_snapshot_refresh(), extension_state_presence_state_cb(), and extension_state_update_sources().

◆ extension_state_update_message_destroy()

static void extension_state_update_message_destroy ( void *  obj)
static

◆ extension_state_update_sources()

static int extension_state_update_sources ( struct extension_state *  state,
struct ast_exten *  exten 
)
static

Definition at line 580 of file extension_state.c.

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}
const char * str
Definition app_jack.c:150
char * strsep(char **str, const char *delims)
static char version[AST_MAX_EXTENSION]
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()
#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
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
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
Support for dynamic strings.
Definition strings.h:623
unsigned int version
The current version for this source.
long int ast_random(void)
Definition utils.c:2346
#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

References ao2_lock, ao2_ref, ao2_replace, ao2_unlock, ast_devstate_aggregate_add(), ast_devstate_aggregate_init(), ast_devstate_aggregate_result(), ast_devstate_to_extenstate(), ast_extension_state_update_message_type(), AST_EXTENSION_UNAVAILABLE, ast_free, ast_get_extension_app(), AST_PRESENCE_NOT_SET, ast_random(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strdup, ast_strlen_zero(), AST_VECTOR_APPEND, AST_VECTOR_GET, AST_VECTOR_REMOVE_ALL_CMP_UNORDERED, AST_VECTOR_SIZE, ast_extension_state_device_state_info::device, DEVICE_SOURCE_ELEM_VERSION_CMP, extension_state_device_snapshot_create(), extension_state_device_source_alloc(), extension_state_device_source_destroy(), extension_state_presence_snapshot_create(), extension_state_update_message_create(), HINTDEVICE_DATA_LENGTH, extension_state_device_source::info, NULL, S_OR, stasis_message_create(), stasis_publish(), ast_extension_state_device_state_info::state, str, strsep(), version, and extension_state_device_source::version.

Referenced by pbx_extension_state_hint_set().

◆ handle_show_hint()

static char * handle_show_hint ( struct ast_cli_entry *  e,
int  cmd,
struct ast_cli_args *  a 
)
static

Definition at line 1618 of file extension_state.c.

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",
1659 ast_extension_state2str(extstate->device_snapshot->state),
1660 ast_presence_state2str(extstate->presence_snapshot->presence_state),
1661 extstate->extension_state_topic ? stasis_topic_subscribers(extstate->extension_state_topic) : 0);
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}
#define CLI_SHOWUSAGE
Definition cli.h:45
#define CLI_SUCCESS
Definition cli.h:44
void ast_cli(int fd, const char *fmt,...)
Definition clicompat.c:6
@ CLI_INIT
Definition cli.h:152
@ CLI_GENERATE
Definition cli.h:153
static char * complete_core_show_hint(const char *line, const char *word, int pos, int state)
const char * ast_presence_state2str(enum ast_presence_state state)
Convert presence state to text string for output.
size_t stasis_topic_subscribers(const struct stasis_topic *topic)
Return the number of subscribers of a topic.
Definition stasis.c:710
char * command
Definition cli.h:186
const char * usage
Definition cli.h:177
char extension[0]
The combined extension this state is for (extension@context)
struct ast_exten * hint_extension
The dialplan hint that last configured this extension state.
static struct test_val a

References a, ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_cli(), ast_extension_state2str(), ast_get_extension_app(), ast_presence_state2str(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_core_show_hint(), extension_state::device_snapshot, extension_state::extension, extension_state_device_snapshot_refresh(), extension_state_presence_snapshot_refresh(), extension_state::extension_state_topic, extension_states, extension_state::hint_extension, NULL, extension_state::presence_snapshot, ast_extension_state_presence_snapshot::presence_state, stasis_topic_subscribers(), ast_extension_state_device_snapshot::state, and ast_cli_entry::usage.

◆ handle_show_hints()

static char * handle_show_hints ( struct ast_cli_entry *  e,
int  cmd,
struct ast_cli_args *  a 
)
static

Definition at line 1515 of file extension_state.c.

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}

References a, ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_cli(), ast_extension_state2str(), ast_get_extension_app(), ast_presence_state2str(), CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, extension_state_device_snapshot_refresh(), extension_state_presence_snapshot_refresh(), extension_states, NULL, stasis_topic_subscribers(), and ast_cli_entry::usage.

◆ pbx_extension_state_hint_remove()

void pbx_extension_state_hint_remove ( struct ast_exten *  exten,
struct ast_context *  context 
)

Definition at line 1268 of file extension_state.c.

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}
#define ao2_unlink_flags(container, obj, flags)
Remove an object from a container.
Definition astobj2.h:1600
@ OBJ_NOLOCK
Assume that the ao2_container is already locked.
Definition astobj2.h:1063

References ao2_cleanup, ao2_find, ao2_lock, ao2_ref, ao2_unlink_flags, ao2_unlock, ast_get_context_name(), ast_get_extension_name(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, extension_state_shutdown(), extension_states, OBJ_NOLOCK, and OBJ_SEARCH_KEY.

Referenced by ast_change_hint(), and ast_remove_hint().

◆ pbx_extension_state_hint_set()

void pbx_extension_state_hint_set ( struct ast_exten *  exten,
struct ast_context *  context 
)

extension_state.c functions needed by pbx.c

Definition at line 1238 of file extension_state.c.

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}
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
Definition astobj2.h:1554
static int extension_state_update_sources(struct extension_state *state, struct ast_exten *exten)
static struct extension_state * extension_state_alloc(struct ast_exten *exten, struct ast_context *context)
#define LOG_WARNING

References ao2_find, ao2_link_flags, ao2_lock, ao2_ref, ao2_unlock, ast_get_context_name(), ast_get_extension_name(), ast_log, AST_MAX_CONTEXT, AST_MAX_EXTENSION, extension_state_alloc(), extension_state_update_sources(), extension_states, LOG_WARNING, OBJ_NOLOCK, and OBJ_SEARCH_KEY.

Referenced by ast_add_hint(), ast_change_hint(), and context_promote().

◆ STASIS_MESSAGE_TYPE_DEFN() [1/2]

STASIS_MESSAGE_TYPE_DEFN ( ast_extension_state_remove_message_type  )

Stasis message type for extension state remove messages.

◆ STASIS_MESSAGE_TYPE_DEFN() [2/2]

STASIS_MESSAGE_TYPE_DEFN ( ast_extension_state_update_message_type  )

Sort function for extension states.

Compare function for extension states

Message type for extension state updates

Variable Documentation

◆ extension_state_cli

struct ast_cli_entry extension_state_cli[]
static
Initial value:
= {
{ .handler = handle_show_hints , .summary = "Show dialplan hints" ,},
{ .handler = handle_show_hint , .summary = "Show dialplan hint" ,},
}
static char * handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)

Definition at line 1674 of file extension_state.c.

1674 {
1675 AST_CLI_DEFINE(handle_show_hints, "Show dialplan hints"),
1676 AST_CLI_DEFINE(handle_show_hint, "Show dialplan hint"),
1677};
#define AST_CLI_DEFINE(fn, txt,...)
Definition cli.h:197

Referenced by ast_extension_state_init(), and extension_state_cleanup().

◆ extension_state_mappings

const struct cfextension_states extension_state_mappings[]
static

Referenced by ast_extension_state2str().

◆ extension_state_topic_all

struct stasis_topic* extension_state_topic_all
static

Topic which receives all extension state updates.

Definition at line 146 of file extension_state.c.

Referenced by ast_extension_state_init(), ast_extension_state_topic_all(), and extension_state_alloc().

◆ extension_states

struct ao2_container* extension_states
static

◆ presence_state_sub

struct stasis_subscription* presence_state_sub
static

Single presence state subscription, for all extension states.

Definition at line 149 of file extension_state.c.

Referenced by ast_extension_state_init(), and extension_state_cleanup().