diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/bridging.c | 247 | ||||
-rw-r--r-- | main/config_options.c | 6 | ||||
-rw-r--r-- | main/stasis_channels.c | 77 |
3 files changed, 272 insertions, 58 deletions
diff --git a/main/bridging.c b/main/bridging.c index 110f5255b..99e97a4ed 100644 --- a/main/bridging.c +++ b/main/bridging.c @@ -3145,15 +3145,70 @@ static struct ast_bridge_channel *bridge_channel_alloc(struct ast_bridge *bridge return bridge_channel; } -struct after_bridge_cb_ds { +struct after_bridge_cb_node { + /*! Next list node. */ + AST_LIST_ENTRY(after_bridge_cb_node) list; /*! Desired callback function. */ ast_after_bridge_cb callback; /*! After bridge callback will not be called and destroy any resources data may contain. */ ast_after_bridge_cb_failed failed; /*! Extra data to pass to the callback. */ void *data; + /*! Reason the after bridge callback failed. */ + enum ast_after_bridge_cb_reason reason; }; +struct after_bridge_cb_ds { + /*! After bridge callbacks container. */ + AST_LIST_HEAD(, after_bridge_cb_node) callbacks; +}; + +/*! + * \internal + * \brief Indicate after bridge callback failed. + * \since 12.0.0 + * + * \param node After bridge callback node. + * + * \return Nothing + */ +static void after_bridge_cb_failed(struct after_bridge_cb_node *node) +{ + if (node->failed) { + node->failed(node->reason, node->data); + node->failed = NULL; + } +} + +/*! + * \internal + * \brief Run discarding any after bridge callbacks. + * \since 12.0.0 + * + * \param after_bridge After bridge callback container process. + * \param reason Why are we doing this. + * + * \return Nothing + */ +static void after_bridge_cb_run_discard(struct after_bridge_cb_ds *after_bridge, enum ast_after_bridge_cb_reason reason) +{ + struct after_bridge_cb_node *node; + + for (;;) { + AST_LIST_LOCK(&after_bridge->callbacks); + node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list); + AST_LIST_UNLOCK(&after_bridge->callbacks); + if (!node) { + break; + } + if (!node->reason) { + node->reason = reason; + } + after_bridge_cb_failed(node); + ast_free(node); + } +} + /*! * \internal * \brief Destroy the after bridge callback datastore. @@ -3167,10 +3222,9 @@ static void after_bridge_cb_destroy(void *data) { struct after_bridge_cb_ds *after_bridge = data; - if (after_bridge->failed) { - after_bridge->failed(AST_AFTER_BRIDGE_CB_REASON_DESTROY, after_bridge->data); - after_bridge->failed = NULL; - } + after_bridge_cb_run_discard(after_bridge, AST_AFTER_BRIDGE_CB_REASON_DESTROY); + + AST_LIST_HEAD_DESTROY(&after_bridge->callbacks); ast_free(after_bridge); } @@ -3187,7 +3241,6 @@ static void after_bridge_cb_destroy(void *data) */ static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) { - /* There can be only one. Discard any already on the new channel. */ ast_after_bridge_callback_discard(new_chan, AST_AFTER_BRIDGE_CB_REASON_MASQUERADE); } @@ -3199,47 +3252,67 @@ static const struct ast_datastore_info after_bridge_cb_info = { /*! * \internal - * \brief Remove channel after the bridge callback and return it. + * \brief Setup/create an after bridge callback datastore container. * \since 12.0.0 * - * \param chan Channel to remove after bridge callback. + * \param chan Channel to setup/create the after bridge callback container on. * - * \retval datastore on success. - * \retval NULL on error or not found. + * \retval after_bridge datastore container on success. + * \retval NULL on error. */ -static struct ast_datastore *after_bridge_cb_remove(struct ast_channel *chan) +static struct after_bridge_cb_ds *after_bridge_cb_setup(struct ast_channel *chan) { struct ast_datastore *datastore; + struct after_bridge_cb_ds *after_bridge; + SCOPED_CHANNELLOCK(lock, chan); - ast_channel_lock(chan); datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL); - if (datastore && ast_channel_datastore_remove(chan, datastore)) { - datastore = NULL; + if (datastore) { + return datastore->data; } - ast_channel_unlock(chan); - return datastore; + /* Create a new datastore. */ + datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL); + if (!datastore) { + return NULL; + } + after_bridge = ast_calloc(1, sizeof(*after_bridge)); + if (!after_bridge) { + ast_datastore_free(datastore); + return NULL; + } + AST_LIST_HEAD_INIT(&after_bridge->callbacks); + datastore->data = after_bridge; + ast_channel_datastore_add(chan, datastore); + + return datastore->data; } -void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason) +/*! + * \internal + * \brief Find an after bridge callback datastore container. + * \since 12.0.0 + * + * \param chan Channel to find the after bridge callback container on. + * + * \retval after_bridge datastore container on success. + * \retval NULL on error. + */ +static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan) { struct ast_datastore *datastore; + SCOPED_CHANNELLOCK(lock, chan); - datastore = after_bridge_cb_remove(chan); - if (datastore) { - struct after_bridge_cb_ds *after_bridge = datastore->data; - - if (after_bridge && after_bridge->failed) { - after_bridge->failed(reason, after_bridge->data); - after_bridge->failed = NULL; - } - ast_datastore_free(datastore); + datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL); + if (!datastore) { + return NULL; } + return datastore->data; } /*! * \internal - * \brief Run any after bridge callback if possible. + * \brief Run any after bridge callback. * \since 12.0.0 * * \param chan Channel to run after bridge callback. @@ -3248,33 +3321,75 @@ void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_ */ static void after_bridge_callback_run(struct ast_channel *chan) { - struct ast_datastore *datastore; struct after_bridge_cb_ds *after_bridge; + struct after_bridge_cb_node *node; - if (ast_check_hangup(chan)) { + after_bridge = after_bridge_cb_find(chan); + if (!after_bridge) { return; } - /* Get after bridge goto datastore. */ - datastore = after_bridge_cb_remove(chan); - if (!datastore) { + for (;;) { + AST_LIST_LOCK(&after_bridge->callbacks); + node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list); + AST_LIST_UNLOCK(&after_bridge->callbacks); + if (!node) { + break; + } + if (node->reason) { + after_bridge_cb_failed(node); + } else { + node->failed = NULL; + node->callback(chan, node->data); + } + ast_free(node); + } +} + +/*! + * \internal + * \brief Run discarding any after bridge callbacks. + * \since 12.0.0 + * + * \param chan Channel to run after bridge callback. + * + * \return Nothing + */ +static void after_bridge_callback_run_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason) +{ + struct after_bridge_cb_ds *after_bridge; + + after_bridge = after_bridge_cb_find(chan); + if (!after_bridge) { return; } - after_bridge = datastore->data; - if (after_bridge) { - after_bridge->failed = NULL; - after_bridge->callback(chan, after_bridge->data); + after_bridge_cb_run_discard(after_bridge, reason); +} + +void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason) +{ + struct after_bridge_cb_ds *after_bridge; + struct after_bridge_cb_node *node; + + after_bridge = after_bridge_cb_find(chan); + if (!after_bridge) { + return; } - /* Discard after bridge callback datastore. */ - ast_datastore_free(datastore); + AST_LIST_LOCK(&after_bridge->callbacks); + node = AST_LIST_LAST(&after_bridge->callbacks); + if (node && !node->reason) { + node->reason = reason; + } + AST_LIST_UNLOCK(&after_bridge->callbacks); } int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data) { - struct ast_datastore *datastore; struct after_bridge_cb_ds *after_bridge; + struct after_bridge_cb_node *new_node; + struct after_bridge_cb_node *last_node; /* Sanity checks. */ ast_assert(chan != NULL); @@ -3282,29 +3397,28 @@ int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb return -1; } - /* Create a new datastore. */ - datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL); - if (!datastore) { - return -1; - } - after_bridge = ast_calloc(1, sizeof(*after_bridge)); + after_bridge = after_bridge_cb_setup(chan); if (!after_bridge) { - ast_datastore_free(datastore); return -1; } - /* Initialize it. */ - after_bridge->callback = callback; - after_bridge->failed = failed; - after_bridge->data = data; - datastore->data = after_bridge; - - /* Put it on the channel replacing any existing one. */ - ast_channel_lock(chan); - ast_after_bridge_callback_discard(chan, AST_AFTER_BRIDGE_CB_REASON_REPLACED); - ast_channel_datastore_add(chan, datastore); - ast_channel_unlock(chan); + /* Create a new callback node. */ + new_node = ast_calloc(1, sizeof(*new_node)); + if (!new_node) { + return -1; + } + new_node->callback = callback; + new_node->failed = failed; + new_node->data = data; + /* Put it in the container disabling any previously active one. */ + AST_LIST_LOCK(&after_bridge->callbacks); + last_node = AST_LIST_LAST(&after_bridge->callbacks); + if (last_node && !last_node->reason) { + last_node->reason = AST_AFTER_BRIDGE_CB_REASON_REPLACED; + } + AST_LIST_INSERT_TAIL(&after_bridge->callbacks, new_node, list); + AST_LIST_UNLOCK(&after_bridge->callbacks); return 0; } @@ -3769,7 +3883,7 @@ static void *bridge_channel_depart_thread(void *data) ast_bridge_features_destroy(bridge_channel->features); bridge_channel->features = NULL; - ast_after_bridge_callback_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART); + after_bridge_callback_run_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART); ast_after_bridge_goto_discard(bridge_channel->chan); return NULL; @@ -4993,6 +5107,23 @@ int ast_bridge_features_unregister(enum ast_bridge_builtin_feature feature) return 0; } +int ast_bridge_features_do(enum ast_bridge_builtin_feature feature, struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt) +{ + ast_bridge_hook_callback callback; + + if (ARRAY_LEN(builtin_features_handlers) <= feature) { + return -1; + } + + callback = builtin_features_handlers[feature]; + if (!callback) { + return -1; + } + callback(bridge, bridge_channel, hook_pvt); + + return 0; +} + int ast_bridge_interval_register(enum ast_bridge_builtin_interval interval, ast_bridge_builtin_set_limits_fn callback) { if (ARRAY_LEN(builtin_interval_handlers) <= interval diff --git a/main/config_options.c b/main/config_options.c index 908b6ac12..caa697c49 100644 --- a/main/config_options.c +++ b/main/config_options.c @@ -598,6 +598,12 @@ enum aco_process_status aco_process_config(struct aco_info *info, int reload) return ACO_PROCESS_ERROR; } +/* + * BUGBUG must fix config framework loading of multiple files. + * + * A reload with multiple files must reload all files if any + * file has been touched. + */ while (res != ACO_PROCESS_ERROR && (file = info->files[x++])) { const char *filename = file->filename; try_alias: diff --git a/main/stasis_channels.c b/main/stasis_channels.c index d121279d8..dd8f7b445 100644 --- a/main/stasis_channels.c +++ b/main/stasis_channels.c @@ -53,6 +53,35 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") </syntax> </managerEventInstance> </managerEvent> + <managerEvent language="en_US" name="AgentLogin"> + <managerEventInstance class="EVENT_FLAG_AGENT"> + <synopsis>Raised when an Agent has logged in.</synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> + <parameter name="Agent"> + <para>The name of the agent.</para> + </parameter> + </syntax> + <see-also> + <ref type="application">AgentLogin</ref> + <ref type="managerEvent">Agentlogoff</ref> + </see-also> + </managerEventInstance> + </managerEvent> + <managerEvent language="en_US" name="AgentLogoff"> + <managerEventInstance class="EVENT_FLAG_AGENT"> + <synopsis>Raised when an Agent has logged off.</synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentLogin']/managerEventInstance/syntax/parameter)" /> + <parameter name="Logintime"> + <para>The number of seconds the agent was logged in.</para> + </parameter> + </syntax> + <see-also> + <ref type="managerEvent">AgentLogin</ref> + </see-also> + </managerEventInstance> + </managerEvent> ***/ #define NUM_MULTI_CHANNEL_BLOB_BUCKETS 7 @@ -590,6 +619,44 @@ static struct ast_manager_event_blob *varset_to_ami(struct stasis_message *msg) ast_str_buffer(channel_event_string), variable, value); } +static struct ast_manager_event_blob *agent_login_to_ami(struct stasis_message *msg) +{ + RAII_VAR(struct ast_str *, channel_string, NULL, ast_free); + RAII_VAR(struct ast_str *, party_string, ast_str_create(256), ast_free); + struct ast_channel_blob *obj = stasis_message_data(msg); + const char *agent = ast_json_string_get(ast_json_object_get(obj->blob, "agent")); + + channel_string = ast_manager_build_channel_state_string(obj->snapshot); + if (!channel_string) { + return NULL; + } + + return ast_manager_event_blob_create(EVENT_FLAG_AGENT, "AgentLogin", + "%s" + "Agent: %s\r\n", + ast_str_buffer(channel_string), agent); +} + +static struct ast_manager_event_blob *agent_logoff_to_ami(struct stasis_message *msg) +{ + RAII_VAR(struct ast_str *, channel_string, NULL, ast_free); + RAII_VAR(struct ast_str *, party_string, ast_str_create(256), ast_free); + struct ast_channel_blob *obj = stasis_message_data(msg); + const char *agent = ast_json_string_get(ast_json_object_get(obj->blob, "agent")); + long logintime = ast_json_integer_get(ast_json_object_get(obj->blob, "logintime")); + + channel_string = ast_manager_build_channel_state_string(obj->snapshot); + if (!channel_string) { + return NULL; + } + + return ast_manager_event_blob_create(EVENT_FLAG_AGENT, "AgentLogoff", + "%s" + "Agent: %s\r\n" + "Logintime: %ld\r\n", + ast_str_buffer(channel_string), agent, logintime); +} + void ast_publish_channel_state(struct ast_channel *chan) { RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); @@ -790,6 +857,12 @@ STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_start_type); STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_stop_type); STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_start_type); STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_stop_type); +STASIS_MESSAGE_TYPE_DEFN(ast_channel_agent_login_type, + .to_ami = agent_login_to_ami, + ); +STASIS_MESSAGE_TYPE_DEFN(ast_channel_agent_logoff_type, + .to_ami = agent_logoff_to_ami, + ); /*! @} */ @@ -816,6 +889,8 @@ static void stasis_channels_cleanup(void) STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_moh_stop_type); STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_start_type); STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_stop_type); + STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_agent_login_type); + STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_agent_logoff_type); } void ast_stasis_channels_init(void) @@ -839,6 +914,8 @@ void ast_stasis_channels_init(void) STASIS_MESSAGE_TYPE_INIT(ast_channel_moh_stop_type); STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_start_type); STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_stop_type); + STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_login_type); + STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_logoff_type); channel_topic_all = stasis_topic_create("ast_channel_topic_all"); channel_topic_all_cached = stasis_caching_topic_create(channel_topic_all, channel_snapshot_get_id); |