diff options
37 files changed, 2088 insertions, 705 deletions
@@ -72,6 +72,26 @@ AMI (Asterisk Manager Interface) event, the various ChanVariable fields will contain a suffix that specifies which channel they correspond to. + * All "Reload" events have been consolidated into a single event type. This + event will always contain a Module field specifying the name of the module + and a Status field denoting the result of the reload. All modules now issue + this event when being reloaded. + + * The "ModuleLoadReport" event has been removed. Most AMI connections would + fail to receive this event due to being connected after modules have loaded. + AMI connections that want to know when Asterisk is ready should listen for + the "FullyBooted" event. + + * app_fax now sends the same send fax/receive fax events as res_fax. The + "FaxSent" event is now the "SendFAX" event, and the "FaxReceived" event is + now the "ReceiveFAX" event. + + * The MusicOnHold event is now two events: MusicOnHoldStart and + MusicOnHoldStop. The sub type field has been removed. + + * The JabberEvent event has been removed. It is not AMI's purpose to be a + carrier for another protocol. + * The AMI 'Status' response event to the AMI Status action replaces the BridgedChannel and BridgedUniqueid headers with the BridgeID header to indicate what bridge the channel is currently in. diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c index 8e4590781..4e241b96f 100644 --- a/apps/app_chanspy.c +++ b/apps/app_chanspy.c @@ -55,6 +55,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/lock.h" #include "asterisk/options.h" #include "asterisk/autochan.h" +#include "asterisk/stasis_channels.h" +#include "asterisk/json.h" #define AST_NAME_STRLEN 256 #define NUM_SPYGROUPS 128 @@ -188,6 +190,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") </description> <see-also> <ref type="application">ExtenSpy</ref> + <ref type="managerEvent">ChanSpyStart</ref> + <ref type="managerEvent">ChanSpyStop</ref> </see-also> </application> <application name="ExtenSpy" language="en_US"> @@ -322,9 +326,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") </description> <see-also> <ref type="application">ChanSpy</ref> + <ref type="managerEvent">ChanSpyStart</ref> + <ref type="managerEvent">ChanSpyStop</ref> </see-also> </application> - <application name="DAHDIScan" language="en_US"> <synopsis> Scan DAHDI channels to monitor calls. @@ -338,6 +343,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <para>Allows a call center manager to monitor DAHDI channels in a convenient way. Use <literal>#</literal> to select the next channel and use <literal>*</literal> to exit.</para> </description> + <see-also> + <ref type="managerEvent">ChanSpyStart</ref> + <ref type="managerEvent">ChanSpyStop</ref> + </see-also> </application> ***/ @@ -512,6 +521,68 @@ static void change_spy_mode(const char digit, struct ast_flags *flags) } } +static int pack_channel_into_message(struct ast_channel *chan, const char *role, + struct ast_multi_channel_blob *payload) +{ + RAII_VAR(struct ast_channel_snapshot *, snapshot, + ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan)), + ao2_cleanup); + + if (!snapshot) { + return -1; + } + ast_multi_channel_blob_add_channel(payload, role, snapshot); + return 0; +} + +/*! \internal + * \brief Publish the chanspy message over Stasis-Core + * \param spyer The channel doing the spying + * \param spyee Who is being spied upon + * \start start If non-zero, the spying is starting. Otherwise, the spyer is + * finishing + */ +static void publish_chanspy_message(struct ast_channel *spyer, + struct ast_channel *spyee, + int start) +{ + RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); + RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup); + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + + if (!spyer) { + ast_log(AST_LOG_WARNING, "Attempt to publish ChanSpy message for NULL spyer channel\n"); + return; + } + blob = ast_json_null(); + if (!blob) { + return; + } + + payload = ast_multi_channel_blob_create(blob); + if (!payload) { + return; + } + + if (pack_channel_into_message(spyer, "spyer_channel", payload)) { + return; + } + + if (spyee) { + if (pack_channel_into_message(spyee, "spyee_channel", payload)) { + return; + } + } + + message = stasis_message_create( + start ? ast_channel_chanspy_start_type(): ast_channel_chanspy_stop_type(), + payload); + if (!message) { + return; + } + stasis_publish(ast_channel_topic(spyer), message); +} + static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan, int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags, char *exitcontext) @@ -524,38 +595,22 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto struct ast_silence_generator *silgen = NULL; struct ast_autochan *spyee_bridge_autochan = NULL; const char *spyer_name; - struct ast_channel *chans[] = { chan, spyee_autochan->chan }; - - ast_channel_lock(chan); - spyer_name = ast_strdupa(ast_channel_name(chan)); - ast_channel_unlock(chan); - - /* We now hold the channel lock on spyee */ if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan) || ast_test_flag(ast_channel_flags(spyee_autochan->chan), AST_FLAG_ZOMBIE)) { return 0; } + ast_channel_lock(chan); + spyer_name = ast_strdupa(ast_channel_name(chan)); + ast_channel_unlock(chan); + ast_channel_lock(spyee_autochan->chan); name = ast_strdupa(ast_channel_name(spyee_autochan->chan)); ast_channel_unlock(spyee_autochan->chan); ast_verb(2, "Spying on channel %s\n", name); - /*** DOCUMENTATION - <managerEventInstance> - <synopsis>Raised when a channel has started spying on another channel.</synopsis> - <see-also> - <ref type="application">ChanSpy</ref> - <ref type="application">ExtenSpy</ref> - <ref type="managerEvent">ChanSpyStop</ref> - </see-also> - </managerEventInstance> - ***/ - ast_manager_event_multichan(EVENT_FLAG_CALL, "ChanSpyStart", 2, chans, - "SpyerChannel: %s\r\n" - "SpyeeChannel: %s\r\n", - spyer_name, name); + publish_chanspy_message(chan, spyee_autochan->chan, 1); memset(&csth, 0, sizeof(csth)); ast_copy_flags(&csth.flags, flags, AST_FLAGS_ALL); @@ -740,15 +795,7 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto } ast_verb(2, "Done Spying on channel %s\n", name); - /*** DOCUMENTATION - <managerEventInstance> - <synopsis>Raised when a channel has stopped spying on another channel.</synopsis> - <see-also> - <ref type="managerEvent">ChanSpyStart</ref> - </see-also> - </managerEventInstance> - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name); + publish_chanspy_message(chan, NULL, 0); return running; } diff --git a/apps/app_fax.c b/apps/app_fax.c index adee8f4bd..a7b9e53d1 100644 --- a/apps/app_fax.c +++ b/apps/app_fax.c @@ -43,7 +43,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/app.h" #include "asterisk/dsp.h" #include "asterisk/module.h" -#include "asterisk/manager.h" +#include "asterisk/stasis.h" +#include "asterisk/stasis_channels.h" /*** DOCUMENTATION <application name="SendFAX" language="en_US" module="app_fax"> @@ -202,6 +203,9 @@ static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uin static void phase_e_handler(t30_state_t *f, void *user_data, int result) { + RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref); + RAII_VAR(struct ast_json *, json_filenames, NULL, ast_json_unref); + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); const char *local_ident; const char *far_ident; char buf[20]; @@ -251,32 +255,24 @@ static void phase_e_handler(t30_state_t *f, void *user_data, int result) ast_debug(1, " Image resolution: %d x %d\n", stat.x_resolution, stat.y_resolution); ast_debug(1, " Transfer Rate: %d\n", stat.bit_rate); - ast_manager_event(s->chan, EVENT_FLAG_CALL, - s->direction ? "FaxSent" : "FaxReceived", - "Channel: %s\r\n" - "Exten: %s\r\n" - "CallerID: %s\r\n" - "CallerIDName: %s\r\n" - "ConnectedLineNum: %s\r\n" - "ConnectedLineName: %s\r\n" - "RemoteStationID: %s\r\n" - "LocalStationID: %s\r\n" - "PagesTransferred: %d\r\n" - "Resolution: %d\r\n" - "TransferRate: %d\r\n" - "FileName: %s\r\n", - ast_channel_name(s->chan), - ast_channel_exten(s->chan), - S_COR(ast_channel_caller(s->chan)->id.number.valid, ast_channel_caller(s->chan)->id.number.str, ""), - S_COR(ast_channel_caller(s->chan)->id.name.valid, ast_channel_caller(s->chan)->id.name.str, ""), - S_COR(ast_channel_connected(s->chan)->id.number.valid, ast_channel_connected(s->chan)->id.number.str, ""), - S_COR(ast_channel_connected(s->chan)->id.name.valid, ast_channel_connected(s->chan)->id.name.str, ""), - far_ident, - local_ident, - pages_transferred, - stat.y_resolution, - stat.bit_rate, - s->file_name); + json_filenames = ast_json_pack("[s]", s->file_name); + if (!json_filenames) { + return; + } + ast_json_ref(json_filenames); + json_object = ast_json_pack("{s: s, s: s, s: s, s: i, s: i, s: i, s: o}", + "type", s->direction ? "send" : "receive", + "remote_station_id", far_ident, + "local_station_id", local_ident, + "fax_pages", pages_transferred, + "fax_resolution", stat.y_resolution, + "fax_bitrate", stat.bit_rate, + "filenames", json_filenames); + message = ast_channel_cached_blob_create(s->chan, ast_channel_fax_type(), json_object); + if (!message) { + return; + } + stasis_publish(ast_channel_topic(s->chan), message); } /* === Helper functions to configure fax === */ diff --git a/apps/app_minivm.c b/apps/app_minivm.c index 53c5f0937..ba6d6e5a2 100644 --- a/apps/app_minivm.c +++ b/apps/app_minivm.c @@ -166,14 +166,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/say.h" #include "asterisk/module.h" #include "asterisk/app.h" -#include "asterisk/manager.h" #include "asterisk/dsp.h" #include "asterisk/localtime.h" #include "asterisk/cli.h" #include "asterisk/utils.h" #include "asterisk/linkedlists.h" #include "asterisk/callerid.h" -#include "asterisk/event.h" +#include "asterisk/stasis.h" +#include "asterisk/stasis_channels.h" +#include "asterisk/json.h" /*** DOCUMENTATION <application name="MinivmRecord" language="en_US"> @@ -495,7 +496,23 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <ref type="function">MINIVMCOUNTER</ref> </see-also> </function> - + <managerEvent language="en_US" name="MiniVoiceMail"> + <managerEventInstance class="EVENT_FLAG_CALL"> + <synopsis>Raised when a notification is sent out by a MiniVoiceMail application</synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> + <parameter name="Action"> + <para>What action was taken. Currently, this will always be <literal>SentNotification</literal></para> + </parameter> + <parameter name="Mailbox"> + <para>The mailbox that the notification was about, specified as <literal>mailbox</literal>@<literal>context</literal></para> + </parameter> + <parameter name="Counter"> + <para>A message counter derived from the <literal>MVM_COUNTER</literal> channel variable.</para> + </parameter> + </syntax> + </managerEventInstance> + </managerEvent> ***/ #ifndef TRUE @@ -1761,6 +1778,9 @@ static void run_externnotify(struct ast_channel *chan, struct minivm_account *vm * \brief Send message to voicemail account owner */ static int notify_new_message(struct ast_channel *chan, const char *templatename, struct minivm_account *vmu, const char *filename, long duration, const char *format, char *cidnum, char *cidname) { + RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref); + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + RAII_VAR(struct ast_mwi_state *, mwi_state, NULL, ao2_cleanup); char *stringp; struct minivm_template *etemplate; char *messageformat; @@ -1826,8 +1846,26 @@ static int notify_new_message(struct ast_channel *chan, const char *templatename res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->attachment, MVM_MESSAGE_PAGE, counter); } - ast_manager_event(chan, EVENT_FLAG_CALL, "MiniVoiceMail", "Action: SentNotification\rn\nMailbox: %s@%s\r\nCounter: %s\r\n", vmu->username, vmu->domain, counter); + mwi_state = ast_mwi_create(vmu->username, vmu->domain); + if (!mwi_state) { + goto notify_cleanup; + } + mwi_state->snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan)); + + json_object = ast_json_pack("{s: s, s: s}", + "Event", "MiniVoiceMail" + "Action", "SentNotification", + "Counter", counter); + if (!json_object) { + goto notify_cleanup; + } + message = ast_mwi_blob_create(mwi_state, ast_mwi_vm_app_type(), json_object); + if (!message) { + goto notify_cleanup; + } + stasis_publish(ast_mwi_topic(mwi_state->uniqueid), message); +notify_cleanup: run_externnotify(chan, vmu); /* Run external notification */ if (etemplate->locale) { @@ -2011,7 +2049,7 @@ static int leave_voicemail(struct ast_channel *chan, char *username, struct leav /*!\internal * \brief Queue a message waiting event */ -static void queue_mwi_event(const char *mbx, const char *ctx, int urgent, int new, int old) +static void queue_mwi_event(const char *channel_id, const char *mbx, const char *ctx, int urgent, int new, int old) { char *mailbox, *context; @@ -2021,7 +2059,7 @@ static void queue_mwi_event(const char *mbx, const char *ctx, int urgent, int ne context = "default"; } - stasis_publish_mwi_state(mailbox, context, new + urgent, old); + ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id); } /*!\internal @@ -2056,7 +2094,7 @@ static int minivm_mwi_exec(struct ast_channel *chan, const char *data) ast_log(LOG_ERROR, "Need mailbox@context as argument. Sorry. Argument 0 %s\n", argv[0]); return -1; } - queue_mwi_event(mailbox, domain, atoi(argv[1]), atoi(argv[2]), atoi(argv[3])); + queue_mwi_event(ast_channel_uniqueid(chan), mailbox, domain, atoi(argv[1]), atoi(argv[2]), atoi(argv[3])); return res; } @@ -2078,7 +2116,6 @@ static int minivm_notify_exec(struct ast_channel *chan, const char *data) const char *filename; const char *format; const char *duration_string; - if (ast_strlen_zero(data)) { ast_log(LOG_ERROR, "Minivm needs at least an account argument \n"); return -1; diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index b3ceeebc9..90458bb31 100644 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -7741,7 +7741,7 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, return cmd; } -static void queue_mwi_event(const char *box, int urgent, int new, int old) +static void queue_mwi_event(const char *channel_id, const char *box, int urgent, int new, int old) { char *mailbox, *context; @@ -7752,7 +7752,7 @@ static void queue_mwi_event(const char *box, int urgent, int new, int old) context = "default"; } - stasis_publish_mwi_state(mailbox, context, new + urgent, old); + ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id); } /*! @@ -7842,32 +7842,7 @@ static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, if (ast_app_has_voicemail(ext_context, NULL)) ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs); - queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs); - - /*** DOCUMENTATION - <managerEventInstance> - <synopsis>Raised when a new message has been left in a voicemail mailbox.</synopsis> - <syntax> - <parameter name="Mailbox"> - <para>The mailbox with the new message, specified as <emphasis>mailbox</emphasis>@<emphasis>context</emphasis></para> - </parameter> - <parameter name="Waiting"> - <para>Whether or not the mailbox has access to a voicemail application.</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> - </managerEventInstance> - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", - "Mailbox: %s@%s\r\n" - "Waiting: %d\r\n" - "New: %d\r\n" - "Old: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs); + queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgentmsgs, newmsgs, oldmsgs); run_externnotify(vmu->context, vmu->mailbox, flag); #ifdef IMAP_STORAGE @@ -11538,16 +11513,10 @@ out: if (valid) { int new = 0, old = 0, urgent = 0; snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context); - /*** DOCUMENTATION - <managerEventInstance> - <synopsis>Raised when a user has finished listening to their messages.</synopsis> - </managerEventInstance> - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL)); /* Urgent flag not passwd to externnotify here */ run_externnotify(vmu->context, vmu->mailbox, NULL); ast_app_inboxcount2(ext_context, &urgent, &new, &old); - queue_mwi_event(ext_context, urgent, new, old); + queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgent, new, old); } #ifdef IMAP_STORAGE /* expunge message - use UID Expunge if supported on IMAP server*/ @@ -11766,7 +11735,7 @@ static int append_mailbox(const char *context, const char *box, const char *data strcat(mailbox_full, context); inboxcount2(mailbox_full, &urgent, &new, &old); - queue_mwi_event(mailbox_full, urgent, new, old); + queue_mwi_event(NULL, mailbox_full, urgent, new, old); return 0; } @@ -12502,7 +12471,7 @@ static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub) mwi_sub->old_urgent = urgent; mwi_sub->old_new = new; mwi_sub->old_old = old; - queue_mwi_event(mwi_sub->mailbox, urgent, new, old); + queue_mwi_event(NULL, mwi_sub->mailbox, urgent, new, old); run_externnotify(NULL, mwi_sub->mailbox, NULL); } } @@ -12647,7 +12616,7 @@ static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct } change = stasis_message_data(msg); - if (change->topic == stasis_mwi_topic_all()) { + if (change->topic == ast_mwi_topic_all()) { return; } @@ -12668,10 +12637,10 @@ static int dump_cache(void *obj, void *arg, int flags) static void start_poll_thread(void) { int errcode; - mwi_sub_sub = stasis_subscribe(stasis_mwi_topic_all(), mwi_event_cb, NULL); + mwi_sub_sub = stasis_subscribe(ast_mwi_topic_all(), mwi_event_cb, NULL); if (mwi_sub_sub) { - struct ao2_container *cached = stasis_cache_dump(stasis_mwi_topic_cached(), stasis_subscription_change_type()); + struct ao2_container *cached = stasis_cache_dump(ast_mwi_topic_cached(), stasis_subscription_change_type()); if (cached) { ao2_callback(cached, OBJ_MULTIPLE | OBJ_NODATA, dump_cache, NULL); } @@ -15263,7 +15232,7 @@ static void notify_new_state(struct ast_vm_user *vmu) snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context); run_externnotify(vmu->context, vmu->mailbox, NULL); ast_app_inboxcount2(ext_context, &urgent, &new, &old); - queue_mwi_event(ext_context, urgent, new, old); + queue_mwi_event(NULL, ext_context, urgent, new, old); } static int vm_msg_forward(const char *from_mailbox, diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 1f9ee5a56..03279a01e 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -3762,7 +3762,7 @@ static void notify_message(char *mailbox_full, int thereornot) if (ast_strlen_zero(context)) context = "default"; - stasis_publish_mwi_state(mailbox, context, thereornot, thereornot); + ast_publish_mwi_state(mailbox, context, thereornot, thereornot); if (!ast_strlen_zero(mailbox) && !ast_strlen_zero(mwimonitornotify)) { snprintf(s, sizeof(s), "%s %s %d", mwimonitornotify, mailbox, thereornot); @@ -5427,10 +5427,10 @@ static int has_voicemail(struct dahdi_pvt *p) } ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context); - mwi_message = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), ast_str_buffer(uniqueid)); + mwi_message = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), ast_str_buffer(uniqueid)); if (mwi_message) { - struct stasis_mwi_state *mwi_state = stasis_message_data(mwi_message); + struct ast_mwi_state *mwi_state = stasis_message_data(mwi_message); new_msgs = mwi_state->new_msgs; } else { new_msgs = ast_app_has_voicemail(p->mailbox, NULL); @@ -13235,7 +13235,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context); - mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid)); + mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid)); if (mailbox_specific_topic) { tmp->mwi_event_sub = stasis_subscribe(mailbox_specific_topic, mwi_event_cb, NULL); } diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 49ce66cb7..7f98159b2 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -8772,10 +8772,10 @@ static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, i } ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context); - msg = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), ast_str_buffer(uniqueid)); + msg = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), ast_str_buffer(uniqueid)); if (msg) { - struct stasis_mwi_state *mwi_state = stasis_message_data(msg); + struct ast_mwi_state *mwi_state = stasis_message_data(msg); new = mwi_state->new_msgs; old = mwi_state->old_msgs; } else { /* Fall back on checking the mailbox directly */ @@ -12555,7 +12555,7 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context); - mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid)); + mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid)); if (mailbox_specific_topic) { peer->mwi_event_sub = stasis_subscribe(mailbox_specific_topic, mwi_event_cb, NULL); } diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c index 1f0830762..c6b441049 100644 --- a/channels/chan_mgcp.c +++ b/channels/chan_mgcp.c @@ -507,10 +507,10 @@ static int has_voicemail(struct mgcp_endpoint *p) ast_str_set(&uniqueid, 0, "%s@%s", mbox, cntx); - msg = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), ast_str_buffer(uniqueid)); + msg = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), ast_str_buffer(uniqueid)); if (msg) { - struct stasis_mwi_state *mwi_state = stasis_message_data(msg); + struct ast_mwi_state *mwi_state = stasis_message_data(msg); new_msgs = mwi_state->new_msgs; } else { new_msgs = ast_app_has_voicemail(p->mailbox, NULL); @@ -4169,7 +4169,7 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v) ast_str_reset(uniqueid); ast_str_set(&uniqueid, 0, "%s@%s", mbox, cntx); - mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid)); + mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid)); if (mailbox_specific_topic) { e->mwi_event_sub = stasis_subscribe(mailbox_specific_topic, mwi_event_cb, NULL); } diff --git a/channels/chan_sip.c b/channels/chan_sip.c index f7a528b68..77cd9c253 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -16724,7 +16724,7 @@ static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct ao2_cleanup(peer); return; } - if (stasis_mwi_state_type() == stasis_message_type(msg)) { + if (ast_mwi_state_type() == stasis_message_type(msg)) { sip_send_mwi_to_peer(peer, 0); } } @@ -24767,7 +24767,7 @@ static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, str char *old = strsep(&c, " "); char *new = strsep(&old, "/"); - stasis_publish_mwi_state(mailbox, "SIP_Remote", atoi(new), atoi(old)); + ast_publish_mwi_state(mailbox, "SIP_Remote", atoi(new), atoi(old)); transmit_response(p, "200 OK", req); } else { @@ -27420,7 +27420,7 @@ static void add_peer_mwi_subs(struct sip_peer *peer) ast_str_reset(uniqueid); ast_str_set(&uniqueid, 0, "%s@%s", mailbox->mailbox, S_OR(mailbox->context, "default")); - mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid)); + mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid)); if (mailbox_specific_topic) { ao2_ref(peer, +1); mailbox->event_sub = stasis_subscribe(mailbox_specific_topic, mwi_event_cb, peer); @@ -28630,12 +28630,12 @@ static int get_cached_mwi(struct sip_peer *peer, int *new, int *old) in_cache = 0; AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) { RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); - struct stasis_mwi_state *mwi_state; + struct ast_mwi_state *mwi_state; ast_str_reset(uniqueid); ast_str_set(&uniqueid, 0, "%s@%s", mailbox->mailbox, S_OR(mailbox->context, "default")); - msg = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), ast_str_buffer(uniqueid)); + msg = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), ast_str_buffer(uniqueid)); if (!msg) { continue; } diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index cd194d5a8..f1398049b 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -3535,8 +3535,8 @@ static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct return; } - if (msg && stasis_mwi_state_type() == stasis_message_type(msg)) { - struct stasis_mwi_state *mwi_state = stasis_message_data(msg); + if (msg && ast_mwi_state_type() == stasis_message_type(msg)) { + struct ast_mwi_state *mwi_state = stasis_message_data(msg); l->newmsgs = mwi_state->new_msgs; } @@ -8214,7 +8214,7 @@ static struct skinny_line *config_line(const char *lname, struct ast_variable *v ast_str_set(&uniqueid, 0, "%s@%s", cfg_mailbox, cfg_context); - mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid)); + mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid)); if (mailbox_specific_topic) { l->mwi_event_sub = stasis_subscribe(mailbox_specific_topic, mwi_event_cb, l); } diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c index 88332ec9a..42a71bac3 100644 --- a/channels/chan_unistim.c +++ b/channels/chan_unistim.c @@ -5508,10 +5508,10 @@ static int unistim_send_mwi_to_peer(struct unistim_line *peer, unsigned int tick ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context); - msg = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), ast_str_buffer(uniqueid)); + msg = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), ast_str_buffer(uniqueid)); if (msg) { - struct stasis_mwi_state *mwi_state = stasis_message_data(msg); + struct ast_mwi_state *mwi_state = stasis_message_data(msg); new = mwi_state->new_msgs; } else { /* Fall back on checking the mailbox directly */ new = ast_app_has_voicemail(peer->mailbox, "INBOX"); diff --git a/channels/sig_pri.c b/channels/sig_pri.c index 353b7f39b..db0c92043 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -8768,9 +8768,9 @@ static void sig_pri_mwi_event_cb(void *userdata, struct stasis_subscription *sub const char *mbox_number; int num_messages; int idx; - struct stasis_mwi_state *mwi_state; + struct ast_mwi_state *mwi_state; - if (stasis_mwi_state_type() != stasis_message_type(msg)) { + if (ast_mwi_state_type() != stasis_message_type(msg)) { return; } @@ -8816,7 +8816,7 @@ static void sig_pri_mwi_cache_update(struct sig_pri_span *pri) { int idx; struct ast_str *uniqueid = ast_str_alloca(AST_MAX_MAILBOX_UNIQUEID); - struct stasis_mwi_state *mwi_state; + struct ast_mwi_state *mwi_state; for (idx = 0; idx < ARRAY_LEN(pri->mbox); ++idx) { RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); @@ -8828,7 +8828,7 @@ static void sig_pri_mwi_cache_update(struct sig_pri_span *pri) ast_str_reset(uniqueid); ast_str_set(&uniqueid, 0, "%s@%s", pri->mbox[idx].number, pri->mbox[idx].context); - msg = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), ast_str_buffer(uniqueid)); + msg = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), ast_str_buffer(uniqueid)); if (!msg) { /* No cached event for this mailbox. */ continue; @@ -9002,7 +9002,7 @@ int sig_pri_start_pri(struct sig_pri_span *pri) ast_str_set(&mwi_description, -1, "%s span %d[%d] MWI mailbox %s@%s", sig_pri_cc_type_name, pri->span, i, mbox_number, mbox_context); - mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid)); + mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid)); if (mailbox_specific_topic) { pri->mbox[i].sub = stasis_subscribe(mailbox_specific_topic, sig_pri_mwi_event_cb, pri); } diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h index 3fe35e58c..1d0844924 100644 --- a/include/asterisk/_private.h +++ b/include/asterisk/_private.h @@ -52,6 +52,20 @@ void ast_msg_shutdown(void); /*!< Provided by message.c */ int aco_init(void); /*!< Provided by config_options.c */ /*! + * \since 12 + * \brief Possible return types for \ref ast_module_reload + */ +enum ast_module_reload_result { + AST_MODULE_RELOAD_SUCCESS = 0, /*!< The module was reloaded succesfully */ + AST_MODULE_RELOAD_QUEUED, /*!< The module reload request was queued */ + AST_MODULE_RELOAD_NOT_FOUND, /*!< The requested module was not found */ + AST_MODULE_RELOAD_ERROR, /*!< An error occurred while reloading the module */ + AST_MODULE_RELOAD_IN_PROGRESS, /*!< A module reload request is already in progress */ + AST_MODULE_RELOAD_UNINITIALIZED, /*!< The module has not been initialized */ + AST_MODULE_RELOAD_NOT_IMPLEMENTED, /*!< This module doesn't support reloading */ +}; + +/*! * \brief Initialize the bridging system. * \since 12.0.0 * @@ -78,13 +92,10 @@ int ast_local_init(void); * * \note Modules are reloaded using their reload() functions, not unloading * them and loading them again. - * - * \return 0 if the specified module was not found. - * \retval 1 if the module was found but cannot be reloaded. - * \retval -1 if a reload operation is already in progress. - * \retval 2 if the specfied module was found and reloaded. + * + * \retval The \ref ast_module_reload_result status of the module load request */ -int ast_module_reload(const char *name); +enum ast_module_reload_result ast_module_reload(const char *name); /*! * \brief Process reload requests received during startup. diff --git a/include/asterisk/app.h b/include/asterisk/app.h index 6cfb38004..85c2aefb1 100644 --- a/include/asterisk/app.h +++ b/include/asterisk/app.h @@ -1104,8 +1104,8 @@ void ast_safe_fork_cleanup(void); int ast_app_parse_timelen(const char *timestr, int *result, enum ast_timelen defunit); /*! + * \since 12 * \brief Publish a MWI state update via stasis - * \param[in] uniqueid A unique identifier for this mailbox (usually mailbox@context) * \param[in] mailbox The number identifying this mailbox * \param[in] context The context this mailbox resides in * \param[in] new_msgs The number of new messages in this mailbox @@ -1114,26 +1114,44 @@ int ast_app_parse_timelen(const char *timestr, int *result, enum ast_timelen def * \retval -1 Failure * \since 12 */ -#define stasis_publish_mwi_state(mailbox, context, new_msgs, old_msgs) \ - stasis_publish_mwi_state_full(mailbox, context, new_msgs, old_msgs, NULL) +#define ast_publish_mwi_state(mailbox, context, new_msgs, old_msgs) \ + ast_publish_mwi_state_full(mailbox, context, new_msgs, old_msgs, NULL, NULL) + +/*! + * \since 12 + * \brief Publish a MWI state update associated with some channel + * \param[in] mailbox The number identifying this mailbox + * \param[in] context The context this mailbox resides in + * \param[in] new_msgs The number of new messages in this mailbox + * \param[in] old_msgs The number of old messages in this mailbox + * \param[in] channel_id A unique identifier for a channel associated with this + * change in mailbox state + * \retval 0 Success + * \retval -1 Failure + * \since 12 + */ +#define ast_publish_mwi_state_channel(mailbox, context, new_msgs, old_msgs, channel_id) \ + ast_publish_mwi_state_full(mailbox, context, new_msgs, old_msgs, channel_id, NULL) /*! - * \brief Publish a MWI state update via stasis with EID - * \param[in] uniqueid A unique identifier for this mailbox (usually mailbox@context) + * \since 12 + * \brief Publish a MWI state update via stasis with all parameters * \param[in] mailbox The number identifying this mailbox * \param[in] context The context this mailbox resides in * \param[in] new_msgs The number of new messages in this mailbox * \param[in] old_msgs The number of old messages in this mailbox + * \param[in] channel_id A unique identifier for a channel associated with this * \param[in] eid The EID of the server that originally published the message * \retval 0 Success * \retval -1 Failure * \since 12 */ -int stasis_publish_mwi_state_full( +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); /*! \addtogroup StasisTopicsAndMessages @@ -1144,49 +1162,103 @@ int stasis_publish_mwi_state_full( * \brief The structure that contains MWI state * \since 12 */ -struct stasis_mwi_state { +struct ast_mwi_state { AST_DECLARE_STRING_FIELDS( - AST_STRING_FIELD(uniqueid); /*!< Unique identifier for this mailbox/context */ - AST_STRING_FIELD(mailbox); /*!< Mailbox for this event */ - AST_STRING_FIELD(context); /*!< Context that this mailbox belongs to */ + AST_STRING_FIELD(uniqueid); /*!< Unique identifier for this mailbox/context */ + AST_STRING_FIELD(mailbox); /*!< Mailbox for this event */ + AST_STRING_FIELD(context); /*!< Context that this mailbox belongs to */ ); - int new_msgs; /*!< The current number of new messages for this mailbox */ - int old_msgs; /*!< The current number of old messages for this mailbox */ - struct ast_eid eid; /*!< The EID of the server where this message originated */ + int new_msgs; /*!< The current number of new messages for this mailbox */ + int old_msgs; /*!< The current number of old messages for this mailbox */ + /*! If applicable, a snapshot of the channel that caused this MWI change */ + struct ast_channel_snapshot *snapshot; + struct ast_eid eid; /*!< The EID of the server where this message originated */ }; /*! - * \brief Get the Stasis topic for MWI messages + * \brief Object that represents an MWI update with some additional application + * defined data + */ +struct ast_mwi_blob { + struct ast_mwi_state *mwi_state; /*!< MWI state */ + struct ast_json *blob; /*!< JSON blob of data */ +}; + +/*! + * \since 12 + * \brief Create a \ref ast_mwi_state object + * + * \retval \ref ast_mwi_state object on success + * \retval NULL on error + */ +struct ast_mwi_state *ast_mwi_create(const char *mailbox, const char *context); + +/*! + * \since 12 + * \brief Creates a \ref ast_mwi_blob message. + * + * The \a blob JSON object requires a \c "type" field describing the blob. It + * should also be treated as immutable and not modified after it is put into the + * message. + * + * \param mwi_state MWI state associated with the update + * \param message_type The type of message to create + * \param blob JSON object representing the data. + * \return \ref ast_mwi_blob message. + * \return \c NULL on error + */ +struct stasis_message *ast_mwi_blob_create(struct ast_mwi_state *mwi_state, + struct stasis_message_type *message_type, + struct ast_json *blob); + +/*! + * \brief Get the \ref stasis topic for MWI messages * \retval The topic structure for MWI messages * \retval NULL if it has not been allocated * \since 12 */ -struct stasis_topic *stasis_mwi_topic_all(void); +struct stasis_topic *ast_mwi_topic_all(void); /*! - * \brief Get the Stasis topic for MWI messages on a unique ID + * \brief Get the \ref stasis topic for MWI messages on a unique ID * \param uniqueid The unique id for which to get the topic * \retval The topic structure for MWI messages for a given uniqueid * \retval NULL if it failed to be found or allocated * \since 12 */ -struct stasis_topic *stasis_mwi_topic(const char *uniqueid); +struct stasis_topic *ast_mwi_topic(const char *uniqueid); /*! - * \brief Get the Stasis caching topic for MWI messages + * \brief Get the \ref stasis caching topic for MWI messages * \retval The caching topic structure for MWI messages * \retval NULL if it has not been allocated * \since 12 */ -struct stasis_caching_topic *stasis_mwi_topic_cached(void); +struct stasis_caching_topic *ast_mwi_topic_cached(void); /*! - * \brief Get the Stasis message type for MWI messages + * \brief Get the \ref stasis message type for MWI messages * \retval The message type structure for MWI messages - * \retval NULL if it has not been allocated + * \retval NULL on error + * \since 12 + */ +struct stasis_message_type *ast_mwi_state_type(void); + +/*! + * \brief Get the \ref stasis message type for voicemail application specific messages + * + * This message type exists for those messages a voicemail application may wish to send + * that have no logical relationship with other voicemail applications. Voicemail apps + * that use this message type must pass a \ref ast_mwi_blob. Any extraneous information + * in the JSON blob must be packed as key/value pair tuples of strings. + * + * At least one key/value tuple must have a key value of "Event". + * + * \retval The \ref stasis_message_type for voicemail application specific messages + * \retval NULL on error * \since 12 */ -struct stasis_message_type *stasis_mwi_state_type(void); +struct stasis_message_type *ast_mwi_vm_app_type(void); /*! @} */ diff --git a/include/asterisk/json.h b/include/asterisk/json.h index 978d6396a..baf8cf6a2 100644 --- a/include/asterisk/json.h +++ b/include/asterisk/json.h @@ -125,6 +125,8 @@ struct ast_json *ast_json_ref(struct ast_json *value); /*! * \brief Decrease refcount on \a value. If refcount reaches zero, \a value is freed. * \since 12.0.0 + * + * \note It is safe to pass \c NULL to this function. */ void ast_json_unref(struct ast_json *value); @@ -602,6 +604,15 @@ struct ast_json_iter *ast_json_object_iter_next(struct ast_json *object, struct const char *ast_json_object_iter_key(struct ast_json_iter *iter); /*! + * \brief Retrieve the iterator object for a particular key + * \since 12.0.0 + * + * \param key Key of the field the \c ast_json_iter points to + * \return \ref ast_json_iter object that points to \a key + */ +struct ast_json_iter *ast_json_object_key_to_iter(const char *key); + +/*! * \brief Get the value from an iterator. * \since 12.0.0 * @@ -628,6 +639,23 @@ struct ast_json *ast_json_object_iter_value(struct ast_json_iter *iter); */ int ast_json_object_iter_set(struct ast_json *object, struct ast_json_iter *iter, struct ast_json *value); +/*! + * \brief Iterate over key/value pairs + * + * \note This is a reproduction of the jansson library's \ref json_object_foreach + * using the equivalent ast_* wrapper functions. This creates a for loop using the various + * iteration function calls. + * + * \param object The \ref ast_json object that contains key/value tuples to iterate over + * \param key A \c const char pointer key for the key/value tuple + * \param value A \ref ast_json object for the key/value tuple + */ +#define ast_json_object_foreach(object, key, value) \ + for (key = ast_json_object_iter_key(ast_json_object_iter(object)); \ + key && (value = ast_json_object_iter_value(ast_json_object_key_to_iter(key))); \ + key = ast_json_object_iter_key(ast_json_object_iter_next(object, ast_json_object_key_to_iter(key)))) + + /*!@}*/ /*!@{*/ diff --git a/include/asterisk/manager.h b/include/asterisk/manager.h index 4e9b8d14a..6b1402bc3 100644 --- a/include/asterisk/manager.h +++ b/include/asterisk/manager.h @@ -330,7 +330,7 @@ struct ast_channel_snapshot; * \retval NULL on error * \retval ast_str* on success (must be ast_freed by caller) */ -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); @@ -351,6 +351,32 @@ struct ast_str *ast_manager_build_channel_state_string( struct ast_bridge_snapshot; /*! + * \since 12 + * \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); + +struct ast_json; + +/*! + * \since 12 + * \brief Convert a JSON object into an AMI compatible string + * + * \param blob The JSON blob containing key/value pairs to convert + * \param exclusion_cb A \ref key_exclusion_cb pointer to a function that will exclude + * keys from the final AMI string + * + * \retval A malloc'd \ref ast_str object. Callers of this function should free + * the returned \ref ast_str object + * \retval NULL on error + */ +struct ast_str *ast_manager_str_from_json_object(struct ast_json *blob, key_exclusion_cb exclusion_cb); + +/*! * \brief Generate the AMI message body from a bridge snapshot * \since 12 * @@ -398,13 +424,21 @@ ast_manager_event_blob_create( /*! * \brief Initialize support for AMI channel events. - * \return 0 on success. - * \return non-zero on error. + * \retval 0 on success. + * \retval non-zero on error. * \since 12 */ int manager_channels_init(void); /*! + * \since 12 + * \brief Initialize support for AMI MWI events. + * \retval 0 on success + * \retval non-zero on error + */ +int manager_mwi_init(void); + +/*! * \brief Initialize support for AMI channel events. * \return 0 on success. * \return non-zero on error. @@ -412,4 +446,54 @@ int manager_channels_init(void); */ int manager_bridging_init(void); +/*! + * \since 12 + * \brief Get the \ref stasis_message_type for generic messages + * + * A generic AMI message expects a JSON only payload. The payload must have the following + * structure: + * {type: s, class_type: i, event: [ {s: s}, ...] } + * + * - type is the AMI event type + * - class_type is the class authorization type for the event + * - event is a list of key/value tuples to be sent out in the message + * + * \retval A \ref stasis_message_type for AMI messages + */ +struct stasis_message_type *ast_manager_get_generic_type(void); + +/*! + * \since 12 + * \brief Get the \ref stasis topic for AMI + * + * \retval The \ref stasis topic for AMI + * \retval NULL on error + */ +struct stasis_topic *ast_manager_get_topic(void); + +struct ast_json; + +/*! + * \since 12 + * \brief Publish a generic \ref stasis_message_type to the \ref stasis_topic for AMI + * + * Publishes a message to the \ref stasis message bus solely for the consumption of AMI. + * The message will be of the type provided by \ref ast_manager_get_type, and will be + * published to the topic provided by \ref ast_manager_get_topic. As such, the JSON must + * be constructed as defined by the \ref ast_manager_get_type message. + * + * \retval 0 on success + * \retval -1 on failure + */ +int ast_manager_publish_message(struct ast_json *json); + +/*! + * \since 12 + * \brief Get the \ref stasis_message_router for AMI + * + * \retval The \ref stasis_message_router for AMI + * \retval NULL on error + */ +struct stasis_message_router *ast_manager_get_message_router(void); + #endif /* _ASTERISK_MANAGER_H */ diff --git a/include/asterisk/stasis_channels.h b/include/asterisk/stasis_channels.h index dace99af5..7c214d5a7 100644 --- a/include/asterisk/stasis_channels.h +++ b/include/asterisk/stasis_channels.h @@ -125,14 +125,15 @@ struct ast_channel_snapshot *ast_channel_snapshot_create( /*! * \since 12 - * \brief Get the most recent snapshot for channel with the given \a uniqueid. + * \brief Obtain the latest \ref ast_channel_snapshot from the \ref stasis cache. This is + * an ao2 object, so use \ref ao2_cleanup() to deallocate. * - * \param uniqueid Uniqueid of the snapshot to fetch. - * \return Most recent channel snapshot - * \return \c NULL on error + * \param unique_id The channel's unique ID + * + * \retval A \ref ast_channel_snapshot on success + * \retval NULL on error */ -struct ast_channel_snapshot *ast_channel_snapshot_get_latest( - const char *uniqueid); +struct ast_channel_snapshot *ast_channel_snapshot_get_latest(const char *uniqueid); /*! * \since 12 @@ -154,6 +155,27 @@ struct stasis_message *ast_channel_blob_create(struct ast_channel *chan, /*! * \since 12 + * \brief Creates a \ref ast_channel_blob message using the current cached + * \ref ast_channel_snapshot for the passed in \ref ast_channel + * + * The given \a blob should be treated as immutable and not modified after it is + * put into the message. + * + * \param chan Channel blob is associated with, or \c NULL for global/all channels. + * \param type Message type for this blob. + * \param blob JSON object representing the data, or \c NULL for no data. If + * \c NULL, ast_json_null() is put into the object. + * + * \param chan Channel blob is associated with + * \param blob JSON object representing the data. + * \return \ref ast_channel_blob message. + * \return \c NULL on error + */ +struct stasis_message *ast_channel_cached_blob_create(struct ast_channel *chan, + struct stasis_message_type *type, struct ast_json *blob); + +/*! + * \since 12 * \brief Create a \ref ast_channel_blob message, pulling channel state from * the cache. * @@ -319,6 +341,70 @@ struct stasis_message_type *ast_channel_dtmf_end_type(void); /*! * \since 12 + * \brief Message type for when a channel starts spying on another channel + * + * \retval A stasis message type + */ +struct stasis_message_type *ast_channel_chanspy_start_type(void); + +/*! + * \since 12 + * \brief Message type for when a channel stops spying on another channel + * + * \retval A stasis message type + */ +struct stasis_message_type *ast_channel_chanspy_stop_type(void); + +/*! + * \since 12 + * \brief Message type for a fax operation + * + * \retval A stasis message type + */ +struct stasis_message_type *ast_channel_fax_type(void); + +/*! + * \since 12 + * \brief Message type for hangup handler related actions + * + * \retval A stasis message type + */ +struct stasis_message_type *ast_channel_hangup_handler_type(void); + +/*! + * \since 12 + * \brief Message type for starting monitor on a channel + * + * \retval A stasis message type + */ +struct stasis_message_type *ast_channel_monitor_start_type(void); + +/*! + * \since 12 + * \brief Message type for stopping monitor on a channel + * + * \retval A stasis message type + */ +struct stasis_message_type *ast_channel_monitor_stop_type(void); + +/*! + * \since 12 + * \brief Message type for starting music on hold on a channel + * + * \retval A stasis message type + */ +struct stasis_message_type *ast_channel_moh_start_type(void); + +/*! + * \since 12 + * \brief Message type for stopping music on hold on a channel + * + * \retval A stasis message type + */ +struct stasis_message_type *ast_channel_moh_stop_type(void); + +/*! + * \since 12 * \brief Publish in the \ref ast_channel_topic or \ref ast_channel_topic_all * topics a stasis message for the channels involved in a dial operation. * 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); } diff --git a/res/parking/parking_manager.c b/res/parking/parking_manager.c index d6a05573f..697468eb3 100644 --- a/res/parking/parking_manager.c +++ b/res/parking/parking_manager.c @@ -277,14 +277,14 @@ static struct ast_str *manager_build_parked_call_string(const struct ast_parked_ return NULL; } - parkee_string = ast_manager_build_channel_state_string_suffix(payload->parkee, "Parkee"); + parkee_string = ast_manager_build_channel_state_string_prefix(payload->parkee, "Parkee"); if (payload->parker) { - parker_string = ast_manager_build_channel_state_string_suffix(payload->parker, "Parker"); + parker_string = ast_manager_build_channel_state_string_prefix(payload->parker, "Parker"); } if (payload->retriever) { - retriever_string = ast_manager_build_channel_state_string_suffix(payload->retriever, "Retriever"); + retriever_string = ast_manager_build_channel_state_string_prefix(payload->retriever, "Retriever"); } ast_str_set(&out, 0, diff --git a/res/res_fax.c b/res/res_fax.c index 9afad4a19..4b8c9d7ca 100644 --- a/res/res_fax.c +++ b/res/res_fax.c @@ -83,11 +83,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/file.h" #include "asterisk/channel.h" #include "asterisk/pbx.h" -#include "asterisk/manager.h" #include "asterisk/dsp.h" #include "asterisk/indications.h" #include "asterisk/ast_version.h" #include "asterisk/translate.h" +#include "asterisk/stasis.h" +#include "asterisk/stasis_channels.h" /*** DOCUMENTATION <application name="ReceiveFAX" language="en_US" module="res_fax"> @@ -384,12 +385,6 @@ AST_APP_OPTIONS(fax_exec_options, BEGIN_OPTIONS AST_APP_OPTION('z', OPT_REQUEST_T38), END_OPTIONS); -struct manager_event_info { - char context[AST_MAX_CONTEXT]; - char exten[AST_MAX_EXTENSION]; - char cid[128]; -}; - static void debug_check_frame_for_silence(struct ast_fax_session *s, unsigned int c2s, struct ast_frame *frame) { struct debug_info_history *history = c2s ? &s->debug_info->c2s : &s->debug_info->s2c; @@ -1091,13 +1086,39 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d return s; } -static void get_manager_event_info(struct ast_channel *chan, struct manager_event_info *info) +/*! + * \internal + * \brief Convert the filenames in a fax session into a JSON array + * \retval NULL on error + * \retval A \ref ast_json array on success + */ +static struct ast_json *generate_filenames_json(struct ast_fax_session_details *details) { - pbx_substitute_variables_helper(chan, "${CONTEXT}", info->context, sizeof(info->context)); - pbx_substitute_variables_helper(chan, "${EXTEN}", info->exten, sizeof(info->exten)); - pbx_substitute_variables_helper(chan, "${CALLERID(num)}", info->cid, sizeof(info->cid)); -} + RAII_VAR(struct ast_json *, json_array, ast_json_array_create(), ast_json_unref); + struct ast_fax_document *doc; + + if (!details || !json_array) { + return NULL; + } + + /* don't process empty lists */ + if (AST_LIST_EMPTY(&details->documents)) { + return NULL; + } + + AST_LIST_TRAVERSE(&details->documents, doc, next) { + struct ast_json *entry = ast_json_string_create(doc->filename); + if (!entry) { + return NULL; + } + if (ast_json_array_append(json_array, entry)) { + return NULL; + } + } + ast_json_ref(json_array); + return json_array; +} /* \brief Generate a string of filenames using the given prefix and separator. * \param details the fax session details @@ -1149,39 +1170,39 @@ static char *generate_filenames_string(struct ast_fax_session_details *details, /*! \brief send a FAX status manager event */ static int report_fax_status(struct ast_channel *chan, struct ast_fax_session_details *details, const char *status) { - char *filenames = generate_filenames_string(details, "FileName: ", "\r\n"); + RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref); + RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + struct ast_json *json_filenames = NULL; - ast_channel_lock(chan); - if (details->option.statusevents) { - struct manager_event_info info; - - get_manager_event_info(chan, &info); - manager_event(EVENT_FLAG_CALL, - "FAXStatus", - "Operation: %s\r\n" - "Status: %s\r\n" - "Channel: %s\r\n" - "Context: %s\r\n" - "Exten: %s\r\n" - "CallerID: %s\r\n" - "LocalStationID: %s\r\n" - "%s%s", - (details->caps & AST_FAX_TECH_GATEWAY) ? "gateway" : (details->caps & AST_FAX_TECH_RECEIVE) ? "receive" : "send", - status, - ast_channel_name(chan), - info.context, - info.exten, - info.cid, - details->localstationid, - S_OR(filenames, ""), - filenames ? "\r\n" : ""); + if (!details->option.statusevents) { + return 0; } - ast_channel_unlock(chan); - if (filenames) { - ast_free(filenames); + json_filenames = generate_filenames_json(details); + if (!json_filenames) { + return -1; + } + + json_object = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: o}", + "type", "status", + "operation", (details->caps & AST_FAX_TECH_GATEWAY) ? "gateway" : (details->caps & AST_FAX_TECH_RECEIVE) ? "receive" : "send", + "status", status, + "local_station_id", details->localstationid, + "filenames", json_filenames); + if (!json_object) { + return -1; } + { + SCOPED_CHANNELLOCK(lock, chan); + + message = ast_channel_cached_blob_create(chan, ast_channel_fax_type(), json_object); + if (!message) { + return -1; + } + stasis_publish(ast_channel_topic(chan), message); + } return 0; } @@ -1738,13 +1759,53 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_ return 0; } +/*! \brief Report on the final state of a receive fax operation + * \note This will lock the \ref ast_channel + */ +static int report_receive_fax_status(struct ast_channel *chan, const char *filename) +{ + RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref); + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + RAII_VAR(struct ast_json *, json_array, ast_json_array_create(), ast_json_unref); + struct ast_json *json_filename = ast_json_string_create(filename); + + if (!json_array || !json_filename) { + return -1; + } + ast_json_array_append(json_array, json_filename); + + { + SCOPED_CHANNELLOCK(lock, chan); + + json_object = ast_json_pack("s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: o", + "type", "receive" + "remote_station_id", S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), ""), + "local_station_id", S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), ""), + "fax_pages", S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), ""), + "fax_resolution", S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), ""), + "fax_bitrate", S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), ""), + "filenames", json_array); + if (!json_object) { + return -1; + } + + message = ast_channel_cached_blob_create(chan, ast_channel_fax_type(), json_object); + if (!message) { + return -1; + } + + stasis_publish(ast_channel_topic(chan), message); + } + return 0; +} + /*! \brief initiate a receive FAX session */ static int receivefax_exec(struct ast_channel *chan, const char *data) { char *parse, modems[128] = ""; int channel_alive; - struct ast_fax_session_details *details; - struct ast_fax_session *s; + RAII_VAR(struct ast_fax_session_details *, details, NULL, ao2_cleanup); + RAII_VAR(struct ast_fax_session *, s, NULL, ao2_cleanup); struct ast_fax_tech_token *token = NULL; struct ast_fax_document *doc; AST_DECLARE_APP_ARGS(args, @@ -1752,7 +1813,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) AST_APP_ARG(options); ); struct ast_flags opts = { 0, }; - struct manager_event_info info; enum ast_t38_state t38state; /* initialize output channel variables */ @@ -1780,7 +1840,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "can't receive a fax on a channel with a T.38 gateway"); set_channel_variables(chan, details); ast_log(LOG_ERROR, "executing ReceiveFAX on a channel with a T.38 Gateway is not supported\n"); - ao2_ref(details, -1); return -1; } @@ -1789,7 +1848,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "maxrate is less than minrate"); set_channel_variables(chan, details); ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate); - ao2_ref(details, -1); return -1; } @@ -1799,7 +1857,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, error, "INVALID_ARGUMENTS"); ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings"); set_channel_variables(chan, details); - ao2_ref(details, -1); return -1; } @@ -1809,7 +1866,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, error, "INVALID_ARGUMENTS"); ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings"); set_channel_variables(chan, details); - ao2_ref(details, -1); return -1; } @@ -1818,7 +1874,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "invalid arguments"); set_channel_variables(chan, details); ast_log(LOG_WARNING, "%s requires an argument (filename[,options])\n", app_receivefax); - ao2_ref(details, -1); return -1; } parse = ast_strdupa(data); @@ -1829,7 +1884,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, error, "INVALID_ARGUMENTS"); ast_string_field_set(details, resultstr, "invalid arguments"); set_channel_variables(chan, details); - ao2_ref(details, -1); return -1; } if (ast_strlen_zero(args.filename)) { @@ -1837,7 +1891,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "invalid arguments"); set_channel_variables(chan, details); ast_log(LOG_WARNING, "%s requires an argument (filename[,options])\n", app_receivefax); - ao2_ref(details, -1); return -1; } @@ -1847,7 +1900,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "invalid arguments"); set_channel_variables(chan, details); ast_log(LOG_WARNING, "%s does not support polling\n", app_receivefax); - ao2_ref(details, -1); return -1; } @@ -1861,7 +1913,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "error allocating memory"); set_channel_variables(chan, details); ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n"); - ao2_ref(details, -1); return -1; } @@ -1894,7 +1945,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "error reserving fax session"); set_channel_variables(chan, details); ast_log(LOG_ERROR, "Unable to reserve FAX session.\n"); - ao2_ref(details, -1); return -1; } @@ -1905,8 +1955,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) set_channel_variables(chan, details); ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", ast_channel_name(chan)); fax_session_release(s, token); - ao2_ref(s, -1); - ao2_ref(details, -1); return -1; } } @@ -1917,8 +1965,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "error negotiating T.38"); set_channel_variables(chan, details); fax_session_release(s, token); - ao2_ref(s, -1); - ao2_ref(details, -1); return -1; } } else { @@ -1931,8 +1977,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "error negotiating T.38"); set_channel_variables(chan, details); fax_session_release(s, token); - ao2_ref(s, -1); - ao2_ref(details, -1); ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", ast_channel_name(chan)); return -1; } @@ -1948,36 +1992,9 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) } } - /* send out the AMI completion event */ - ast_channel_lock(chan); - - get_manager_event_info(chan, &info); - manager_event(EVENT_FLAG_CALL, - "ReceiveFAX", - "Channel: %s\r\n" - "Context: %s\r\n" - "Exten: %s\r\n" - "CallerID: %s\r\n" - "RemoteStationID: %s\r\n" - "LocalStationID: %s\r\n" - "PagesTransferred: %s\r\n" - "Resolution: %s\r\n" - "TransferRate: %s\r\n" - "FileName: %s\r\n", - ast_channel_name(chan), - info.context, - info.exten, - info.cid, - S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), ""), - S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), ""), - S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), ""), - S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), ""), - S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), ""), - args.filename); - ast_channel_unlock(chan); - - ao2_ref(s, -1); - ao2_ref(details, -1); + if (report_receive_fax_status(chan, args.filename)) { + ast_log(AST_LOG_ERROR, "Error publishing ReceiveFax status message\n"); + } /* If the channel hungup return -1; otherwise, return 0 to continue in the dialplan */ return (!channel_alive) ? -1 : 0; @@ -2223,14 +2240,53 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det return 0; } +/*! + * \brief Report on the status of a completed fax send attempt + * \note This will lock the \ref ast_channel + */ +static int report_send_fax_status(struct ast_channel *chan, struct ast_fax_session_details *details) +{ + RAII_VAR(struct ast_json *, json_obj, NULL, ao2_cleanup); + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + struct ast_json *json_filenames; + + json_filenames = generate_filenames_json(details); + if (!json_filenames) { + return -1; + } + + { + SCOPED_CHANNELLOCK(lock, chan); + json_obj = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: o}", + "type", "send" + "remote_station_id", S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), ""), + "local_station_id", S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), ""), + "fax_pages", S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), ""), + "fax_resolution", S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), ""), + "fax_bitrate", S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), ""), + "filenames", json_filenames); + if (!json_obj) { + return -1; + } + + message = ast_channel_cached_blob_create(chan, ast_channel_fax_type(), json_obj); + if (!message) { + return -1; + } + stasis_publish(ast_channel_topic(chan), message); + } + return 0; +} + + /*! \brief initiate a send FAX session */ static int sendfax_exec(struct ast_channel *chan, const char *data) { char *parse, *filenames, *c, modems[128] = ""; int channel_alive, file_count; - struct ast_fax_session_details *details; - struct ast_fax_session *s; + RAII_VAR(struct ast_fax_session_details *, details, NULL, ao2_cleanup); + RAII_VAR(struct ast_fax_session *, s, NULL, ao2_cleanup); struct ast_fax_tech_token *token = NULL; struct ast_fax_document *doc; AST_DECLARE_APP_ARGS(args, @@ -2238,7 +2294,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) AST_APP_ARG(options); ); struct ast_flags opts = { 0, }; - struct manager_event_info info; enum ast_t38_state t38state; /* initialize output channel variables */ @@ -2266,7 +2321,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "can't send a fax on a channel with a T.38 gateway"); set_channel_variables(chan, details); ast_log(LOG_ERROR, "executing SendFAX on a channel with a T.38 Gateway is not supported\n"); - ao2_ref(details, -1); return -1; } @@ -2275,7 +2329,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "maxrate is less than minrate"); set_channel_variables(chan, details); ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate); - ao2_ref(details, -1); return -1; } @@ -2285,7 +2338,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, error, "INVALID_ARGUMENTS"); ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings"); set_channel_variables(chan, details); - ao2_ref(details, -1); return -1; } @@ -2295,7 +2347,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, error, "INVALID_ARGUMENTS"); ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings"); set_channel_variables(chan, details); - ao2_ref(details, -1); return -1; } @@ -2304,7 +2355,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "invalid arguments"); set_channel_variables(chan, details); ast_log(LOG_WARNING, "%s requires an argument (filename[&filename[&filename]][,options])\n", app_sendfax); - ao2_ref(details, -1); return -1; } parse = ast_strdupa(data); @@ -2316,7 +2366,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, error, "INVALID_ARGUMENTS"); ast_string_field_set(details, resultstr, "invalid arguments"); set_channel_variables(chan, details); - ao2_ref(details, -1); return -1; } if (ast_strlen_zero(args.filenames)) { @@ -2324,7 +2373,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "invalid arguments"); set_channel_variables(chan, details); ast_log(LOG_WARNING, "%s requires an argument (filename[&filename[&filename]],options])\n", app_sendfax); - ao2_ref(details, -1); return -1; } @@ -2334,7 +2382,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "invalid arguments"); set_channel_variables(chan, details); ast_log(LOG_WARNING, "%s does not support polling\n", app_sendfax); - ao2_ref(details, -1); return -1; } @@ -2348,7 +2395,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "error reading file"); set_channel_variables(chan, details); ast_log(LOG_ERROR, "access failure. Verify '%s' exists and check permissions.\n", args.filenames); - ao2_ref(details, -1); return -1; } @@ -2357,7 +2403,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "error allocating memory"); set_channel_variables(chan, details); ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n"); - ao2_ref(details, -1); return -1; } @@ -2402,7 +2447,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "error reserving fax session"); set_channel_variables(chan, details); ast_log(LOG_ERROR, "Unable to reserve FAX session.\n"); - ao2_ref(details, -1); return -1; } @@ -2413,8 +2457,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) set_channel_variables(chan, details); ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", ast_channel_name(chan)); fax_session_release(s, token); - ao2_ref(s, -1); - ao2_ref(details, -1); return -1; } } @@ -2425,8 +2467,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "error negotiating T.38"); set_channel_variables(chan, details); fax_session_release(s, token); - ao2_ref(s, -1); - ao2_ref(details, -1); return -1; } } else { @@ -2439,8 +2479,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, resultstr, "error negotiating T.38"); set_channel_variables(chan, details); fax_session_release(s, token); - ao2_ref(s, -1); - ao2_ref(details, -1); ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", ast_channel_name(chan)); return -1; } @@ -2460,42 +2498,13 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) if (!(filenames = generate_filenames_string(details, "FileName: ", "\r\n"))) { ast_log(LOG_ERROR, "Error generating SendFAX manager event\n"); - ao2_ref(s, -1); - ao2_ref(details, -1); return (!channel_alive) ? -1 : 0; } /* send out the AMI completion event */ - ast_channel_lock(chan); - get_manager_event_info(chan, &info); - manager_event(EVENT_FLAG_CALL, - "SendFAX", - "Channel: %s\r\n" - "Context: %s\r\n" - "Exten: %s\r\n" - "CallerID: %s\r\n" - "RemoteStationID: %s\r\n" - "LocalStationID: %s\r\n" - "PagesTransferred: %s\r\n" - "Resolution: %s\r\n" - "TransferRate: %s\r\n" - "%s\r\n", - ast_channel_name(chan), - info.context, - info.exten, - info.cid, - S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), ""), - S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), ""), - S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), ""), - S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), ""), - S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), ""), - filenames); - ast_channel_unlock(chan); - - ast_free(filenames); - - ao2_ref(s, -1); - ao2_ref(details, -1); + if (report_send_fax_status(chan, details)) { + ast_log(AST_LOG_ERROR, "Error publishing SendFAX status message\n"); + } /* If the channel hungup return -1; otherwise, return 0 to continue in the dialplan */ return (!channel_alive) ? -1 : 0; diff --git a/res/res_jabber.c b/res/res_jabber.c index 2070c8024..7ca0bf81e 100644 --- a/res/res_jabber.c +++ b/res/res_jabber.c @@ -1536,10 +1536,6 @@ static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incom { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); - if (!ast_strlen_zero(xmpp)) { - manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp); - } - if (client->debug) { if (is_incoming) { ast_verbose("\nJABBER: %s INCOMING: %s\n", client->name, xmpp); @@ -3247,9 +3243,9 @@ static void aji_mwi_cb(void *data, struct stasis_subscription *sub, struct stasi char oldmsgs[10]; char newmsgs[10]; struct aji_client *client = data; - struct stasis_mwi_state *mwi_state; + struct ast_mwi_state *mwi_state; - if (!stasis_subscription_is_subscribed(sub) || stasis_mwi_state_type() != stasis_message_type(msg)) { + if (!stasis_subscription_is_subscribed(sub) || ast_mwi_state_type() != stasis_message_type(msg)) { return; } @@ -3308,7 +3304,7 @@ static int cached_devstate_cb(void *obj, void *arg, int flags) static void aji_init_event_distribution(struct aji_client *client) { if (!mwi_sub) { - mwi_sub = stasis_subscribe(stasis_mwi_topic_all(), aji_mwi_cb, client); + mwi_sub = stasis_subscribe(ast_mwi_topic_all(), aji_mwi_cb, client); } if (!device_state_sub) { RAII_VAR(struct ao2_container *, cached, NULL, ao2_cleanup); @@ -3369,7 +3365,7 @@ static int aji_handle_pubsub_event(void *data, ikspak *pak) sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs); sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs); - stasis_publish_mwi_state_full(item_id, context, newmsgs, oldmsgs, &pubsub_eid); + ast_publish_mwi_state_full(item_id, context, newmsgs, oldmsgs, NULL, &pubsub_eid); return IKS_FILTER_EAT; } else { diff --git a/res/res_monitor.c b/res/res_monitor.c index 9aca24a0d..72911b5b1 100644 --- a/res/res_monitor.c +++ b/res/res_monitor.c @@ -40,8 +40,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/file.h" #include "asterisk/pbx.h" #include "asterisk/module.h" -#include "asterisk/manager.h" #include "asterisk/cli.h" +#include "asterisk/manager.h" +#include "asterisk/stasis.h" +#include "asterisk/stasis_channels.h" #define AST_API_MODULE #include "asterisk/monitor.h" #include "asterisk/app.h" @@ -291,6 +293,7 @@ int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const cha const char *fname_base, int need_lock, int stream_action) { int res = 0; + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); LOCK_IF_NEEDED(chan, need_lock); @@ -393,11 +396,12 @@ int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const cha /* so we know this call has been monitored in case we need to bill for it or something */ pbx_builtin_setvar_helper(chan, "__MONITORED","true"); - ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStart", - "Channel: %s\r\n" - "Uniqueid: %s\r\n", - ast_channel_name(chan), - ast_channel_uniqueid(chan)); + message = ast_channel_cached_blob_create(chan, + ast_channel_monitor_start_type(), + NULL); + if (message) { + stasis_publish(ast_channel_topic(chan), message); + } } else { ast_debug(1,"Cannot start monitoring %s, already monitored\n", ast_channel_name(chan)); res = -1; @@ -437,6 +441,7 @@ static const char *get_soxmix_format(const char *format) int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_lock) { int delfiles = 0; + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); LOCK_IF_NEEDED(chan, need_lock); @@ -511,12 +516,12 @@ int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_l ast_free(ast_channel_monitor(chan)); ast_channel_monitor_set(chan, NULL); - ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStop", - "Channel: %s\r\n" - "Uniqueid: %s\r\n", - ast_channel_name(chan), - ast_channel_uniqueid(chan) - ); + message = ast_channel_cached_blob_create(chan, + ast_channel_monitor_stop_type(), + NULL); + if (message) { + stasis_publish(ast_channel_topic(chan), message); + } pbx_builtin_setvar_helper(chan, "MONITORED", NULL); } pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL); diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c index 130618431..2ed7ea52b 100644 --- a/res/res_musiconhold.c +++ b/res/res_musiconhold.c @@ -67,7 +67,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/cli.h" #include "asterisk/stringfields.h" #include "asterisk/linkedlists.h" -#include "asterisk/manager.h" +#include "asterisk/stasis.h" +#include "asterisk/stasis_channels.h" #include "asterisk/paths.h" #include "asterisk/astobj2.h" #include "asterisk/timing.h" @@ -1373,6 +1374,8 @@ static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, con struct mohclass *mohclass = NULL; struct moh_files_state *state = ast_channel_music_state(chan); struct ast_variable *var = NULL; + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref); int res; int realtime_possible = ast_check_realtime("musiconhold"); @@ -1567,14 +1570,6 @@ static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, con } } - ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold", - "State: Start\r\n" - "Channel: %s\r\n" - "UniqueID: %s\r\n" - "Class: %s\r\n", - ast_channel_name(chan), ast_channel_uniqueid(chan), - mohclass->name); - ast_set_flag(ast_channel_flags(chan), AST_FLAG_MOH); if (mohclass->total_files) { @@ -1583,6 +1578,20 @@ static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, con res = ast_activate_generator(chan, &mohgen, mohclass); } + json_object = ast_json_pack("{s: s}", + "class", mohclass->name); + if (!json_object) { + mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start"); + return -1; + } + + message = ast_channel_cached_blob_create(chan, + ast_channel_moh_start_type(), + json_object); + if (message) { + stasis_publish(ast_channel_topic(chan), message); + } + mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start"); return res; @@ -1590,6 +1599,7 @@ static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, con static void local_ast_moh_stop(struct ast_channel *chan) { + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); ast_clear_flag(ast_channel_flags(chan), AST_FLAG_MOH); ast_deactivate_generator(chan); @@ -1601,11 +1611,10 @@ static void local_ast_moh_stop(struct ast_channel *chan) } } - ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold", - "State: Stop\r\n" - "Channel: %s\r\n" - "UniqueID: %s\r\n", - ast_channel_name(chan), ast_channel_uniqueid(chan)); + message = ast_channel_cached_blob_create(chan, ast_channel_moh_stop_type(), NULL); + if (message) { + stasis_publish(ast_channel_topic(chan), message); + } ast_channel_unlock(chan); } diff --git a/res/res_sip_mwi.c b/res/res_sip_mwi.c index 588662f33..f18e56469 100644 --- a/res/res_sip_mwi.c +++ b/res/res_sip_mwi.c @@ -133,7 +133,7 @@ static struct mwi_stasis_subscription *mwi_stasis_subscription_alloc(const char return NULL; } - topic = stasis_mwi_topic(mailbox); + topic = ast_mwi_topic(mailbox); /* Safe strcpy */ strcpy(mwi_stasis_sub->mailbox, mailbox); @@ -237,9 +237,9 @@ static int get_message_count(void *obj, void *arg, int flags) RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); struct mwi_stasis_subscription *mwi_stasis = obj; struct message_accumulator *counter = arg; - struct stasis_mwi_state *mwi_state; + struct ast_mwi_state *mwi_state; - msg = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), mwi_stasis->mailbox); + msg = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), mwi_stasis->mailbox); if (!msg) { return 0; } @@ -604,7 +604,7 @@ static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub, return; } - if (stasis_mwi_state_type() == stasis_message_type(msg)) { + if (ast_mwi_state_type() == stasis_message_type(msg)) { struct ast_taskprocessor *serializer = mwi_sub->is_solicited ? ast_sip_subscription_get_serializer(mwi_sub->sip_sub) : NULL; ao2_ref(mwi_sub, +1); ast_sip_push_task(serializer, serialized_notify, mwi_sub); diff --git a/res/res_xmpp.c b/res/res_xmpp.c index dea837204..89eb45d18 100644 --- a/res/res_xmpp.c +++ b/res/res_xmpp.c @@ -1324,9 +1324,9 @@ static void xmpp_pubsub_mwi_cb(void *data, struct stasis_subscription *sub, stru struct ast_xmpp_client *client = data; const char *mailbox, *context; char oldmsgs[10], newmsgs[10]; - struct stasis_mwi_state *mwi_state; + struct ast_mwi_state *mwi_state; - if (!stasis_subscription_is_subscribed(sub) || stasis_mwi_state_type() != stasis_message_type(msg)) { + if (!stasis_subscription_is_subscribed(sub) || ast_mwi_state_type() != stasis_message_type(msg)) { return; } @@ -1484,7 +1484,7 @@ static int xmpp_pubsub_handle_event(void *data, ikspak *pak) sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs); sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs); - stasis_publish_mwi_state_full(item_id, context, newmsgs, oldmsgs, &pubsub_eid); + ast_publish_mwi_state_full(item_id, context, newmsgs, oldmsgs, NULL, &pubsub_eid); return IKS_FILTER_EAT; } else { @@ -1596,7 +1596,7 @@ static void xmpp_init_event_distribution(struct ast_xmpp_client *client) xmpp_pubsub_unsubscribe(client, "device_state"); xmpp_pubsub_unsubscribe(client, "message_waiting"); - if (!(client->mwi_sub = stasis_subscribe(stasis_mwi_topic_all(), xmpp_pubsub_mwi_cb, client))) { + if (!(client->mwi_sub = stasis_subscribe(ast_mwi_topic_all(), xmpp_pubsub_mwi_cb, client))) { return; } @@ -2546,10 +2546,6 @@ static void xmpp_log_hook(void *data, const char *xmpp, size_t size, int incomin RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup); struct ast_xmpp_client *client = data; - if (!ast_strlen_zero(xmpp)) { - manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp); - } - if (!debug && (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) || !ast_test_flag(&clientcfg->flags, XMPP_DEBUG))) { return; } |