diff options
author | Matthew Jordan <mjordan@digium.com> | 2013-05-24 20:44:07 +0000 |
---|---|---|
committer | Matthew Jordan <mjordan@digium.com> | 2013-05-24 20:44:07 +0000 |
commit | 06be8463b683333c79845402d55168ef1b582fa9 (patch) | |
tree | 2fe0871cfec4d5edf3aae763541ff7efa32a444a /main | |
parent | c1b51fd2654736fd7c614d1571f904e236006651 (diff) |
Migrate a large number of AMI events over to Stasis-Core
This patch moves a number of AMI events over to the Stasis-Core message bus.
This includes:
* ChanSpyStart/Stop
* MonitorStart/Stop
* MusicOnHoldStart/Stop
* FullyBooted/Reload
* All Voicemail/MWI related events
In addition, it adds some Stasis-Core and AMI support for generic AMI messages,
refactors the message router in AMI to use a single router with topic
forwarding for the topics that AMI cares about, and refactors MWI message
types and topics to be more name compliant.
Review: https://reviewboard.asterisk.org/r/2532
(closes issue ASTERISK-21462)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@389733 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main')
-rw-r--r-- | main/app.c | 132 | ||||
-rw-r--r-- | main/asterisk.c | 137 | ||||
-rw-r--r-- | main/cdr.c | 1 | ||||
-rw-r--r-- | main/cli.c | 26 | ||||
-rw-r--r-- | main/dnsmgr.c | 1 | ||||
-rw-r--r-- | main/enum.c | 1 | ||||
-rw-r--r-- | main/json.c | 7 | ||||
-rw-r--r-- | main/loader.c | 138 | ||||
-rw-r--r-- | main/manager.c | 171 | ||||
-rw-r--r-- | main/manager_channels.c | 688 | ||||
-rw-r--r-- | main/manager_mwi.c | 202 | ||||
-rw-r--r-- | main/pbx.c | 94 | ||||
-rw-r--r-- | main/stasis_channels.c | 112 |
13 files changed, 1364 insertions, 346 deletions
diff --git a/main/app.c b/main/app.c index 3001450e8..9fa501fe5 100644 --- a/main/app.c +++ b/main/app.c @@ -68,6 +68,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" #include "asterisk/astobj2.h" #include "asterisk/stasis.h" +#include "asterisk/stasis_channels.h" +#include "asterisk/json.h" #define MWI_TOPIC_BUCKETS 57 @@ -82,11 +84,22 @@ struct zombie { static AST_LIST_HEAD_STATIC(zombies, zombie); +/* + * @{ \brief Define \ref stasis topic objects for MWI + */ static struct stasis_topic *mwi_topic_all; static struct stasis_caching_topic *mwi_topic_cached; static struct stasis_topic_pool *mwi_topic_pool; +/* @} */ + +/* + * @{ \brief Define \ref stasis message types for MWI + */ +STASIS_MESSAGE_TYPE_DEFN(ast_mwi_state_type); +STASIS_MESSAGE_TYPE_DEFN(ast_mwi_vm_app_type); +/* @} */ + -STASIS_MESSAGE_TYPE_DEFN(stasis_mwi_state_type); static void *shaun_of_the_dead(void *data) { @@ -2657,61 +2670,95 @@ int ast_app_parse_timelen(const char *timestr, int *result, enum ast_timelen uni static void mwi_state_dtor(void *obj) { - struct stasis_mwi_state *mwi_state = obj; + struct ast_mwi_state *mwi_state = obj; ast_string_field_free_memory(mwi_state); + ao2_cleanup(mwi_state->snapshot); + mwi_state->snapshot = NULL; } -struct stasis_topic *stasis_mwi_topic_all(void) +struct stasis_topic *ast_mwi_topic_all(void) { return mwi_topic_all; } -struct stasis_caching_topic *stasis_mwi_topic_cached(void) +struct stasis_caching_topic *ast_mwi_topic_cached(void) { return mwi_topic_cached; } -struct stasis_topic *stasis_mwi_topic(const char *uniqueid) +struct stasis_topic *ast_mwi_topic(const char *uniqueid) { return stasis_topic_pool_get_topic(mwi_topic_pool, uniqueid); } -int stasis_publish_mwi_state_full( +struct ast_mwi_state *ast_mwi_create(const char *mailbox, const char *context) +{ + RAII_VAR(struct ast_mwi_state *, mwi_state, NULL, ao2_cleanup); + struct ast_str *uniqueid = ast_str_alloca(AST_MAX_MAILBOX_UNIQUEID); + + ast_assert(!ast_strlen_zero(mailbox)); + ast_assert(!ast_strlen_zero(context)); + + mwi_state = ao2_alloc(sizeof(*mwi_state), mwi_state_dtor); + if (!mwi_state) { + return NULL; + } + + if (ast_string_field_init(mwi_state, 256)) { + return NULL; + } + ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context); + ast_string_field_set(mwi_state, uniqueid, ast_str_buffer(uniqueid)); + ast_string_field_set(mwi_state, mailbox, mailbox); + ast_string_field_set(mwi_state, context, context); + + ao2_ref(mwi_state, +1); + return mwi_state; +} + + +int ast_publish_mwi_state_full( const char *mailbox, const char *context, int new_msgs, int old_msgs, + const char *channel_id, struct ast_eid *eid) { - RAII_VAR(struct stasis_mwi_state *, mwi_state, NULL, ao2_cleanup); + RAII_VAR(struct ast_mwi_state *, mwi_state, NULL, ao2_cleanup); RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); struct ast_str *uniqueid = ast_str_alloca(AST_MAX_MAILBOX_UNIQUEID); struct stasis_topic *mailbox_specific_topic; - ast_assert(!ast_strlen_zero(mailbox)); - ast_assert(!ast_strlen_zero(context)); - - ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context); - - mwi_state = ao2_alloc(sizeof(*mwi_state), mwi_state_dtor); - if (ast_string_field_init(mwi_state, 256)) { + mwi_state = ast_mwi_create(mailbox, context); + if (!mwi_state) { return -1; } - ast_string_field_set(mwi_state, uniqueid, ast_str_buffer(uniqueid)); - ast_string_field_set(mwi_state, mailbox, mailbox); - ast_string_field_set(mwi_state, context, context); mwi_state->new_msgs = new_msgs; mwi_state->old_msgs = old_msgs; + + if (!ast_strlen_zero(channel_id)) { + RAII_VAR(struct stasis_message *, chan_message, + stasis_cache_get(ast_channel_topic_all_cached(), + ast_channel_snapshot_type(), + channel_id), + ao2_cleanup); + if (chan_message) { + mwi_state->snapshot = stasis_message_data(chan_message); + ao2_ref(mwi_state->snapshot, +1); + } + } + if (eid) { mwi_state->eid = *eid; } else { ast_set_default_eid(&mwi_state->eid); } - message = stasis_message_create(stasis_mwi_state_type(), mwi_state); + message = stasis_message_create(ast_mwi_state_type(), mwi_state); - mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid)); + mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid)); if (!mailbox_specific_topic) { return -1; } @@ -2723,8 +2770,8 @@ int stasis_publish_mwi_state_full( static const char *mwi_state_get_id(struct stasis_message *message) { - if (stasis_mwi_state_type() == stasis_message_type(message)) { - struct stasis_mwi_state *mwi_state = stasis_message_data(message); + if (ast_mwi_state_type() == stasis_message_type(message)) { + struct ast_mwi_state *mwi_state = stasis_message_data(message); return mwi_state->uniqueid; } else if (stasis_subscription_change_type() == stasis_message_type(message)) { struct stasis_subscription_change *change = stasis_message_data(message); @@ -2734,19 +2781,58 @@ static const char *mwi_state_get_id(struct stasis_message *message) return NULL; } +static void mwi_blob_dtor(void *obj) +{ + struct ast_mwi_blob *mwi_blob = obj; + + ao2_cleanup(mwi_blob->mwi_state); + ast_json_unref(mwi_blob->blob); +} + +struct stasis_message *ast_mwi_blob_create(struct ast_mwi_state *mwi_state, + struct stasis_message_type *message_type, + struct ast_json *blob) +{ + RAII_VAR(struct ast_mwi_blob *, obj, NULL, ao2_cleanup); + RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); + + ast_assert(blob != NULL); + + obj = ao2_alloc(sizeof(*obj), mwi_blob_dtor); + if (!obj) { + return NULL; + } + + obj->mwi_state = mwi_state; + ao2_ref(obj->mwi_state, +1); + obj->blob = ast_json_ref(blob); + + msg = stasis_message_create(message_type, obj); + if (!msg) { + return NULL; + } + + ao2_ref(msg, +1); + return msg; +} + static void app_exit(void) { ao2_cleanup(mwi_topic_all); mwi_topic_all = NULL; mwi_topic_cached = stasis_caching_unsubscribe_and_join(mwi_topic_cached); - STASIS_MESSAGE_TYPE_CLEANUP(stasis_mwi_state_type); + STASIS_MESSAGE_TYPE_CLEANUP(ast_mwi_state_type); + STASIS_MESSAGE_TYPE_CLEANUP(ast_mwi_vm_app_type); ao2_cleanup(mwi_topic_pool); mwi_topic_pool = NULL; } int app_init(void) { - if (STASIS_MESSAGE_TYPE_INIT(stasis_mwi_state_type) != 0) { + if (STASIS_MESSAGE_TYPE_INIT(ast_mwi_state_type) != 0) { + return -1; + } + if (STASIS_MESSAGE_TYPE_INIT(ast_mwi_vm_app_type) != 0) { return -1; } mwi_topic_all = stasis_topic_create("stasis_mwi_topic"); diff --git a/main/asterisk.c b/main/asterisk.c index d8062d3b1..9407338ed 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -242,12 +242,44 @@ int daemon(int, int); /* defined in libresolv of all places */ #include "asterisk/sorcery.h" #include "asterisk/stasis.h" #include "asterisk/json.h" -#include "asterisk/security_events.h" #include "asterisk/stasis_endpoints.h" #include "../defaults.h" /*** DOCUMENTATION + <managerEvent language="en_US" name="FullyBooted"> + <managerEventInstance class="EVENT_FLAG_SYSTEM"> + <synopsis>Raised when all Asterisk initialization procedures have finished.</synopsis> + <syntax> + <parameter name="Status"> + <para>Informational message</para> + </parameter> + </syntax> + </managerEventInstance> + </managerEvent> + <managerEvent language="en_US" name="Shutdown"> + <managerEventInstance class="EVENT_FLAG_SYSTEM"> + <synopsis>Raised when Asterisk is shutdown or restarted.</synopsis> + <syntax> + <parameter name="Shutdown"> + <para>Whether the shutdown is proceeding cleanly (all channels + were hungup successfully) or uncleanly (channels will be + terminated)</para> + <enumlist> + <enum name="Uncleanly"/> + <enum name="Cleanly"/> + </enumlist> + </parameter> + <parameter name="Restart"> + <para>Whether or not a restart will occur.</para> + <enumlist> + <enum name="True"/> + <enum name="False"/> + </enumlist> + </parameter> + </syntax> + </managerEventInstance> + </managerEvent> ***/ #ifndef AF_LOCAL @@ -425,6 +457,9 @@ struct file_version { char *version; }; +/*! \brief The \ref stasis topic for system level changes */ +static struct stasis_topic *system_topic; + static AST_RWLIST_HEAD_STATIC(file_versions, file_version); void ast_register_file_version(const char *file, const char *version) @@ -1067,7 +1102,7 @@ struct stasis_topic *ast_system_topic(void) /*! \brief Cleanup the \ref stasis system level items */ static void stasis_system_topic_cleanup(void) { - ao2_ref(system_topic, -1); + ao2_cleanup(system_topic); system_topic = NULL; STASIS_MESSAGE_TYPE_CLEANUP(ast_network_change_type); } @@ -1085,9 +1120,54 @@ static int stasis_system_topic_init(void) if (STASIS_MESSAGE_TYPE_INIT(ast_network_change_type) != 0) { return -1; } + return 0; } +/*! + * \brief Publish a \ref system_status_type message over \ref stasis + * + * \param payload The JSON payload to send with the message + */ +static void publish_system_message(const char *message_type, struct ast_json *obj) +{ + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup); + RAII_VAR(struct ast_json *, event_info, NULL, ast_json_unref); + + if (!obj) { + return; + } + + event_info = ast_json_pack("{s: s, s: i, s: o}", + "type", message_type, + "class_type", EVENT_FLAG_SYSTEM, + "event", obj); + if (!event_info) { + return; + } + + payload = ast_json_payload_create(event_info); + if (!payload) { + return; + } + + message = stasis_message_create(ast_manager_get_generic_type(), payload); + if (!message) { + return; + } + stasis_publish(ast_manager_get_topic(), message); +} + +static void publish_fully_booted(void) +{ + RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref); + + json_object = ast_json_pack("{s: s}", + "Status", "Fully Booted"); + publish_system_message("FullyBooted", json_object); +} + static void ast_run_atexits(void) { struct ast_atexit *ae; @@ -1897,6 +1977,7 @@ static int can_safely_quit(shutdown_nice_t niceness, int restart) static void really_quit(int num, shutdown_nice_t niceness, int restart) { int active_channels; + RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref); if (niceness >= SHUTDOWN_NICE) { ast_module_shutdown(); @@ -1925,33 +2006,10 @@ static void really_quit(int num, shutdown_nice_t niceness, int restart) } } active_channels = ast_active_channels(); - /* The manager event for shutdown must happen prior to ast_run_atexits, as - * the manager interface will dispose of its sessions as part of its - * shutdown. - */ - /*** DOCUMENTATION - <managerEventInstance> - <synopsis>Raised when Asterisk is shutdown or restarted.</synopsis> - <syntax> - <parameter name="Shutdown"> - <enumlist> - <enum name="Uncleanly"/> - <enum name="Cleanly"/> - </enumlist> - </parameter> - <parameter name="Restart"> - <enumlist> - <enum name="True"/> - <enum name="False"/> - </enumlist> - </parameter> - </syntax> - </managerEventInstance> - ***/ - manager_event(EVENT_FLAG_SYSTEM, "Shutdown", "Shutdown: %s\r\n" - "Restart: %s\r\n", - active_channels ? "Uncleanly" : "Cleanly", - restart ? "True" : "False"); + json_object = ast_json_pack("{s: s, s: s}", + "Shutdown", active_channels ? "Uncleanly" : "Cleanly", + "Restart", restart ? "True" : "False"); + publish_system_message("Shutdown", json_object); ast_verb(0, "Asterisk %s ending (%d).\n", active_channels ? "uncleanly" : "cleanly", num); @@ -4226,13 +4284,13 @@ int main(int argc, char *argv[]) aco_init(); - if (devstate_init()) { - printf("Device state core initialization failed.\n%s", term_quit()); + if (app_init()) { + printf("App core initialization failed.\n%s", term_quit()); exit(1); } - if (app_init()) { - printf("App core initialization failed.\n%s", term_quit()); + if (devstate_init()) { + printf("Device state core initialization failed.\n%s", term_quit()); exit(1); } @@ -4264,12 +4322,6 @@ int main(int argc, char *argv[]) exit(1); } - if (ast_security_stasis_init()) { /* Initialize Security Stasis Topic and Events */ - ast_security_stasis_cleanup(); - printf("%s", term_quit()); - exit(1); - } - if (ast_named_acl_init()) { /* Initialize the Named ACL system */ printf("%s", term_quit()); exit(1); @@ -4374,12 +4426,7 @@ int main(int argc, char *argv[]) } ast_set_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED); - /*** DOCUMENTATION - <managerEventInstance> - <synopsis>Raised when all Asterisk initialization procedures have finished.</synopsis> - </managerEventInstance> - ***/ - manager_event(EVENT_FLAG_SYSTEM, "FullyBooted", "Status: Fully Booted\r\n"); + publish_fully_booted(); ast_process_pending_reloads(); diff --git a/main/cdr.c b/main/cdr.c index ff7cef207..a0560676a 100644 --- a/main/cdr.c +++ b/main/cdr.c @@ -1674,7 +1674,6 @@ static void do_reload(int reload) ast_mutex_unlock(&cdr_batch_lock); ast_config_destroy(config); - manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: CDR\r\nMessage: CDR subsystem reload requested\r\n"); } static void cdr_engine_shutdown(void) diff --git a/main/cli.c b/main/cli.c index 22232acbc..683ae9c3e 100644 --- a/main/cli.c +++ b/main/cli.c @@ -303,14 +303,30 @@ static char *handle_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args return CLI_SUCCESS; } for (x = e->args; x < a->argc; x++) { - int res = ast_module_reload(a->argv[x]); - /* XXX reload has multiple error returns, including -1 on error and 2 on success */ + enum ast_module_reload_result res = ast_module_reload(a->argv[x]); switch (res) { - case 0: + case AST_MODULE_RELOAD_NOT_FOUND: ast_cli(a->fd, "No such module '%s'\n", a->argv[x]); break; - case 1: - ast_cli(a->fd, "Module '%s' does not support reload\n", a->argv[x]); + case AST_MODULE_RELOAD_NOT_IMPLEMENTED: + ast_cli(a->fd, "The module '%s' does not support reloads\n", a->argv[x]); + break; + case AST_MODULE_RELOAD_QUEUED: + ast_cli(a->fd, "Asterisk cannot reload a module yet; request queued\n"); + break; + case AST_MODULE_RELOAD_ERROR: + ast_cli(a->fd, "The module '%s' reported a reload failure\n", a->argv[x]); + break; + case AST_MODULE_RELOAD_IN_PROGRESS: + ast_cli(a->fd, "A module reload request is already in progress; please be patient\n"); + break; + case AST_MODULE_RELOAD_UNINITIALIZED: + ast_cli(a->fd, "The module '%s' was not properly initialized. Before reloading" + " the module, you must run \"module load %s\" and fix whatever is" + " preventing the module from being initialized.\n", a->argv[x], a->argv[x]); + break; + case AST_MODULE_RELOAD_SUCCESS: + ast_cli(a->fd, "Module '%s' reloaded successfully.\n", a->argv[x]); break; } } diff --git a/main/dnsmgr.c b/main/dnsmgr.c index bfba4714d..d642cd616 100644 --- a/main/dnsmgr.c +++ b/main/dnsmgr.c @@ -514,7 +514,6 @@ static int do_reload(int loading) } ast_mutex_unlock(&refresh_lock); - manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: DNSmgr\r\nStatus: %s\r/nMessage: DNSmgr reload Requested\r\n", enabled ? "Enabled" : "Disabled"); return 0; } diff --git a/main/enum.c b/main/enum.c index d09728889..7528092e9 100644 --- a/main/enum.c +++ b/main/enum.c @@ -1007,7 +1007,6 @@ static int private_enum_init(int reload) ast_config_destroy(cfg); } ast_mutex_unlock(&enumlock); - manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Enum\r\nStatus: Enabled\r\nMessage: ENUM reload Requested\r\n"); return 0; } diff --git a/main/json.c b/main/json.c index 5b69ccbaa..70830bd13 100644 --- a/main/json.c +++ b/main/json.c @@ -78,6 +78,9 @@ struct ast_json *ast_json_ref(struct ast_json *json) void ast_json_unref(struct ast_json *json) { + if (!json) { + return; + } json_decref((json_t *)json); } @@ -327,6 +330,10 @@ const char *ast_json_object_iter_key(struct ast_json_iter *iter) { return json_object_iter_key(iter); } +struct ast_json_iter *ast_json_object_key_to_iter(const char *key) +{ + return (struct ast_json_iter *)json_object_key_to_iter(key); +} struct ast_json *ast_json_object_iter_value(struct ast_json_iter *iter) { return (struct ast_json *)json_object_iter_value(iter); diff --git a/main/loader.c b/main/loader.c index 3bcf37ca9..7e5a5ae3b 100644 --- a/main/loader.c +++ b/main/loader.c @@ -63,6 +63,30 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/utils.h" /*** DOCUMENTATION + <managerEvent language="en_US" name="Reload"> + <managerEventInstance class="EVENT_FLAG_SYSTEM"> + <synopsis>Raised when a module has been reloaded in Asterisk.</synopsis> + <syntax> + <parameter name="Module"> + <para>The name of the module that was reloaded, or + <literal>All</literal> if all modules were reloaded</para> + </parameter> + <parameter name="Status"> + <para>The numeric status code denoting the success or failure + of the reload request.</para> + <enumlist> + <enum name="0"><para>Success</para></enum> + <enum name="1"><para>Request queued</para></enum> + <enum name="2"><para>Module not found</para></enum> + <enum name="3"><para>Error</para></enum> + <enum name="4"><para>Reload already in progress</para></enum> + <enum name="5"><para>Module uninitialized</para></enum> + <enum name="6"><para>Reload not supported</para></enum> + </enumlist> + </parameter> + </syntax> + </managerEventInstance> + </managerEvent> ***/ #ifndef RTLD_NOW @@ -709,22 +733,63 @@ static void queue_reload_request(const char *module) AST_LIST_UNLOCK(&reload_queue); } -int ast_module_reload(const char *name) +/*! + * \since 12 + * \internal + * \brief Publish a \ref stasis message regarding the reload result + */ +static void publish_reload_message(const char *name, enum ast_module_reload_result result) +{ + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup); + RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref); + RAII_VAR(struct ast_json *, event_object, NULL, ast_json_unref); + char res_buffer[8]; + + snprintf(res_buffer, sizeof(res_buffer), "%d", result); + event_object = ast_json_pack("{s: s, s: s}", + "Module", S_OR(name, "All"), + "Status", res_buffer); + json_object = ast_json_pack("{s: s, s: i, s: o}", + "type", "Reload", + "class_type", EVENT_FLAG_SYSTEM, + "event", event_object); + + if (!json_object) { + return; + } + + payload = ast_json_payload_create(json_object); + if (!payload) { + return; + } + + message = stasis_message_create(ast_manager_get_generic_type(), payload); + if (!message) { + return; + } + + stasis_publish(ast_manager_get_topic(), message); +} + +enum ast_module_reload_result ast_module_reload(const char *name) { struct ast_module *cur; - int res = 0; /* return value. 0 = not found, others, see below */ + enum ast_module_reload_result res = AST_MODULE_RELOAD_NOT_FOUND; int i; /* If we aren't fully booted, we just pretend we reloaded but we queue this up to run once we are booted up. */ if (!ast_fully_booted) { queue_reload_request(name); - return 0; + res = AST_MODULE_RELOAD_QUEUED; + goto module_reload_exit; } if (ast_mutex_trylock(&reloadlock)) { ast_verbose("The previous reload command didn't finish yet\n"); - return -1; /* reload already in progress */ + res = AST_MODULE_RELOAD_IN_PROGRESS; + goto module_reload_exit; } ast_lastreloadtime = ast_tvnow(); @@ -740,26 +805,26 @@ int ast_module_reload(const char *name) if (res != AST_LOCK_SUCCESS) { ast_verbose("Cannot grab lock on %s\n", ast_config_AST_CONFIG_DIR); ast_mutex_unlock(&reloadlock); - return -1; + res = AST_MODULE_RELOAD_ERROR; + goto module_reload_exit; } } /* Call "predefined" reload here first */ for (i = 0; reload_classes[i].name; i++) { if (!name || !strcasecmp(name, reload_classes[i].name)) { - if (!reload_classes[i].reload_fn()) { - ast_test_suite_event_notify("MODULE_RELOAD", "Message: %s", name); + if (reload_classes[i].reload_fn() == AST_MODULE_LOAD_SUCCESS) { + res = AST_MODULE_RELOAD_SUCCESS; } - res = 2; /* found and reloaded */ } } - if (name && res) { + if (name && res == AST_MODULE_RELOAD_SUCCESS) { if (ast_opt_lock_confdir) { ast_unlock_path(ast_config_AST_CONFIG_DIR); } ast_mutex_unlock(&reloadlock); - return res; + goto module_reload_exit; } AST_LIST_LOCK(&module_list); @@ -770,28 +835,30 @@ int ast_module_reload(const char *name) continue; if (!cur->flags.running || cur->flags.declined) { - if (!name) + if (res == AST_MODULE_RELOAD_NOT_FOUND) { + res = AST_MODULE_RELOAD_UNINITIALIZED; + } + if (!name) { continue; - ast_log(LOG_NOTICE, "The module '%s' was not properly initialized. " - "Before reloading the module, you must run \"module load %s\" " - "and fix whatever is preventing the module from being initialized.\n", - name, name); - res = 2; /* Don't report that the module was not found */ + } break; } if (!info->reload) { /* cannot be reloaded */ - /* Nothing to reload, so reload is successful */ - ast_test_suite_event_notify("MODULE_RELOAD", "Message: %s", cur->resource); - if (res < 1) /* store result if possible */ - res = 1; /* 1 = no reload() method */ - continue; + if (res == AST_MODULE_RELOAD_NOT_FOUND) { + res = AST_MODULE_RELOAD_NOT_IMPLEMENTED; + } + if (!name) { + continue; + } + break; } - - res = 2; ast_verb(3, "Reloading module '%s' (%s)\n", cur->resource, info->description); - if (!info->reload()) { - ast_test_suite_event_notify("MODULE_RELOAD", "Message: %s", cur->resource); + if (info->reload() == AST_MODULE_LOAD_SUCCESS) { + res = AST_MODULE_RELOAD_SUCCESS; + } + if (name) { + break; } } AST_LIST_UNLOCK(&module_list); @@ -801,6 +868,8 @@ int ast_module_reload(const char *name) } ast_mutex_unlock(&reloadlock); +module_reload_exit: + publish_reload_message(name, res); return res; } @@ -1212,25 +1281,6 @@ done: } AST_LIST_UNLOCK(&module_list); - - /* Tell manager clients that are aggressive at logging in that we're done - loading modules. If there's a DNS problem in chan_sip, we might not - even reach this */ - /*** DOCUMENTATION - <managerEventInstance> - <synopsis>Raised when all dynamic modules have finished their initial loading.</synopsis> - <syntax> - <parameter name="ModuleSelection"> - <enumlist> - <enum name="Preload"/> - <enum name="All"/> - </enumlist> - </parameter> - </syntax> - </managerEventInstance> - ***/ - manager_event(EVENT_FLAG_SYSTEM, "ModuleLoadReport", "ModuleLoadStatus: Done\r\nModuleSelection: %s\r\nModuleCount: %d\r\n", preload_only ? "Preload" : "All", modulecount); - return res; } diff --git a/main/manager.c b/main/manager.c index c28e6169b..96fbdae61 100644 --- a/main/manager.c +++ b/main/manager.c @@ -92,6 +92,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/stringfields.h" #include "asterisk/presencestate.h" #include "asterisk/stasis.h" +#include "asterisk/stasis_message_router.h" #include "asterisk/test.h" #include "asterisk/json.h" #include "asterisk/bridging.h" @@ -1062,6 +1063,12 @@ static int block_sockets; static int unauth_sessions = 0; static struct stasis_subscription *acl_change_sub; +/*! \brief A \ref stasis_topic that all topics AMI cares about will be forwarded to */ +static struct stasis_topic *manager_topic; + +/*! \brief The \ref stasis_message_router for all \ref stasis messages */ +static struct stasis_message_router *stasis_router; + #define MGR_SHOW_TERMINAL_WIDTH 80 #define MAX_VARS 128 @@ -1226,6 +1233,12 @@ AO2_GLOBAL_OBJ_STATIC(event_docs); static enum add_filter_result manager_add_filter(const char *filter_pattern, struct ao2_container *whitefilters, struct ao2_container *blackfilters); /*! + * @{ \brief Define AMI message types. + */ +STASIS_MESSAGE_TYPE_DEFN(ast_manager_get_generic_type); +/*! @} */ + +/*! * \internal * \brief Find a registered action object. * @@ -1249,6 +1262,89 @@ static struct manager_action *action_find(const char *name) return act; } +struct stasis_topic *ast_manager_get_topic(void) +{ + return manager_topic; +} + +struct stasis_message_router *ast_manager_get_message_router(void) +{ + return stasis_router; +} + +struct ast_str *ast_manager_str_from_json_object(struct ast_json *blob, key_exclusion_cb exclusion_cb) +{ + struct ast_str *output_str = ast_str_create(32); + struct ast_json *value; + const char *key; + if (!output_str) { + return NULL; + } + + ast_json_object_foreach(blob, key, value) { + if (exclusion_cb && exclusion_cb(key)) { + continue; + } + ast_str_append(&output_str, 0, "%s: %s\r\n", key, ast_json_string_get(value)); + if (!output_str) { + return NULL; + } + } + + return output_str; +} + +static void manager_generic_msg_cb(void *data, struct stasis_subscription *sub, + struct stasis_topic *topic, + struct stasis_message *message) +{ + struct ast_json_payload *payload = stasis_message_data(message); + int class_type = ast_json_integer_get(ast_json_object_get(payload->json, "class_type")); + const char *type = ast_json_string_get(ast_json_object_get(payload->json, "type")); + struct ast_json *event = ast_json_object_get(payload->json, "event"); + RAII_VAR(struct ast_str *, event_buffer, NULL, ast_free); + + event_buffer = ast_manager_str_from_json_object(event, NULL); + if (!event_buffer) { + ast_log(AST_LOG_WARNING, "Error while creating payload for event %s\n", type); + return; + } + manager_event(class_type, type, "%s", ast_str_buffer(event_buffer)); +} + +int ast_manager_publish_message(struct ast_json *obj) +{ + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup); + struct ast_json *type = ast_json_object_get(obj, "type"); + struct ast_json *class_type = ast_json_object_get(obj, "class_type"); + struct ast_json *event = ast_json_object_get(obj, "event"); + + if (!type) { + ast_log(AST_LOG_ERROR, "Attempt to send generic manager event without type field\n"); + return -1; + } + if (!class_type) { + ast_log(AST_LOG_ERROR, "Attempt to send generic manager event without class type field\n"); + return -1; + } + if (!event) { + ast_log(AST_LOG_ERROR, "Attempt to send generic manager event without event payload\n"); + return -1; + } + + payload = ast_json_payload_create(obj); + if (!payload) { + return -1; + } + message = stasis_message_create(ast_manager_get_generic_type(), payload); + if (!message) { + return -1; + } + stasis_publish(ast_manager_get_topic(), message); + return 0; +} + /*! \brief Add a custom hook to be called when an event is fired */ void ast_manager_register_hook(struct manager_custom_hook *hook) { @@ -5034,24 +5130,29 @@ static int action_corestatus(struct mansession *s, const struct message *m) static int action_reload(struct mansession *s, const struct message *m) { const char *module = astman_get_header(m, "Module"); - int res = ast_module_reload(S_OR(module, NULL)); + enum ast_module_reload_result res = ast_module_reload(S_OR(module, NULL)); switch (res) { - case -1: - astman_send_error(s, m, "A reload is in progress"); - break; - case 0: + case AST_MODULE_RELOAD_NOT_FOUND: astman_send_error(s, m, "No such module"); break; - case 1: + case AST_MODULE_RELOAD_NOT_IMPLEMENTED: astman_send_error(s, m, "Module does not support reload"); break; - case 2: - astman_send_ack(s, m, "Module Reloaded"); - break; - default: + case AST_MODULE_RELOAD_ERROR: astman_send_error(s, m, "An unknown error occurred"); break; + case AST_MODULE_RELOAD_IN_PROGRESS: + astman_send_error(s, m, "A reload is in progress"); + break; + case AST_MODULE_RELOAD_UNINITIALIZED: + astman_send_error(s, m, "Module not initialized"); + break; + case AST_MODULE_RELOAD_QUEUED: + case AST_MODULE_RELOAD_SUCCESS: + /* Treat a queued request as success */ + astman_send_ack(s, m, "Module Reloaded"); + break; } return 0; } @@ -7526,6 +7627,14 @@ static void manager_shutdown(void) ao2_t_global_obj_release(event_docs, "Dispose of event_docs"); #endif + if (stasis_router) { + stasis_message_router_unsubscribe_and_join(stasis_router); + stasis_router = NULL; + } + ao2_cleanup(manager_topic); + manager_topic = NULL; + STASIS_MESSAGE_TYPE_CLEANUP(ast_manager_get_generic_type); + ast_tcptls_server_stop(&ami_desc); ast_tcptls_server_stop(&amis_desc); @@ -7552,6 +7661,31 @@ static void manager_shutdown(void) } } + +/*! \brief Initialize all \ref stasis topics and routers used by the various + * sub-components of AMI + */ +static int manager_subscriptions_init(void) +{ + STASIS_MESSAGE_TYPE_INIT(ast_manager_get_generic_type); + manager_topic = stasis_topic_create("manager_topic"); + if (!manager_topic) { + return -1; + } + stasis_router = stasis_message_router_create(manager_topic); + if (!stasis_router) { + return -1; + } + + if (stasis_message_router_add(stasis_router, + ast_manager_get_generic_type(), + manager_generic_msg_cb, + NULL)) { + return -1; + } + return 0; +} + static int __init_manager(int reload, int by_external_config) { struct ast_config *ucfg = NULL, *cfg = NULL; @@ -7573,8 +7707,19 @@ static int __init_manager(int reload, int by_external_config) manager_enabled = 0; - if (manager_channels_init()) { - return -1; + if (!reload) { + if (manager_subscriptions_init()) { + ast_log(AST_LOG_ERROR, "Failed to initialize manager subscriptions\n"); + return -1; + } + if (manager_channels_init()) { + ast_log(AST_LOG_ERROR, "Failed to initialize manager channel handling\n"); + return -1; + } + if (manager_mwi_init()) { + ast_log(AST_LOG_ERROR, "Failed to initialize manager MWI handling\n"); + return -1; + } } if (manager_bridging_init()) { @@ -8025,8 +8170,6 @@ static int __init_manager(int reload, int by_external_config) httptimeout = newhttptimeout; } - manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Manager\r\nStatus: %s\r\nMessage: Manager reload Requested\r\n", manager_enabled ? "Enabled" : "Disabled"); - ast_tcptls_server_start(&ami_desc); if (tls_was_enabled && !ami_tls_cfg.enabled) { ast_tcptls_server_stop(&amis_desc); diff --git a/main/manager_channels.c b/main/manager_channels.c index fb579dd95..f3c72ec4c 100644 --- a/main/manager_channels.c +++ b/main/manager_channels.c @@ -37,8 +37,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/stasis_channels.h" -static struct stasis_message_router *channel_state_router; - /*** DOCUMENTATION <managerEvent language="en_US" name="Newchannel"> <managerEventInstance class="EVENT_FLAG_CALL"> @@ -160,12 +158,12 @@ static struct stasis_message_router *channel_state_router; <synopsis>Raised when a dial action has started.</synopsis> <syntax> <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> - <parameter name="ChannelDest"> + <parameter name="DestChannel"> </parameter> - <parameter name="ChannelStateDest"> - <para>A numeric code for the channel's current state, related to ChannelStateDescDest</para> + <parameter name="DestChannelState"> + <para>A numeric code for the channel's current state, related to DestChannelStateDesc</para> </parameter> - <parameter name="ChannelStateDescDest"> + <parameter name="DestChannelStateDesc"> <enumlist> <enum name="Down"/> <enum name="Rsrvd"/> @@ -180,23 +178,23 @@ static struct stasis_message_router *channel_state_router; <enum name="Unknown"/> </enumlist> </parameter> - <parameter name="CallerIDNumDest"> + <parameter name="DestCallerIDNum"> </parameter> - <parameter name="CallerIDNameDest"> + <parameter name="DestCallerIDName"> </parameter> - <parameter name="ConnectedLineNumDest"> + <parameter name="DestConnectedLineNum"> </parameter> - <parameter name="ConnectedLineNameDest"> + <parameter name="DestConnectedLineName"> </parameter> - <parameter name="AccountCodeDest"> + <parameter name="DestAccountCode"> </parameter> - <parameter name="ContextDest"> + <parameter name="DestContext"> </parameter> - <parameter name="ExtenDest"> + <parameter name="DestExten"> </parameter> - <parameter name="PriorityDest"> + <parameter name="DestPriority"> </parameter> - <parameter name="UniqueidDest"> + <parameter name="DestUniqueid"> </parameter> <parameter name="DialString"> <para>The non-technology specific device being dialed.</para> @@ -230,11 +228,270 @@ static struct stasis_message_router *channel_state_router; </see-also> </managerEventInstance> </managerEvent> - ***/ + <managerEvent language="en_US" name="ChanSpyStart"> + <managerEventInstance class="EVENT_FLAG_CALL"> + <synopsis>Raised when one channel begins spying on another channel.</synopsis> + <syntax> + <parameter name="SpyerChannel"> + <para>The channel performing the spying.</para> + </parameter> + <parameter name="SpyerChannelState"> + <para>A numeric code for the channel's current state, related to SpyerChannelStateDesc</para> + </parameter> + <parameter name="SpyerChannelStateDesc"> + <enumlist> + <enum name="Down"/> + <enum name="Rsrvd"/> + <enum name="OffHook"/> + <enum name="Dialing"/> + <enum name="Ring"/> + <enum name="Ringing"/> + <enum name="Up"/> + <enum name="Busy"/> + <enum name="Dialing Offhook"/> + <enum name="Pre-ring"/> + <enum name="Unknown"/> + </enumlist> + </parameter> + <parameter name="SpyerCallerIDNum"> + </parameter> + <parameter name="SpyerCallerIDName"> + </parameter> + <parameter name="SpyerConnectedLineNum"> + </parameter> + <parameter name="SpyerConnectedLineName"> + </parameter> + <parameter name="SpyerAccountCode"> + </parameter> + <parameter name="SpyerContext"> + </parameter> + <parameter name="SpyerExten"> + </parameter> + <parameter name="SpyerPriority"> + </parameter> + <parameter name="SpyerUniqueid"> + </parameter> + <parameter name="SpyeeChannel"> + <para>The channel being spied upon.</para> + </parameter> + <parameter name="SpyeeChannelState"> + <para>A numeric code for the channel's current state, related to SpyeeChannelStateDesc</para> + </parameter> + <parameter name="SpyeeChannelStateDesc"> + <enumlist> + <enum name="Down"/> + <enum name="Rsrvd"/> + <enum name="OffHook"/> + <enum name="Dialing"/> + <enum name="Ring"/> + <enum name="Ringing"/> + <enum name="Up"/> + <enum name="Busy"/> + <enum name="Dialing Offhook"/> + <enum name="Pre-ring"/> + <enum name="Unknown"/> + </enumlist> + </parameter> + <parameter name="SpyeeCallerIDNum"> + </parameter> + <parameter name="SpyeeCallerIDName"> + </parameter> + <parameter name="SpyeeConnectedLineNum"> + </parameter> + <parameter name="SpyeeConnectedLineName"> + </parameter> + <parameter name="SpyeeAccountCode"> + </parameter> + <parameter name="SpyeeContext"> + </parameter> + <parameter name="SpyeeExten"> + </parameter> + <parameter name="SpyeePriority"> + </parameter> + <parameter name="SpyeeUniqueid"> + </parameter> + </syntax> + <see-also> + <ref type="application">ChanSpyStop</ref> + </see-also> + </managerEventInstance> + </managerEvent> + <managerEvent language="en_US" name="ChanSpyStop"> + <managerEventInstance class="EVENT_FLAG_CALL"> + <synopsis>Raised when a channel has stopped spying.</synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='ChanSpyStart']/managerEventInstance/syntax/parameter[contains(@name, 'Spyer')])" /> + </syntax> + <see-also> + <ref type="application">ChanSpyStart</ref> + </see-also> + </managerEventInstance> + </managerEvent> + <managerEvent language="en_US" name="HangupHandlerRun"> + <managerEventInstance class="EVENT_FLAG_DIALPLAN"> + <synopsis>Raised when a hangup handler is about to be called.</synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> + <parameter name="Handler"> + <para>Hangup handler parameter string passed to the Gosub application.</para> + </parameter> + </syntax> + </managerEventInstance> + </managerEvent> + <managerEvent language="en_US" name="HangupHandlerPop"> + <managerEventInstance class="EVENT_FLAG_DIALPLAN"> + <synopsis> + Raised when a hangup handler is removed from the handler stack + by the CHANNEL() function. + </synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='HangupHandlerRun']/managerEventInstance/syntax/parameter)" /> + </syntax> + <see-also> + <ref type="managerEvent">HangupHandlerPush</ref> + <ref type="function">CHANNEL</ref> + </see-also> + </managerEventInstance> + </managerEvent> + <managerEvent language="en_US" name="HangupHandlerPush"> + <managerEventInstance class="EVENT_FLAG_DIALPLAN"> + <synopsis> + Raised when a hangup handler is added to the handler stack by + the CHANNEL() function. + </synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='HangupHandlerRun']/managerEventInstance/syntax/parameter)" /> + </syntax> + <see-also> + <ref type="managerEvent">HangupHandlerPop</ref> + <ref type="function">CHANNEL</ref> + </see-also> + </managerEventInstance> + </managerEvent> + <managerEvent language="en_US" name="FAXStatus"> + <managerEventInstance class="EVENT_FLAG_CALL"> + <synopsis> + Raised periodically during a fax transmission. + </synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> + <parameter name="Operation"> + <enumlist> + <enum name="gateway"/> + <enum name="receive"/> + <enum name="send"/> + </enumlist> + </parameter> + <parameter name="Status"> + <para>A text message describing the current status of the fax</para> + </parameter> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='ReceiveFAX']/managerEventInstance/syntax/parameter[@name='LocalStationID'])" /> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='ReceiveFAX']/managerEventInstance/syntax/parameter[@name='FileName'])" /> + </syntax> + </managerEventInstance> + </managerEvent> + <managerEvent language="en_US" name="ReceiveFAX"> + <managerEventInstance class="EVENT_FLAG_CALL"> + <synopsis> + Raised when a receive fax operation has completed. + </synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> + <parameter name="LocalStationID"> + <para>The value of the <variable>LOCALSTATIONID</variable> channel variable</para> + </parameter> + <parameter name="RemoteStationID"> + <para>The value of the <variable>REMOTESTATIONID</variable> channel variable</para> + </parameter> + <parameter name="PagesTransferred"> + <para>The number of pages that have been transferred</para> + </parameter> + <parameter name="Resolution"> + <para>The negotiated resolution</para> + </parameter> + <parameter name="TransferRate"> + <para>The negotiated transfer rate</para> + </parameter> + <parameter name="FileName" multiple="yes"> + <para>The files being affected by the fax operation</para> + </parameter> + </syntax> + </managerEventInstance> + </managerEvent> + <managerEvent language="en_US" name="SendFAX"> + <managerEventInstance class="EVENT_FLAG_CALL"> + <synopsis> + Raised when a send fax operation has completed. + </synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='ReceiveFAX']/managerEventInstance/syntax/parameter)" /> + </syntax> + </managerEventInstance> + </managerEvent> + <managerEvent language="en_US" name="MusicOnHoldStart"> + <managerEventInstance class="EVENT_FLAG_CALL"> + <synopsis>Raised when music on hold has started on a channel.</synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> + <parameter name="Class"> + <para>The class of music being played on the channel</para> + </parameter> + </syntax> + <see-also> + <ref type="managerEvent">MusicOnHoldStop</ref> + <ref type="application">MusicOnHold</ref> + </see-also> + </managerEventInstance> + </managerEvent> + <managerEvent language="en_US" name="MusicOnHoldStop"> + <managerEventInstance class="EVENT_FLAG_CALL"> + <synopsis>Raised when music on hold has stopped on a channel.</synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> + </syntax> + <see-also> + <ref type="managerEvent">MusicOnHoldStart</ref> + <ref type="application">StopMusicOnHold</ref> + </see-also> + </managerEventInstance> + </managerEvent> + <managerEvent language="en_US" name="MonitorStart"> + <managerEventInstance class="EVENT_FLAG_CALL"> + <synopsis>Raised when monitoring has started on a channel.</synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> + </syntax> + <see-also> + <ref type="managerEvent">MonitorStop</ref> + <ref type="application">Monitor</ref> + <ref type="manager">Monitor</ref> + </see-also> + </managerEventInstance> + </managerEvent> + <managerEvent language="en_US" name="MonitorStop"> + <managerEventInstance class="EVENT_FLAG_CALL"> + <synopsis>Raised when monitoring has stopped on a channel.</synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> + </syntax> + <see-also> + <ref type="managerEvent">MonitorStart</ref> + <ref type="application">StopMonitor</ref> + <ref type="manager">StopMonitor</ref> + </see-also> + </managerEventInstance> + </managerEvent> +***/ + +/*! \brief The \ref stasis subscription returned by the forwarding of the channel topic + * to the manager topic + */ +static struct stasis_subscription *topic_forwarder; -struct ast_str *ast_manager_build_channel_state_string_suffix( +struct ast_str *ast_manager_build_channel_state_string_prefix( const struct ast_channel_snapshot *snapshot, - const char *suffix) + const char *prefix) { struct ast_str *out = ast_str_create(1024); int res = 0; @@ -242,30 +499,30 @@ struct ast_str *ast_manager_build_channel_state_string_suffix( return NULL; } res = ast_str_set(&out, 0, - "Channel%s: %s\r\n" - "ChannelState%s: %d\r\n" - "ChannelStateDesc%s: %s\r\n" - "CallerIDNum%s: %s\r\n" - "CallerIDName%s: %s\r\n" - "ConnectedLineNum%s: %s\r\n" - "ConnectedLineName%s: %s\r\n" - "AccountCode%s: %s\r\n" - "Context%s: %s\r\n" - "Exten%s: %s\r\n" - "Priority%s: %d\r\n" - "Uniqueid%s: %s\r\n", - suffix, snapshot->name, - suffix, snapshot->state, - suffix, ast_state2str(snapshot->state), - suffix, S_OR(snapshot->caller_number, "<unknown>"), - suffix, S_OR(snapshot->caller_name, "<unknown>"), - suffix, S_OR(snapshot->connected_number, "<unknown>"), - suffix, S_OR(snapshot->connected_name, "<unknown>"), - suffix, snapshot->accountcode, - suffix, snapshot->context, - suffix, snapshot->exten, - suffix, snapshot->priority, - suffix, snapshot->uniqueid); + "%sChannel: %s\r\n" + "%sChannelState: %d\r\n" + "%sChannelStateDesc: %s\r\n" + "%sCallerIDNum: %s\r\n" + "%sCallerIDName: %s\r\n" + "%sConnectedLineNum: %s\r\n" + "%sConnectedLineName: %s\r\n" + "%sAccountCode: %s\r\n" + "%sContext: %s\r\n" + "%sExten: %s\r\n" + "%sPriority: %d\r\n" + "%sUniqueid: %s\r\n", + prefix, snapshot->name, + prefix, snapshot->state, + prefix, ast_state2str(snapshot->state), + prefix, S_OR(snapshot->caller_number, "<unknown>"), + prefix, S_OR(snapshot->caller_name, "<unknown>"), + prefix, S_OR(snapshot->connected_number, "<unknown>"), + prefix, S_OR(snapshot->connected_name, "<unknown>"), + prefix, snapshot->accountcode, + prefix, snapshot->context, + prefix, snapshot->exten, + prefix, snapshot->priority, + prefix, snapshot->uniqueid); if (!res) { return NULL; @@ -274,8 +531,8 @@ struct ast_str *ast_manager_build_channel_state_string_suffix( if (snapshot->manager_vars) { struct ast_var_t *var; AST_LIST_TRAVERSE(snapshot->manager_vars, var, entries) { - ast_str_append(&out, 0, "ChanVariable%s: %s=%s\r\n", - suffix, + ast_str_append(&out, 0, "%sChanVariable: %s=%s\r\n", + prefix, var->name, var->value); } } @@ -286,7 +543,7 @@ struct ast_str *ast_manager_build_channel_state_string_suffix( struct ast_str *ast_manager_build_channel_state_string( const struct ast_channel_snapshot *snapshot) { - return ast_manager_build_channel_state_string_suffix(snapshot, ""); + return ast_manager_build_channel_state_string_prefix(snapshot, ""); } /*! \brief Typedef for callbacks that get called on channel snapshot updates */ @@ -477,38 +734,6 @@ static void channel_varset_cb(void *data, struct stasis_subscription *sub, variable, value); } -/*! - * \brief Callback used to determine whether a key should be skipped when converting a JSON object to a manager blob - * \param key Key from JSON blob to be evaluated - * \retval non-zero if the key should be excluded - * \retval zero if the key should not be excluded - */ -typedef int (*key_exclusion_cb)(const char *key); - -static struct ast_str *manager_str_from_json_object(struct ast_json *blob, key_exclusion_cb exclusion_cb) -{ - struct ast_str *output_str = ast_str_create(32); - struct ast_json_iter *blob_iter = ast_json_object_iter(blob); - if (!output_str || !blob_iter) { - return NULL; - } - - do { - const char *key = ast_json_object_iter_key(blob_iter); - const char *value = ast_json_string_get(ast_json_object_iter_value(blob_iter)); - if (exclusion_cb && exclusion_cb(key)) { - continue; - } - - ast_str_append(&output_str, 0, "%s: %s\r\n", key, value); - if (!output_str) { - return NULL; - } - } while ((blob_iter = ast_json_object_iter_next(blob, blob_iter))); - - return output_str; -} - static int userevent_exclusion_cb(const char *key) { if (!strcmp("type", key)) { @@ -529,7 +754,7 @@ static void channel_user_event_cb(void *data, struct stasis_subscription *sub, const char *eventname; eventname = ast_json_string_get(ast_json_object_get(obj->blob, "eventname")); - body = manager_str_from_json_object(obj->blob, userevent_exclusion_cb); + body = ast_manager_str_from_json_object(obj->blob, userevent_exclusion_cb); channel_event_string = ast_manager_build_channel_state_string(obj->snapshot); if (!channel_event_string || !body) { @@ -557,6 +782,20 @@ static void channel_user_event_cb(void *data, struct stasis_subscription *sub, ast_str_buffer(channel_event_string), eventname, ast_str_buffer(body)); } +static void publish_basic_channel_event(const char *event, int class, struct ast_channel_snapshot *snapshot) +{ + RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free); + + channel_event_string = ast_manager_build_channel_state_string(snapshot); + if (!channel_event_string) { + return; + } + + manager_event(class, event, + "%s", + ast_str_buffer(channel_event_string)); +} + static void channel_hangup_request_cb(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message) @@ -597,6 +836,64 @@ static void channel_hangup_request_cb(void *data, ast_str_buffer(extra)); } +static void channel_chanspy_stop_cb(void *data, struct stasis_subscription *sub, + struct stasis_topic *topic, struct stasis_message *message) +{ + RAII_VAR(struct ast_str *, spyer_channel_string, NULL, ast_free); + RAII_VAR(struct ast_channel_snapshot *, spyer, NULL, ao2_cleanup); + struct ast_multi_channel_blob *payload = stasis_message_data(message); + + spyer = ast_multi_channel_blob_get_channel(payload, "spyer_channel"); + if (!spyer) { + ast_log(AST_LOG_WARNING, "Received ChanSpy Start event with no spyer channel!\n"); + return; + } + + spyer_channel_string = ast_manager_build_channel_state_string_prefix(spyer, "Spyer"); + if (!spyer_channel_string) { + return; + } + + manager_event(EVENT_FLAG_CALL, "ChanSpyStop", + "%s", + ast_str_buffer(spyer_channel_string)); +} + +static void channel_chanspy_start_cb(void *data, struct stasis_subscription *sub, + struct stasis_topic *topic, struct stasis_message *message) +{ + RAII_VAR(struct ast_str *, spyer_channel_string, NULL, ast_free); + RAII_VAR(struct ast_str *, spyee_channel_string, NULL, ast_free); + RAII_VAR(struct ast_channel_snapshot *, spyer, NULL, ao2_cleanup); + RAII_VAR(struct ast_channel_snapshot *, spyee, NULL, ao2_cleanup); + struct ast_multi_channel_blob *payload = stasis_message_data(message); + + spyer = ast_multi_channel_blob_get_channel(payload, "spyer_channel"); + if (!spyer) { + ast_log(AST_LOG_WARNING, "Received ChanSpy Start event with no spyer channel!\n"); + return; + } + spyee = ast_multi_channel_blob_get_channel(payload, "spyee_channel"); + if (!spyee) { + ast_log(AST_LOG_WARNING, "Received ChanSpy Start event with no spyee channel!\n"); + return; + } + + spyer_channel_string = ast_manager_build_channel_state_string_prefix(spyer, "Spyer"); + if (!spyer_channel_string) { + return; + } + spyee_channel_string = ast_manager_build_channel_state_string_prefix(spyee, "Spyee"); + if (!spyee_channel_string) { + return; + } + + manager_event(EVENT_FLAG_CALL, "ChanSpyStart", + "%s%s", + ast_str_buffer(spyer_channel_string), + ast_str_buffer(spyee_channel_string)); +} + static void channel_dtmf_begin_cb(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message) { @@ -685,6 +982,154 @@ static void channel_dtmf_end_cb(void *data, struct stasis_subscription *sub, digit, duration_ms, direction); } +static void channel_hangup_handler_cb(void *data, struct stasis_subscription *sub, + struct stasis_topic *topic, struct stasis_message *message) +{ + RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free); + struct ast_channel_blob *payload = stasis_message_data(message); + const char *action = ast_json_string_get(ast_json_object_get(payload->blob, "type")); + const char *handler = ast_json_string_get(ast_json_object_get(payload->blob, "handler")); + const char *event; + + channel_event_string = ast_manager_build_channel_state_string(payload->snapshot); + + if (!channel_event_string) { + return; + } + + if (!strcmp(action, "type")) { + event = "HangupHandlerRun"; + } else if (!strcmp(action, "type")) { + event = "HangupHandlerPop"; + } else if (!strcmp(action, "type")) { + event = "HangupHandlerPush"; + } else { + return; + } + manager_event(EVENT_FLAG_DIALPLAN, event, + "%s" + "Handler: %s\r\n", + ast_str_buffer(channel_event_string), + handler); +} + +static void channel_fax_cb(void *data, struct stasis_subscription *sub, + struct stasis_topic *topic, struct stasis_message *message) +{ + RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free); + RAII_VAR(struct ast_str *, event_buffer, ast_str_create(256), ast_free); + struct ast_channel_blob *payload = stasis_message_data(message); + const char *type = ast_json_string_get(ast_json_object_get(payload->blob, "type")); + struct ast_json *operation = ast_json_object_get(payload->blob, "operation"); + struct ast_json *status = ast_json_object_get(payload->blob, "status"); + struct ast_json *local_station_id = ast_json_object_get(payload->blob, "local_station_id"); + struct ast_json *remote_station_id = ast_json_object_get(payload->blob, "remote_station_id"); + struct ast_json *fax_pages = ast_json_object_get(payload->blob, "fax_pages"); + struct ast_json *fax_resolution = ast_json_object_get(payload->blob, "fax_resolution"); + struct ast_json *fax_bitrate = ast_json_object_get(payload->blob, "fax_bitrate"); + struct ast_json *filenames = ast_json_object_get(payload->blob, "filenames"); + const char *event; + size_t array_len; + size_t i; + + if (!event_buffer) { + return; + } + + channel_event_string = ast_manager_build_channel_state_string(payload->snapshot); + if (!channel_event_string) { + return; + } + + if (!strcmp(type, "status")) { + event = "FAXStatus"; + } else if (!strcmp(type, "receive")) { + event = "ReceiveFAX"; + } else if (!strcmp(type, "send")) { + event = "SendFAX"; + } else { + return; + } + + if (operation) { + ast_str_append(&event_buffer, 0, "Operation: %s\r\n", ast_json_string_get(operation)); + } + if (status) { + ast_str_append(&event_buffer, 0, "Status: %s\r\n", ast_json_string_get(status)); + } + if (local_station_id) { + ast_str_append(&event_buffer, 0, "LocalStationID: %s\r\n", ast_json_string_get(local_station_id)); + } + if (remote_station_id) { + ast_str_append(&event_buffer, 0, "RemoteStationID: %s\r\n", ast_json_string_get(remote_station_id)); + } + if (fax_pages) { + ast_str_append(&event_buffer, 0, "PagesTransferred: %s\r\n", ast_json_string_get(fax_pages)); + } + if (fax_resolution) { + ast_str_append(&event_buffer, 0, "Resolution: %s\r\n", ast_json_string_get(fax_resolution)); + } + if (fax_bitrate) { + ast_str_append(&event_buffer, 0, "TransferRate: %s\r\n", ast_json_string_get(fax_bitrate)); + } + if (filenames) { + array_len = ast_json_array_size(filenames); + for (i = 0; i < array_len; i++) { + ast_str_append(&event_buffer, 0, "FileName: %s\r\n", ast_json_string_get(ast_json_array_get(filenames, i))); + } + } + + manager_event(EVENT_FLAG_CALL, event, + "%s" + "%s", + ast_str_buffer(channel_event_string), + ast_str_buffer(event_buffer)); +} + +static void channel_moh_start_cb(void *data, struct stasis_subscription *sub, + struct stasis_topic *topic, struct stasis_message *message) +{ + struct ast_channel_blob *payload = stasis_message_data(message); + struct ast_json *blob = payload->blob; + RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free); + + channel_event_string = ast_manager_build_channel_state_string(payload->snapshot); + if (!channel_event_string) { + return; + } + + manager_event(EVENT_FLAG_CALL, "MusicOnHoldStart", + "%s" + "Class: %s\r\n", + ast_str_buffer(channel_event_string), + ast_json_string_get(ast_json_object_get(blob, "class"))); + +} + +static void channel_moh_stop_cb(void *data, struct stasis_subscription *sub, + struct stasis_topic *topic, struct stasis_message *message) +{ + struct ast_channel_blob *payload = stasis_message_data(message); + + publish_basic_channel_event("MusicOnHoldStop", EVENT_FLAG_CALL, payload->snapshot); +} + +static void channel_monitor_start_cb(void *data, struct stasis_subscription *sub, + struct stasis_topic *topic, struct stasis_message *message) +{ + struct ast_channel_blob *payload = stasis_message_data(message); + + publish_basic_channel_event("MonitorStart", EVENT_FLAG_CALL, payload->snapshot); +} + +static void channel_monitor_stop_cb(void *data, struct stasis_subscription *sub, + struct stasis_topic *topic, struct stasis_message *message) +{ + struct ast_channel_blob *payload = stasis_message_data(message); + + publish_basic_channel_event("MonitorStop", EVENT_FLAG_CALL, payload->snapshot); +} + /*! * \brief Callback processing messages for channel dialing */ @@ -704,7 +1149,7 @@ static void channel_dial_cb(void *data, struct stasis_subscription *sub, /* Peer is required - otherwise, who are we dialing? */ ast_assert(peer != NULL); - peer_event_string = ast_manager_build_channel_state_string_suffix(peer, "Dest"); + peer_event_string = ast_manager_build_channel_state_string_prefix(peer, "Dest"); if (!peer_event_string) { return; } @@ -737,63 +1182,112 @@ static void channel_dial_cb(void *data, struct stasis_subscription *sub, static void manager_channels_shutdown(void) { - stasis_message_router_unsubscribe_and_join(channel_state_router); - channel_state_router = NULL; + stasis_unsubscribe(topic_forwarder); + topic_forwarder = NULL; } int manager_channels_init(void) { int ret = 0; + struct stasis_topic *manager_topic; + struct stasis_topic *channel_topic; + struct stasis_message_router *message_router; - if (channel_state_router) { - /* Already initialized */ - return 0; + manager_topic = ast_manager_get_topic(); + if (!manager_topic) { + return -1; + } + message_router = ast_manager_get_message_router(); + if (!message_router) { + return -1; + } + channel_topic = stasis_caching_get_topic(ast_channel_topic_all_cached()); + if (!channel_topic) { + return -1; } - ast_register_atexit(manager_channels_shutdown); - - channel_state_router = stasis_message_router_create( - stasis_caching_get_topic(ast_channel_topic_all_cached())); - - if (!channel_state_router) { + topic_forwarder = stasis_forward_all(channel_topic, manager_topic); + if (!topic_forwarder) { return -1; } - ret |= stasis_message_router_add(channel_state_router, + ast_register_atexit(manager_channels_shutdown); + + ret |= stasis_message_router_add(message_router, stasis_cache_update_type(), channel_snapshot_update, NULL); - ret |= stasis_message_router_add(channel_state_router, + ret |= stasis_message_router_add(message_router, ast_channel_varset_type(), channel_varset_cb, NULL); - ret |= stasis_message_router_add(channel_state_router, + ret |= stasis_message_router_add(message_router, ast_channel_user_event_type(), channel_user_event_cb, NULL); - ret |= stasis_message_router_add(channel_state_router, + ret |= stasis_message_router_add(message_router, ast_channel_dtmf_begin_type(), channel_dtmf_begin_cb, NULL); - ret |= stasis_message_router_add(channel_state_router, + ret |= stasis_message_router_add(message_router, ast_channel_dtmf_end_type(), channel_dtmf_end_cb, NULL); - ret |= stasis_message_router_add(channel_state_router, + ret |= stasis_message_router_add(message_router, ast_channel_hangup_request_type(), channel_hangup_request_cb, NULL); - ret |= stasis_message_router_add(channel_state_router, + ret |= stasis_message_router_add(message_router, ast_channel_dial_type(), channel_dial_cb, NULL); + ret |= stasis_message_router_add(message_router, + ast_channel_fax_type(), + channel_fax_cb, + NULL); + + ret |= stasis_message_router_add(message_router, + ast_channel_chanspy_start_type(), + channel_chanspy_start_cb, + NULL); + + ret |= stasis_message_router_add(message_router, + ast_channel_chanspy_stop_type(), + channel_chanspy_stop_cb, + NULL); + + ret |= stasis_message_router_add(message_router, + ast_channel_hangup_handler_type(), + channel_hangup_handler_cb, + NULL); + + ret |= stasis_message_router_add(message_router, + ast_channel_moh_start_type(), + channel_moh_start_cb, + NULL); + + ret |= stasis_message_router_add(message_router, + ast_channel_moh_stop_type(), + channel_moh_stop_cb, + NULL); + + ret |= stasis_message_router_add(message_router, + ast_channel_monitor_start_type(), + channel_monitor_start_cb, + NULL); + + ret |= stasis_message_router_add(message_router, + ast_channel_monitor_stop_type(), + channel_monitor_stop_cb, + NULL); + /* If somehow we failed to add any routes, just shut down the whole * thing and fail it. */ diff --git a/main/manager_mwi.c b/main/manager_mwi.c new file mode 100644 index 000000000..ac629089b --- /dev/null +++ b/main/manager_mwi.c @@ -0,0 +1,202 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Matt Jordan <mjordan@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief The Asterisk Management Interface - AMI (MWI event handling) + * + * \author Matt Jordan <mjordan@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/manager.h" +#include "asterisk/app.h" +#include "asterisk/channel.h" +#include "asterisk/stasis_message_router.h" +#include "asterisk/stasis.h" + +struct stasis_message_router *mwi_state_router; + +/*** DOCUMENTATION + ***/ + +/*! \brief The \ref stasis subscription returned by the forwarding of the MWI topic + * to the manager topic + */ +static struct stasis_subscription *topic_forwarder; + +/*! \brief Callback function used by \ref mwi_app_event_cb to weed out "Event" keys */ +static int exclude_event_cb(const char *key) +{ + if (!strcmp(key, "Event")) { + return -1; + } + return 0; +} + +/*! \brief Generic MWI event callback used for one-off events from voicemail modules */ +static void mwi_app_event_cb(void *data, struct stasis_subscription *sub, + struct stasis_topic *topic, + struct stasis_message *message) +{ + struct ast_mwi_blob *payload = stasis_message_data(message); + RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free); + RAII_VAR(struct ast_str *, event_buffer, NULL, ast_free); + struct ast_json *event_json = ast_json_object_get(payload->blob, "Event"); + + if (!event_json) { + return; + } + + if (payload->mwi_state && payload->mwi_state->snapshot) { + channel_event_string = ast_manager_build_channel_state_string(payload->mwi_state->snapshot); + } + + event_buffer = ast_manager_str_from_json_object(payload->blob, exclude_event_cb); + if (!event_buffer) { + ast_log(AST_LOG_WARNING, "Failed to create payload for event %s\n", ast_json_string_get(event_json)); + return; + } + + manager_event(EVENT_FLAG_CALL, ast_json_string_get(event_json), + "Mailbox: %s\r\n" + "%s" + "%s", + payload->mwi_state ? payload->mwi_state->uniqueid : "Unknown", + ast_str_buffer(event_buffer), + channel_event_string ? ast_str_buffer(channel_event_string) : ""); +} + +static void mwi_update_cb(void *data, struct stasis_subscription *sub, + struct stasis_topic *topic, + struct stasis_message *message) +{ + struct ast_mwi_state *mwi_state; + RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free); + + if (ast_mwi_state_type() != stasis_message_type(message)) { + return; + } + + mwi_state = stasis_message_data(message); + if (!mwi_state) { + return; + } + + if (mwi_state->snapshot) { + channel_event_string = ast_manager_build_channel_state_string(mwi_state->snapshot); + } + + /*** DOCUMENTATION + <managerEventInstance> + <synopsis>Raised when the state of messages in a voicemail mailbox + has changed or when a channel has finished interacting with a + mailbox.</synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> + <parameter name="Mailbox"> + <para>The mailbox with the new message, specified as <literal>mailbox</literal>@<literal>context</literal></para> + </parameter> + <parameter name="Waiting"> + <para>Whether or not the mailbox has messages waiting for it.</para> + </parameter> + <parameter name="New"> + <para>The number of new messages.</para> + </parameter> + <parameter name="Old"> + <para>The number of old messages.</para> + </parameter> + </syntax> + <description> + <note><para>The Channel related parameters are only present if a + channel was involved in the manipulation of a mailbox. If no + channel is involved, the parameters are not included with the + event.</para> + </note> + </description> + </managerEventInstance> + ***/ + manager_event(EVENT_FLAG_CALL, "MessageWaiting", + "%s" + "Mailbox: %s\r\n" + "Waiting: %d\r\n" + "New: %d\r\n" + "Old: %d\r\n", + AS_OR(channel_event_string, ""), + mwi_state->uniqueid, + ast_app_has_voicemail(mwi_state->uniqueid, NULL), + mwi_state->new_msgs, + mwi_state->old_msgs); +} + +static void manager_mwi_shutdown(void) +{ + stasis_unsubscribe(topic_forwarder); + topic_forwarder = NULL; +} + +int manager_mwi_init(void) +{ + int ret = 0; + struct stasis_topic *manager_topic; + struct stasis_topic *mwi_topic; + struct stasis_message_router *message_router; + + manager_topic = ast_manager_get_topic(); + if (!manager_topic) { + return -1; + } + message_router = ast_manager_get_message_router(); + if (!message_router) { + return -1; + } + mwi_topic = ast_mwi_topic_all(); + if (!mwi_topic) { + return -1; + } + + topic_forwarder = stasis_forward_all(mwi_topic, manager_topic); + if (!topic_forwarder) { + return -1; + } + + ast_register_atexit(manager_mwi_shutdown); + + ret |= stasis_message_router_add(message_router, + ast_mwi_state_type(), + mwi_update_cb, + NULL); + + ret |= stasis_message_router_add(message_router, + ast_mwi_vm_app_type(), + mwi_app_event_cb, + NULL); + + /* If somehow we failed to add any routes, just shut down the whole + * thing and fail it. + */ + if (ret) { + manager_mwi_shutdown(); + return -1; + } + + return 0; +} diff --git a/main/pbx.c b/main/pbx.c index 8408048f2..1c26a9c10 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -5769,6 +5769,30 @@ void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context) ast_channel_unlock(chan); } +/*! + * \internal + * \brief Publish a hangup handler related message to \ref stasis + */ +static void publish_hangup_handler_message(const char *action, struct ast_channel *chan, const char *handler) +{ + RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + + blob = ast_json_pack("{s: s, s: s}", + "type", action, + "handler", S_OR(handler, "")); + if (!blob) { + return; + } + + message = ast_channel_blob_create(chan, ast_channel_hangup_handler_type(), blob); + if (!message) { + return; + } + + stasis_publish(ast_channel_topic(chan), message); +} + int ast_pbx_hangup_handler_run(struct ast_channel *chan) { struct ast_hangup_handler_list *handlers; @@ -5798,23 +5822,7 @@ int ast_pbx_hangup_handler_run(struct ast_channel *chan) break; } - /*** DOCUMENTATION - <managerEventInstance> - <synopsis>Raised when a hangup handler is about to be called.</synopsis> - <syntax> - <parameter name="Handler"> - <para>Hangup handler parameter string passed to the Gosub application.</para> - </parameter> - </syntax> - </managerEventInstance> - ***/ - manager_event(EVENT_FLAG_DIALPLAN, "HangupHandlerRun", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Handler: %s\r\n", - ast_channel_name(chan), - ast_channel_uniqueid(chan), - h_handler->args); + publish_hangup_handler_message("run", chan, h_handler->args); ast_channel_unlock(chan); ast_app_exec_sub(NULL, chan, h_handler->args, 1); @@ -5859,30 +5867,7 @@ int ast_pbx_hangup_handler_pop(struct ast_channel *chan) handlers = ast_channel_hangup_handlers(chan); h_handler = AST_LIST_REMOVE_HEAD(handlers, node); if (h_handler) { - /*** DOCUMENTATION - <managerEventInstance> - <synopsis> - Raised when a hangup handler is removed from the handler - stack by the CHANNEL() function. - </synopsis> - <syntax> - <parameter name="Handler"> - <para>Hangup handler parameter string passed to the Gosub application.</para> - </parameter> - </syntax> - <see-also> - <ref type="managerEvent">HangupHandlerPush</ref> - <ref type="function">CHANNEL</ref> - </see-also> - </managerEventInstance> - ***/ - manager_event(EVENT_FLAG_DIALPLAN, "HangupHandlerPop", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Handler: %s\r\n", - ast_channel_name(chan), - ast_channel_uniqueid(chan), - h_handler->args); + publish_hangup_handler_message("pop", chan, h_handler->args); } ast_channel_unlock(chan); if (h_handler) { @@ -5918,32 +5903,7 @@ void ast_pbx_hangup_handler_push(struct ast_channel *chan, const char *handler) handlers = ast_channel_hangup_handlers(chan); AST_LIST_INSERT_HEAD(handlers, h_handler, node); - - /*** DOCUMENTATION - <managerEventInstance> - <synopsis> - Raised when a hangup handler is added to the handler - stack by the CHANNEL() function. - </synopsis> - <syntax> - <parameter name="Handler"> - <para>Hangup handler parameter string passed to the Gosub application.</para> - </parameter> - </syntax> - <see-also> - <ref type="managerEvent">HangupHandlerPop</ref> - <ref type="function">CHANNEL</ref> - </see-also> - </managerEventInstance> - ***/ - manager_event(EVENT_FLAG_DIALPLAN, "HangupHandlerPush", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Handler: %s\r\n", - ast_channel_name(chan), - ast_channel_uniqueid(chan), - h_handler->args); - + publish_hangup_handler_message("push", chan, h_handler->args); ast_channel_unlock(chan); } diff --git a/main/stasis_channels.c b/main/stasis_channels.c index f8c9be327..d3c543ac5 100644 --- a/main/stasis_channels.c +++ b/main/stasis_channels.c @@ -48,6 +48,14 @@ STASIS_MESSAGE_TYPE_DEFN(ast_channel_user_event_type); STASIS_MESSAGE_TYPE_DEFN(ast_channel_hangup_request_type); STASIS_MESSAGE_TYPE_DEFN(ast_channel_dtmf_begin_type); STASIS_MESSAGE_TYPE_DEFN(ast_channel_dtmf_end_type); +STASIS_MESSAGE_TYPE_DEFN(ast_channel_chanspy_start_type); +STASIS_MESSAGE_TYPE_DEFN(ast_channel_chanspy_stop_type); +STASIS_MESSAGE_TYPE_DEFN(ast_channel_fax_type); +STASIS_MESSAGE_TYPE_DEFN(ast_channel_hangup_handler_type); +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); /*! @} */ /*! \brief Topic for all channels */ @@ -150,28 +158,6 @@ struct ast_channel_snapshot *ast_channel_snapshot_create(struct ast_channel *cha return snapshot; } -struct ast_channel_snapshot *ast_channel_snapshot_get_latest( - const char *uniqueid) -{ - RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); - struct ast_channel_snapshot *snapshot; - - msg = stasis_cache_get(ast_channel_topic_all_cached(), - ast_channel_snapshot_type(), uniqueid); - - if (!msg) { - return NULL; - } - - snapshot = stasis_message_data(msg); - if (!snapshot) { - return NULL; - } - - ao2_ref(snapshot, +1); - return snapshot; -} - static void publish_message_for_channel_topics(struct stasis_message *message, struct ast_channel *chan) { if (chan) { @@ -230,12 +216,13 @@ void ast_channel_publish_dial(struct ast_channel *caller, struct ast_channel *pe publish_message_for_channel_topics(msg, caller); } -static struct stasis_message *channel_blob_create( - struct ast_channel_snapshot *snapshot, - struct stasis_message_type *type, struct ast_json *blob) +static struct stasis_message *create_channel_blob_message(struct ast_channel_snapshot *snapshot, + struct stasis_message_type *type, + struct ast_json *blob) + { - RAII_VAR(struct ast_channel_blob *, obj, NULL, ao2_cleanup); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); + RAII_VAR(struct ast_channel_blob *, obj, NULL, ao2_cleanup); if (blob == NULL) { blob = ast_json_null(); @@ -247,10 +234,9 @@ static struct stasis_message *channel_blob_create( } if (snapshot) { - ao2_ref(snapshot, +1); obj->snapshot = snapshot; + ao2_ref(obj->snapshot, +1); } - obj->blob = ast_json_ref(blob); msg = stasis_message_create(type, obj); @@ -262,33 +248,27 @@ static struct stasis_message *channel_blob_create( return msg; } -struct stasis_message *ast_channel_blob_create(struct ast_channel *chan, - struct stasis_message_type *type, struct ast_json *blob) +struct stasis_message *ast_channel_cached_blob_create(struct ast_channel *chan, + struct stasis_message_type *type, + struct ast_json *blob) { - RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); - - if (chan != NULL) { - snapshot = ast_channel_snapshot_create(chan); - if (snapshot == NULL) { - return NULL; - } - } + RAII_VAR(struct ast_channel_snapshot *, snapshot, + ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan)), + ao2_cleanup); - return channel_blob_create(snapshot, type, blob); + return create_channel_blob_message(snapshot, type, blob); } -struct stasis_message *ast_channel_blob_create_from_cache( - const char *uniqueid, struct stasis_message_type *type, - struct ast_json *blob) +struct stasis_message *ast_channel_blob_create(struct ast_channel *chan, + struct stasis_message_type *type, struct ast_json *blob) { RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); - snapshot = ast_channel_snapshot_get_latest(uniqueid); - if (snapshot == NULL) { - return NULL; + if (chan) { + snapshot = ast_channel_snapshot_create(chan); } - return channel_blob_create(snapshot, type, blob); + return create_channel_blob_message(snapshot, type, blob); } /*! \brief A channel snapshot wrapper object used in \ref ast_multi_channel_blob objects */ @@ -362,6 +342,28 @@ struct ast_multi_channel_blob *ast_multi_channel_blob_create(struct ast_json *bl return obj; } +struct ast_channel_snapshot *ast_channel_snapshot_get_latest(const char *uniqueid) +{ + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + struct ast_channel_snapshot *snapshot; + + ast_assert(!ast_strlen_zero(uniqueid)); + + message = stasis_cache_get(ast_channel_topic_all_cached(), + ast_channel_snapshot_type(), + uniqueid); + if (!message) { + return NULL; + } + + snapshot = stasis_message_data(message); + if (!snapshot) { + return NULL; + } + ao2_ref(snapshot, +1); + return snapshot; +} + static void channel_role_snapshot_dtor(void *obj) { struct channel_role_snapshot *role_snapshot = obj; @@ -459,7 +461,6 @@ void ast_channel_publish_snapshot(struct ast_channel *chan) stasis_publish(ast_channel_topic(chan), message); } - void ast_channel_publish_varset(struct ast_channel *chan, const char *name, const char *value) { RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); @@ -584,6 +585,14 @@ void ast_stasis_channels_shutdown(void) STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_hangup_request_type); STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_dtmf_begin_type); STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_dtmf_end_type); + STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_chanspy_start_type); + STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_chanspy_stop_type); + STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_fax_type); + STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_hangup_handler_type); + STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_moh_start_type); + 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); } void ast_stasis_channels_init(void) @@ -595,7 +604,14 @@ void ast_stasis_channels_init(void) STASIS_MESSAGE_TYPE_INIT(ast_channel_hangup_request_type); STASIS_MESSAGE_TYPE_INIT(ast_channel_dtmf_begin_type); STASIS_MESSAGE_TYPE_INIT(ast_channel_dtmf_end_type); - + STASIS_MESSAGE_TYPE_INIT(ast_channel_chanspy_start_type); + STASIS_MESSAGE_TYPE_INIT(ast_channel_chanspy_stop_type); + STASIS_MESSAGE_TYPE_INIT(ast_channel_fax_type); + STASIS_MESSAGE_TYPE_INIT(ast_channel_hangup_handler_type); + STASIS_MESSAGE_TYPE_INIT(ast_channel_moh_start_type); + 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); 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); } |