summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorMatthew Jordan <mjordan@digium.com>2013-05-24 20:44:07 +0000
committerMatthew Jordan <mjordan@digium.com>2013-05-24 20:44:07 +0000
commit06be8463b683333c79845402d55168ef1b582fa9 (patch)
tree2fe0871cfec4d5edf3aae763541ff7efa32a444a /main
parentc1b51fd2654736fd7c614d1571f904e236006651 (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.c132
-rw-r--r--main/asterisk.c137
-rw-r--r--main/cdr.c1
-rw-r--r--main/cli.c26
-rw-r--r--main/dnsmgr.c1
-rw-r--r--main/enum.c1
-rw-r--r--main/json.c7
-rw-r--r--main/loader.c138
-rw-r--r--main/manager.c171
-rw-r--r--main/manager_channels.c688
-rw-r--r--main/manager_mwi.c202
-rw-r--r--main/pbx.c94
-rw-r--r--main/stasis_channels.c112
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);
}