diff options
-rw-r--r-- | CHANGES | 3 | ||||
-rw-r--r-- | apps/app_dial.c | 38 | ||||
-rw-r--r-- | apps/app_followme.c | 4 | ||||
-rw-r--r-- | apps/app_queue.c | 10 | ||||
-rw-r--r-- | channels/chan_local.c | 6 | ||||
-rw-r--r-- | configs/cdr.conf.sample | 5 | ||||
-rw-r--r-- | funcs/func_channel.c | 34 | ||||
-rw-r--r-- | include/asterisk/channel.h | 21 | ||||
-rw-r--r-- | include/asterisk/pbx.h | 65 | ||||
-rw-r--r-- | main/autoservice.c | 10 | ||||
-rw-r--r-- | main/channel.c | 93 | ||||
-rw-r--r-- | main/channel_internal_api.c | 5 | ||||
-rw-r--r-- | main/features.c | 139 | ||||
-rw-r--r-- | main/pbx.c | 382 |
14 files changed, 630 insertions, 185 deletions
@@ -46,6 +46,9 @@ Core have their own verbosity level. The command 'core set verbose' will now set a separate level for each remote console without affecting any other console. + * Hangup handlers can be attached to channels using the CHANNEL(hangup_handler_xxx) + options. Hangup handlers will run when the channel is hung up similar to the + h extension. CLI Changes ------------------- diff --git a/apps/app_dial.c b/apps/app_dial.c index 281204847..ccb7b018c 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -1881,7 +1881,8 @@ static int do_privacy(struct ast_channel *chan, struct ast_channel *peer, } return 0; /* the good exit path */ } else { - ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */ + /* hang up on the callee -- he didn't want to talk anyway! */ + ast_autoservice_chan_hangup_peer(chan, peer); return -1; } } @@ -2774,7 +2775,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast if (active_chan) { struct ast_frame *fr = ast_read(active_chan); if (!fr) { - ast_hangup(peer); + ast_autoservice_chan_hangup_peer(chan, peer); res = -1; goto done; } @@ -2790,7 +2791,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast switch (fr->subclass.integer) { case AST_CONTROL_HANGUP: ast_frfree(fr); - ast_hangup(peer); + ast_autoservice_chan_hangup_peer(chan, peer); res = -1; goto done; default: @@ -2821,7 +2822,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast ast_channel_exten_set(peer, ast_channel_exten(chan)); ast_channel_priority_set(peer, ast_channel_priority(chan) + 2); if (ast_pbx_start(peer)) { - ast_hangup(peer); + ast_autoservice_chan_hangup_peer(chan, peer); } hanguptree(&out_chans, NULL, ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0); if (continue_exec) @@ -3027,7 +3028,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast res = ast_channel_make_compatible(chan, peer); if (res < 0) { ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(chan), ast_channel_name(peer)); - ast_hangup(peer); + ast_autoservice_chan_hangup_peer(chan, peer); res = -1; goto done; } @@ -3047,28 +3048,9 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast if (ast_test_flag64(&opts, OPT_PEER_H) && ast_exists_extension(peer, ast_channel_context(peer), "h", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) { - int autoloopflag; - int found; - int res9; - - ast_channel_exten_set(peer, "h"); - ast_channel_priority_set(peer, 1); - autoloopflag = ast_test_flag(ast_channel_flags(peer), AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */ - ast_set_flag(ast_channel_flags(peer), AST_FLAG_IN_AUTOLOOP); - - while ((res9 = ast_spawn_extension(peer, ast_channel_context(peer), ast_channel_exten(peer), - ast_channel_priority(peer), - S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL), - &found, 1)) == 0) { - ast_channel_priority_set(peer, ast_channel_priority(peer) + 1); - } - - if (found && res9) { - /* Something bad happened, or a hangup has been requested. */ - ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", ast_channel_context(peer), ast_channel_exten(peer), ast_channel_priority(peer), ast_channel_name(peer)); - ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", ast_channel_context(peer), ast_channel_exten(peer), ast_channel_priority(peer), ast_channel_name(peer)); - } - ast_set2_flag(ast_channel_flags(peer), autoloopflag, AST_FLAG_IN_AUTOLOOP); /* set it back the way it was */ + ast_autoservice_start(chan); + ast_pbx_h_exten_run(peer, ast_channel_context(peer)); + ast_autoservice_stop(chan); } if (!ast_check_hangup(peer)) { if (ast_test_flag64(&opts, OPT_CALLEE_GO_ON)) { @@ -3089,7 +3071,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast ast_channel_hangupcause_set(chan, ast_channel_hangupcause(peer)); } } - ast_hangup(peer); + ast_autoservice_chan_hangup_peer(chan, peer); } out: if (moh) { diff --git a/apps/app_followme.c b/apps/app_followme.c index 275ed43c5..9b4a925da 100644 --- a/apps/app_followme.c +++ b/apps/app_followme.c @@ -1490,7 +1490,7 @@ static int app_exec(struct ast_channel *chan, const char *data) res = ast_channel_make_compatible(caller, outbound); if (res < 0) { ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(caller), ast_channel_name(outbound)); - ast_hangup(outbound); + ast_autoservice_chan_hangup_peer(caller, outbound); goto outrun; } @@ -1513,7 +1513,7 @@ static int app_exec(struct ast_channel *chan, const char *data) } res = ast_bridge_call(caller, outbound, &config); - ast_hangup(outbound); + ast_autoservice_chan_hangup_peer(caller, outbound); } outrun: diff --git a/apps/app_queue.c b/apps/app_queue.c index 4acd799f1..cee5d449f 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -5266,7 +5266,7 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char * "%s", queuename, ast_channel_uniqueid(qe->chan), ast_channel_name(peer), member->interface, member->membername, qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); - ast_hangup(peer); + ast_autoservice_chan_hangup_peer(qe->chan, peer); ao2_ref(member, -1); goto out; } else if (ast_check_hangup(qe->chan)) { @@ -5274,7 +5274,7 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char * ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", ast_channel_name(peer)); ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start); record_abandoned(qe); - ast_hangup(peer); + ast_autoservice_chan_hangup_peer(qe->chan, peer); ao2_ref(member, -1); return -1; } @@ -5296,7 +5296,7 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char * ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(qe->chan), ast_channel_name(peer)); record_abandoned(qe); ast_cdr_failed(ast_channel_cdr(qe->chan)); - ast_hangup(peer); + ast_autoservice_chan_hangup_peer(qe->chan, peer); ao2_ref(member, -1); return -1; } @@ -5673,10 +5673,10 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char * caller_priority + 1); } if (goto_res || ast_pbx_start(peer)) { - ast_hangup(peer); + ast_autoservice_chan_hangup_peer(qe->chan, peer); } } else { - ast_hangup(peer); + ast_autoservice_chan_hangup_peer(qe->chan, peer); } res = bridge ? bridge : 1; diff --git a/channels/chan_local.c b/channels/chan_local.c index 695949fdc..5e72a6153 100644 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -238,6 +238,12 @@ static int local_setoption(struct ast_channel *ast, int option, void * data, int return -1; } + if (!strcmp(write_info->function, "CHANNEL") + && !strncasecmp(write_info->data, "hangup_handler_", 15)) { + /* Block CHANNEL(hangup_handler_xxx) writes to the other local channel. */ + return 0; + } + /* get the tech pvt */ if (!(p = ast_channel_tech_pvt(ast))) { return -1; diff --git a/configs/cdr.conf.sample b/configs/cdr.conf.sample index 882f48735..458e19ab4 100644 --- a/configs/cdr.conf.sample +++ b/configs/cdr.conf.sample @@ -36,8 +36,9 @@ ; Normally, CDR's are not closed out until after all extensions are finished ; executing. By enabling this option, the CDR will be ended before executing -; the "h" extension so that CDR values such as "end" and "billsec" may be -; retrieved inside of of this extension. The default value is "no". +; the "h" extension and hangup handlers so that CDR values such as "end" and +; "billsec" may be retrieved inside of of this extension. +; The default value is "no". ;endbeforehexten=no ; Normally, the 'billsec' field logged to the backends (text files or databases) diff --git a/funcs/func_channel.c b/funcs/func_channel.c index 14ece0cfd..699f3b660 100644 --- a/funcs/func_channel.c +++ b/funcs/func_channel.c @@ -98,6 +98,29 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <enum name="checkhangup"> <para>R/O Whether the channel is hanging up (1/0)</para> </enum> + <enum name="hangup_handler_pop"> + <para>W/O Replace the most recently added hangup handler + with a new hangup handler on the channel if supplied. The + assigned string is passed to the Gosub application when + the channel is hung up. Any optionally omitted context + and exten are supplied by the channel pushing the handler + before it is pushed.</para> + </enum> + <enum name="hangup_handler_push"> + <para>W/O Push a hangup handler onto the channel hangup + handler stack. The assigned string is passed to the + Gosub application when the channel is hung up. Any + optionally omitted context and exten are supplied by the + channel pushing the handler before it is pushed.</para> + </enum> + <enum name="hangup_handler_wipe"> + <para>W/O Wipe the entire hangup handler stack and replace + with a new hangup handler on the channel if supplied. The + assigned string is passed to the Gosub application when + the channel is hung up. Any optionally omitted context + and exten are supplied by the channel pushing the handler + before it is pushed.</para> + </enum> <enum name="language"> <para>R/W language for sounds played.</para> </enum> @@ -523,6 +546,17 @@ static int func_channel_write_real(struct ast_channel *chan, const char *functio break; } } + } else if (!strcasecmp(data, "hangup_handler_pop")) { + /* Pop one hangup handler before pushing the new handler. */ + ast_pbx_hangup_handler_pop(chan); + ast_pbx_hangup_handler_push(chan, value); + } else if (!strcasecmp(data, "hangup_handler_push")) { + ast_pbx_hangup_handler_push(chan, value); + } else if (!strcasecmp(data, "hangup_handler_wipe")) { + /* Pop all hangup handlers before pushing the new handler. */ + while (ast_pbx_hangup_handler_pop(chan)) { + } + ast_pbx_hangup_handler_push(chan, value); } else if (!strncasecmp(data, "secure_bridge_", 14)) { struct ast_datastore *ds; struct ast_secure_call_store *store; diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 06be07b2a..4f2cc8849 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -731,6 +731,15 @@ enum ast_t38_state { T38_STATE_NEGOTIATED, /*!< T38 established */ }; +/*! Hangup handler instance node. */ +struct ast_hangup_handler { + /*! Next hangup handler node. */ + AST_LIST_ENTRY(ast_hangup_handler) node; + /*! Hangup handler arg string passed to the Gosub application */ + char args[0]; +}; + +AST_LIST_HEAD_NOLOCK(ast_hangup_handler_list, ast_hangup_handler); AST_LIST_HEAD_NOLOCK(ast_datastore_list, ast_datastore); AST_LIST_HEAD_NOLOCK(ast_autochan_list, ast_autochan); AST_LIST_HEAD_NOLOCK(ast_readq_list, ast_frame); @@ -2198,6 +2207,17 @@ int ast_autoservice_start(struct ast_channel *chan); int ast_autoservice_stop(struct ast_channel *chan); /*! + * \brief Put chan into autoservice while hanging up peer. + * \since 11.0 + * + * \param chan Chan to put into autoservice. + * \param peer Chan to run hangup handlers and hangup. + * + * \return Nothing + */ +void ast_autoservice_chan_hangup_peer(struct ast_channel *chan, struct ast_channel *peer); + +/*! * \brief Ignore certain frame types * \note Normally, we cache DTMF, IMAGE, HTML, TEXT, and CONTROL frames * while a channel is in autoservice and queue them up when taken out of @@ -3748,6 +3768,7 @@ void ast_channel_whentohangup_set(struct ast_channel *chan, struct timeval *valu void ast_channel_varshead_set(struct ast_channel *chan, struct varshead *value); /* List getters */ +struct ast_hangup_handler_list *ast_channel_hangup_handlers(struct ast_channel *chan); struct ast_datastore_list *ast_channel_datastores(struct ast_channel *chan); struct ast_autochan_list *ast_channel_autochans(struct ast_channel *chan); struct ast_readq_list *ast_channel_readq(struct ast_channel *chan); diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h index 2305f3910..bea7e5ebf 100644 --- a/include/asterisk/pbx.h +++ b/include/asterisk/pbx.h @@ -368,6 +368,71 @@ struct ast_pbx_args { enum ast_pbx_result ast_pbx_run_args(struct ast_channel *c, struct ast_pbx_args *args); /*! + * \brief Run the h exten from the given context. + * \since 11.0 + * + * \param chan Channel to run the h exten on. + * \param context Context the h exten is in. + * + * \return Nothing + */ +void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context); + +/*! + * \brief Run all hangup handlers on the channel. + * \since 11.0 + * + * \param chan Channel to run the hangup handlers on. + * + * \note Absolutely _NO_ channel locks should be held before calling this function. + * + * \retval Zero if no hangup handlers run. + * \retval non-zero if hangup handlers were run. + */ +int ast_pbx_hangup_handler_run(struct ast_channel *chan); + +/*! + * \brief Init the hangup handler container on a channel. + * \since 11.0 + * + * \param chan Channel to init the hangup handler container on. + * + * \return Nothing + */ +void ast_pbx_hangup_handler_init(struct ast_channel *chan); + +/*! + * \brief Destroy the hangup handler container on a channel. + * \since 11.0 + * + * \param chan Channel to destroy the hangup handler container on. + * + * \return Nothing + */ +void ast_pbx_hangup_handler_destroy(struct ast_channel *chan); + +/*! + * \brief Pop the top of the channel hangup handler stack. + * \since 11.0 + * + * \param chan Channel to push the hangup handler onto. + * + * \retval TRUE if a handler was popped off of the stack. + */ +int ast_pbx_hangup_handler_pop(struct ast_channel *chan); + +/*! + * \brief Push the given hangup handler onto the channel hangup handler stack. + * \since 11.0 + * + * \param chan Channel to push the hangup handler onto. + * \param handler Gosub application parameter string. + * + * \return Nothing + */ +void ast_pbx_hangup_handler_push(struct ast_channel *chan, const char *handler); + +/*! * \brief Add and extension to an extension context. * * \param context context to add the extension to diff --git a/main/autoservice.c b/main/autoservice.c index 37bbee682..afbcbd3e0 100644 --- a/main/autoservice.c +++ b/main/autoservice.c @@ -313,6 +313,16 @@ int ast_autoservice_stop(struct ast_channel *chan) return res; } +void ast_autoservice_chan_hangup_peer(struct ast_channel *chan, struct ast_channel *peer) +{ + if (chan && !ast_autoservice_start(chan)) { + ast_hangup(peer); + ast_autoservice_stop(chan); + } else { + ast_hangup(peer); + } +} + int ast_autoservice_ignore(struct ast_channel *chan, enum ast_frame_type ftype) { struct asent *as; diff --git a/main/channel.c b/main/channel.c index 59d40dc6c..0f4c3bc77 100644 --- a/main/channel.c +++ b/main/channel.c @@ -1107,6 +1107,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char headp = ast_channel_varshead(tmp); AST_LIST_HEAD_INIT_NOLOCK(headp); + ast_pbx_hangup_handler_init(tmp); AST_LIST_HEAD_INIT_NOLOCK(ast_channel_datastores(tmp)); AST_LIST_HEAD_INIT_NOLOCK(ast_channel_autochans(tmp)); @@ -1183,6 +1184,9 @@ struct ast_channel *ast_dummy_channel_alloc(void) return NULL; } + ast_pbx_hangup_handler_init(tmp); + AST_LIST_HEAD_INIT_NOLOCK(ast_channel_datastores(tmp)); + headp = ast_channel_varshead(tmp); AST_LIST_HEAD_INIT_NOLOCK(headp); @@ -2198,8 +2202,11 @@ static void ast_channel_destructor(void *obj) ast_cel_check_retire_linkedid(chan); } - /* Get rid of each of the data stores on the channel */ + ast_pbx_hangup_handler_destroy(chan); + ast_channel_lock(chan); + + /* Get rid of each of the data stores on the channel */ while ((datastore = AST_LIST_REMOVE_HEAD(ast_channel_datastores(chan), entry))) /* Free the data store */ ast_datastore_free(datastore); @@ -2316,10 +2323,17 @@ static void ast_channel_destructor(void *obj) static void ast_dummy_channel_destructor(void *obj) { struct ast_channel *chan = obj; + struct ast_datastore *datastore; struct ast_var_t *vardata; struct varshead *headp; - headp = ast_channel_varshead(chan); + ast_pbx_hangup_handler_destroy(chan); + + /* Get rid of each of the data stores on the channel */ + while ((datastore = AST_LIST_REMOVE_HEAD(ast_channel_datastores(chan), entry))) { + /* Free the data store */ + ast_datastore_free(datastore); + } ast_party_dialed_free(ast_channel_dialed(chan)); ast_party_caller_free(ast_channel_caller(chan)); @@ -2328,6 +2342,7 @@ static void ast_dummy_channel_destructor(void *obj) /* loop over the variables list, freeing all data and deleting list items */ /* no need to lock the list, as the channel is already locked */ + headp = ast_channel_varshead(chan); while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries))) ast_var_delete(vardata); @@ -2599,7 +2614,6 @@ static void destroy_hooks(struct ast_channel *chan) int ast_hangup(struct ast_channel *chan) { char extra_str[64]; /* used for cel logging below */ - int was_zombie; ast_autoservice_stop(chan); @@ -2632,11 +2646,20 @@ int ast_hangup(struct ast_channel *chan) } /* Mark as a zombie so a masquerade cannot be setup on this channel. */ - if (!(was_zombie = ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE))) { - ast_set_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE); - } + ast_set_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE); ast_channel_unlock(chan); + + /* + * XXX if running the hangup handlers here causes problems + * because the handlers take too long to execute, we could move + * the meat of this function into another thread. A thread + * where channels go to die. + * + * If this is done, ast_autoservice_chan_hangup_peer() will no + * longer be needed. + */ + ast_pbx_hangup_handler_run(chan); ao2_unlink(channels, chan); ast_channel_lock(chan); @@ -2675,14 +2698,10 @@ int ast_hangup(struct ast_channel *chan) (long) pthread_self(), ast_channel_name(chan), (long)ast_channel_blocker(chan), ast_channel_blockproc(chan)); ast_assert(ast_test_flag(ast_channel_flags(chan), AST_FLAG_BLOCKING) == 0); } - if (!was_zombie) { - ast_debug(1, "Hanging up channel '%s'\n", ast_channel_name(chan)); - if (ast_channel_tech(chan)->hangup) { - ast_channel_tech(chan)->hangup(chan); - } - } else { - ast_debug(1, "Hanging up zombie '%s'\n", ast_channel_name(chan)); + ast_debug(1, "Hanging up channel '%s'\n", ast_channel_name(chan)); + if (ast_channel_tech(chan)->hangup) { + ast_channel_tech(chan)->hangup(chan); } ast_channel_unlock(chan); @@ -6535,6 +6554,7 @@ int ast_do_masquerade(struct ast_channel *original) const struct ast_channel_tech *t; void *t_pvt; union { + struct ast_hangup_handler_list handlers; struct ast_party_dialed dialed; struct ast_party_caller caller; struct ast_party_connected_line connected; @@ -6758,6 +6778,11 @@ int ast_do_masquerade(struct ast_channel *original) ast_app_group_update(clonechan, original); + /* Swap hangup handlers. */ + exchange.handlers = *ast_channel_hangup_handlers(original); + *ast_channel_hangup_handlers(original) = *ast_channel_hangup_handlers(clonechan); + *ast_channel_hangup_handlers(clonechan) = exchange.handlers; + /* Move data stores over */ if (AST_LIST_FIRST(ast_channel_datastores(clonechan))) { struct ast_datastore *ds; @@ -6878,19 +6903,6 @@ int ast_do_masquerade(struct ast_channel *original) * container lock. */ ast_channel_unlock(original); - - /* Disconnect the original original's physical side */ - if (ast_channel_tech(clonechan)->hangup && ast_channel_tech(clonechan)->hangup(clonechan)) { - ast_log(LOG_WARNING, "Hangup failed! Strange things may happen!\n"); - } else { - /* - * We just hung up the original original's physical side of the - * channel. Set the new zombie to use the kill channel driver - * for safety. - */ - ast_channel_tech_set(clonechan, &ast_kill_tech); - } - ast_channel_unlock(clonechan); /* @@ -6938,33 +6950,18 @@ int ast_do_masquerade(struct ast_channel *original) ast_datastore_free(xfer_ds); } - if (clone_was_zombie) { - ast_channel_lock(clonechan); - ast_debug(1, "Destroying channel clone '%s'\n", ast_channel_name(clonechan)); - ast_manager_event(clonechan, EVENT_FLAG_CALL, "Hangup", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Cause: %d\r\n" - "Cause-txt: %s\r\n", - ast_channel_name(clonechan), - ast_channel_uniqueid(clonechan), - ast_channel_hangupcause(clonechan), - ast_cause2str(ast_channel_hangupcause(clonechan)) - ); - ast_channel_unlock(clonechan); - - /* - * Drop the system reference to destroy the channel since it is - * already unlinked. - */ - ast_channel_unref(clonechan); - } else { + if (!clone_was_zombie) { ao2_link(channels, clonechan); } - ao2_link(channels, original); ao2_unlock(channels); + if (clone_was_zombie) { + /* Restart the ast_hangup() that was deferred because of this masquerade. */ + ast_debug(1, "Destroying channel clone '%s'\n", ast_channel_name(clonechan)); + ast_hangup(clonechan); + } + /* Release our held safety references. */ ast_channel_unref(original); ast_channel_unref(clonechan); diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c index 1922db9a9..b91e2e253 100644 --- a/main/channel_internal_api.c +++ b/main/channel_internal_api.c @@ -137,6 +137,7 @@ struct ast_channel { struct ast_readq_list readq; struct ast_jb jb; /*!< The jitterbuffer state */ struct timeval dtmf_tv; /*!< The time that an in process digit began, or the last digit ended */ + struct ast_hangup_handler_list hangup_handlers;/*!< Hangup handlers on the channel. */ struct ast_datastore_list datastores; /*!< Data stores on the channel */ struct ast_autochan_list autochans; /*!< Autochans on the channel */ unsigned long insmpl; /*!< Track the read/written samples for monitor use */ @@ -884,6 +885,10 @@ struct ast_format *ast_channel_writeformat(struct ast_channel *chan) { return &chan->writeformat; } +struct ast_hangup_handler_list *ast_channel_hangup_handlers(struct ast_channel *chan) +{ + return &chan->hangup_handlers; +} struct ast_datastore_list *ast_channel_datastores(struct ast_channel *chan) { return &chan->datastores; diff --git a/main/features.c b/main/features.c index 79d1036f2..b602dbb98 100644 --- a/main/features.c +++ b/main/features.c @@ -1077,10 +1077,10 @@ static void *bridge_call_thread(void *data) ast_log(LOG_VERBOSE, "putting peer %s into PBX again\n", ast_channel_name(tobj->peer)); if (ast_pbx_start(tobj->peer)) { ast_log(LOG_WARNING, "FAILED continuing PBX on peer %s\n", ast_channel_name(tobj->peer)); - ast_hangup(tobj->peer); + ast_autoservice_chan_hangup_peer(tobj->chan, tobj->peer); } } else { - ast_hangup(tobj->peer); + ast_autoservice_chan_hangup_peer(tobj->chan, tobj->peer); } if (!ast_check_hangup(tobj->chan)) { ast_log(LOG_VERBOSE, "putting chan %s into PBX again\n", ast_channel_name(tobj->chan)); @@ -2526,7 +2526,7 @@ static int check_compat(struct ast_channel *c, struct ast_channel *newchan) if (ast_channel_make_compatible(c, newchan) < 0) { ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(c), ast_channel_name(newchan)); - ast_hangup(newchan); + ast_autoservice_chan_hangup_peer(c, newchan); return -1; } return 0; @@ -2762,7 +2762,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st } if (ast_check_hangup(newchan) || !ast_check_hangup(transferer)) { - ast_hangup(newchan); + ast_autoservice_chan_hangup_peer(transferer, newchan); if (ast_stream_and_wait(transferer, xfersound, "")) { ast_log(LOG_WARNING, "Failed to play transfer sound!\n"); } @@ -2882,7 +2882,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st /* newchan is up, we should prepare transferee and bridge them */ if (ast_check_hangup(newchan)) { - ast_hangup(newchan); + ast_autoservice_chan_hangup_peer(transferee, newchan); ast_party_connected_line_free(&connected_line); return -1; } @@ -2908,7 +2908,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", ast_channel_linkedid(transferee), 0, "Transfered/%s", ast_channel_name(transferee)); if (!xferchan) { - ast_hangup(newchan); + ast_autoservice_chan_hangup_peer(transferee, newchan); ast_party_connected_line_free(&connected_line); return -1; } @@ -2922,7 +2922,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st if (ast_channel_masquerade(xferchan, transferee)) { ast_hangup(xferchan); - ast_hangup(newchan); + ast_autoservice_chan_hangup_peer(transferee, newchan); ast_party_connected_line_free(&connected_line); return -1; } @@ -4169,7 +4169,6 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a int diff; int hasfeatures=0; int hadfeatures=0; - int autoloopflag; int sendingdtmfdigit = 0; int we_disabled_peer_cdr = 0; struct ast_option_header *aoh; @@ -4179,7 +4178,8 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a struct ast_cdr *new_chan_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */ struct ast_cdr *new_peer_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */ struct ast_silence_generator *silgen = NULL; - const char *h_context; + /*! TRUE if h-exten or hangup handlers run. */ + int hangup_run = 0; pbx_builtin_setvar_helper(chan, "BRIDGEPEER", ast_channel_name(peer)); pbx_builtin_setvar_helper(peer, "BRIDGEPEER", ast_channel_name(chan)); @@ -4611,105 +4611,82 @@ before_you_go: config->end_bridge_callback(config->end_bridge_callback_data); } - /* run the hangup exten on the chan object IFF it was NOT involved in a parking situation - * if it were, then chan belongs to a different thread now, and might have been hung up long - * ago. - */ - if (ast_test_flag(&config->features_caller, AST_FEATURE_NO_H_EXTEN)) { - h_context = NULL; - } else if (ast_exists_extension(chan, ast_channel_context(chan), "h", 1, - S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) { - h_context = ast_channel_context(chan); - } else if (!ast_strlen_zero(ast_channel_macrocontext(chan)) - && ast_exists_extension(chan, ast_channel_macrocontext(chan), "h", 1, - S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) { - h_context = ast_channel_macrocontext(chan); - } else { - h_context = NULL; - } - if (h_context) { + if (!ast_test_flag(&config->features_caller, AST_FEATURE_NO_H_EXTEN)) { struct ast_cdr *swapper = NULL; char savelastapp[AST_MAX_EXTENSION]; char savelastdata[AST_MAX_EXTENSION]; char save_context[AST_MAX_CONTEXT]; char save_exten[AST_MAX_EXTENSION]; int save_prio; - int found = 0; /* set if we find at least one match */ - int spawn_error = 0; - /* - * Make sure that the channel is marked as hungup since we are - * going to run the "h" exten on it. - */ - ast_softhangup(chan, AST_SOFTHANGUP_APPUNLOAD); - - autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP); - ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP); - if (bridge_cdr && ast_opt_end_cdr_before_h_exten) { - ast_cdr_end(bridge_cdr); - } - - /* swap the bridge cdr and the chan cdr for a moment, and let the endbridge - dialplan code operate on it */ ast_channel_lock(chan); if (bridge_cdr) { + /* + * Swap the bridge_cdr and the chan cdr for a moment, and let + * the hangup dialplan code operate on it. + */ swapper = ast_channel_cdr(chan); + ast_channel_cdr_set(chan, bridge_cdr); + + /* protect the lastapp/lastdata against the effects of the hangup/dialplan code */ ast_copy_string(savelastapp, bridge_cdr->lastapp, sizeof(bridge_cdr->lastapp)); ast_copy_string(savelastdata, bridge_cdr->lastdata, sizeof(bridge_cdr->lastdata)); - ast_channel_cdr_set(chan, bridge_cdr); } ast_copy_string(save_context, ast_channel_context(chan), sizeof(save_context)); ast_copy_string(save_exten, ast_channel_exten(chan), sizeof(save_exten)); save_prio = ast_channel_priority(chan); - if (h_context != ast_channel_context(chan)) { - ast_channel_context_set(chan, h_context); - } - ast_channel_exten_set(chan, "h"); - ast_channel_priority_set(chan, 1); ast_channel_unlock(chan); - while ((spawn_error = ast_spawn_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), - ast_channel_priority(chan), - S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL), - &found, 1)) == 0) { - ast_channel_priority_set(chan, ast_channel_priority(chan) + 1); - } - if (found && spawn_error) { - /* Something bad happened, or a hangup has been requested. */ - ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), ast_channel_name(chan)); - ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), ast_channel_name(chan)); - } + ast_autoservice_start(peer); + if (ast_exists_extension(chan, ast_channel_context(chan), "h", 1, + S_COR(ast_channel_caller(chan)->id.number.valid, + ast_channel_caller(chan)->id.number.str, NULL))) { + ast_pbx_h_exten_run(chan, ast_channel_context(chan)); + hangup_run = 1; + } else if (!ast_strlen_zero(ast_channel_macrocontext(chan)) + && ast_exists_extension(chan, ast_channel_macrocontext(chan), "h", 1, + S_COR(ast_channel_caller(chan)->id.number.valid, + ast_channel_caller(chan)->id.number.str, NULL))) { + ast_pbx_h_exten_run(chan, ast_channel_macrocontext(chan)); + hangup_run = 1; + } + if (ast_pbx_hangup_handler_run(chan)) { + /* Indicate hangup handlers were run. */ + hangup_run = 1; + } + ast_autoservice_stop(peer); - /* swap it back */ ast_channel_lock(chan); + + /* swap it back */ ast_channel_context_set(chan, save_context); ast_channel_exten_set(chan, save_exten); ast_channel_priority_set(chan, save_prio); if (bridge_cdr) { if (ast_channel_cdr(chan) == bridge_cdr) { ast_channel_cdr_set(chan, swapper); + + /* Restore the lastapp/lastdata */ + ast_copy_string(bridge_cdr->lastapp, savelastapp, sizeof(bridge_cdr->lastapp)); + ast_copy_string(bridge_cdr->lastdata, savelastdata, sizeof(bridge_cdr->lastdata)); } else { bridge_cdr = NULL; } } - /* An "h" exten has been run, so indicate that one has been run. */ - ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_RUN); ast_channel_unlock(chan); - - /* protect the lastapp/lastdata against the effects of the hangup/dialplan code */ - if (bridge_cdr) { - ast_copy_string(bridge_cdr->lastapp, savelastapp, sizeof(bridge_cdr->lastapp)); - ast_copy_string(bridge_cdr->lastdata, savelastdata, sizeof(bridge_cdr->lastdata)); - } - ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP); } /* obey the NoCDR() wishes. -- move the DISABLED flag to the bridge CDR if it was set on the channel during the bridge... */ new_chan_cdr = pick_unlocked_cdr(ast_channel_cdr(chan)); /* the proper chan cdr, if there are forked cdrs */ - /* If the channel CDR has been modified during the call, record the changes in the bridge cdr, - * BUT, if we've gone through the h extension block above, the CDR got swapped so don't overwrite - * what was done in the h extension. What a mess. This is why you never touch CDR code. */ - if (new_chan_cdr && bridge_cdr && !h_context) { + + /* + * If the channel CDR has been modified during the call, record + * the changes in the bridge cdr, BUT, if hangup_run, the CDR + * got swapped so don't overwrite what was done in the + * h-extension or hangup handlers. What a mess. This is why + * you never touch CDR code. + */ + if (new_chan_cdr && bridge_cdr && !hangup_run) { ast_cdr_copy_vars(bridge_cdr, new_chan_cdr); ast_copy_string(bridge_cdr->userfield, new_chan_cdr->userfield, sizeof(bridge_cdr->userfield)); bridge_cdr->amaflags = new_chan_cdr->amaflags; @@ -5540,7 +5517,7 @@ static int parked_call_exec(struct ast_channel *chan, const char *data) break; } if (res) { - ast_hangup(peer); + ast_autoservice_chan_hangup_peer(chan, peer); parkinglot_unref(parkinglot); return -1; } @@ -5549,7 +5526,7 @@ static int parked_call_exec(struct ast_channel *chan, const char *data) res = ast_channel_make_compatible(chan, peer); if (res < 0) { ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", ast_channel_name(chan), ast_channel_name(peer)); - ast_hangup(peer); + ast_autoservice_chan_hangup_peer(chan, peer); parkinglot_unref(parkinglot); return -1; } @@ -5601,7 +5578,7 @@ static int parked_call_exec(struct ast_channel *chan, const char *data) ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(peer)); /* Simulate the PBX hanging up */ - ast_hangup(peer); + ast_autoservice_chan_hangup_peer(chan, peer); } else { if (ast_stream_and_wait(chan, "pbx-invalidpark", "")) { ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", @@ -8014,7 +7991,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data) "Channel2: %s\r\n", ast_channel_name(chan), ast_channel_name(final_dest_chan)); /* Maybe we should return this channel to the PBX? */ - ast_hangup(final_dest_chan); + ast_autoservice_chan_hangup_peer(chan, final_dest_chan); pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "INCOMPATIBLE"); current_dest_chan = ast_channel_unref(current_dest_chan); @@ -8088,7 +8065,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data) goto_opt = ast_goto_if_exists(final_dest_chan, caller_context, caller_extension, caller_priority + 1); } if (goto_opt || ast_pbx_start(final_dest_chan)) { - ast_hangup(final_dest_chan); + ast_autoservice_chan_hangup_peer(chan, final_dest_chan); } } else if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) { ast_debug(1, "starting new PBX in %s,%s,%d for chan %s\n", @@ -8097,16 +8074,16 @@ static int bridge_exec(struct ast_channel *chan, const char *data) if (ast_pbx_start(final_dest_chan)) { ast_log(LOG_WARNING, "FAILED continuing PBX on dest chan %s\n", ast_channel_name(final_dest_chan)); - ast_hangup(final_dest_chan); + ast_autoservice_chan_hangup_peer(chan, final_dest_chan); } else { ast_debug(1, "SUCCESS continuing PBX on chan %s\n", ast_channel_name(final_dest_chan)); } } else { - ast_hangup(final_dest_chan); + ast_autoservice_chan_hangup_peer(chan, final_dest_chan); } } else { ast_debug(1, "chan %s was hungup\n", ast_channel_name(final_dest_chan)); - ast_hangup(final_dest_chan); + ast_autoservice_chan_hangup_peer(chan, final_dest_chan); } done: ast_free((char *) bconfig.warning_sound); diff --git a/main/pbx.c b/main/pbx.c index 006549c12..db929519e 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -5342,6 +5342,360 @@ int ast_spawn_extension(struct ast_channel *c, const char *context, const char * return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN, found, combined_find_spawn); } +void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context) +{ + int autoloopflag; + int found; + int spawn_error; + + ast_channel_lock(chan); + + if (ast_channel_cdr(chan) && ast_opt_end_cdr_before_h_exten) { + ast_cdr_end(ast_channel_cdr(chan)); + } + + /* Set h exten location */ + if (context != ast_channel_context(chan)) { + ast_channel_context_set(chan, context); + } + ast_channel_exten_set(chan, "h"); + ast_channel_priority_set(chan, 1); + + /* + * Make sure that the channel is marked as hungup since we are + * going to run the h exten on it. + */ + ast_softhangup_nolock(chan, AST_SOFTHANGUP_APPUNLOAD); + + /* Save autoloop flag */ + autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP); + ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP); + ast_channel_unlock(chan); + + for (;;) { + spawn_error = ast_spawn_extension(chan, ast_channel_context(chan), + ast_channel_exten(chan), ast_channel_priority(chan), + S_COR(ast_channel_caller(chan)->id.number.valid, + ast_channel_caller(chan)->id.number.str, NULL), &found, 1); + + ast_channel_lock(chan); + if (spawn_error) { + /* The code after the loop needs the channel locked. */ + break; + } + ast_channel_priority_set(chan, ast_channel_priority(chan) + 1); + ast_channel_unlock(chan); + } + if (found && spawn_error) { + /* Something bad happened, or a hangup has been requested. */ + ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", + ast_channel_context(chan), ast_channel_exten(chan), + ast_channel_priority(chan), ast_channel_name(chan)); + ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", + ast_channel_context(chan), ast_channel_exten(chan), + ast_channel_priority(chan), ast_channel_name(chan)); + } + + /* An "h" exten has been run, so indicate that one has been run. */ + ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_RUN); + + /* Restore autoloop flag */ + ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP); + ast_channel_unlock(chan); +} + +int ast_pbx_hangup_handler_run(struct ast_channel *chan) +{ + struct ast_hangup_handler_list *handlers; + struct ast_hangup_handler *h_handler; + + ast_channel_lock(chan); + handlers = ast_channel_hangup_handlers(chan); + if (AST_LIST_EMPTY(handlers)) { + ast_channel_unlock(chan); + return 0; + } + + if (ast_channel_cdr(chan) && ast_opt_end_cdr_before_h_exten) { + ast_cdr_end(ast_channel_cdr(chan)); + } + + /* + * Make sure that the channel is marked as hungup since we are + * going to run the hangup handlers on it. + */ + ast_softhangup_nolock(chan, AST_SOFTHANGUP_APPUNLOAD); + + for (;;) { + handlers = ast_channel_hangup_handlers(chan); + h_handler = AST_LIST_REMOVE_HEAD(handlers, node); + if (!h_handler) { + 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); + ast_channel_unlock(chan); + + ast_app_exec_sub(NULL, chan, h_handler->args, 1); + ast_free(h_handler); + + ast_channel_lock(chan); + } + ast_channel_unlock(chan); + return 1; +} + +void ast_pbx_hangup_handler_init(struct ast_channel *chan) +{ + struct ast_hangup_handler_list *handlers; + + handlers = ast_channel_hangup_handlers(chan); + AST_LIST_HEAD_INIT_NOLOCK(handlers); +} + +void ast_pbx_hangup_handler_destroy(struct ast_channel *chan) +{ + struct ast_hangup_handler_list *handlers; + struct ast_hangup_handler *h_handler; + + ast_channel_lock(chan); + + /* Get rid of each of the hangup handlers on the channel */ + handlers = ast_channel_hangup_handlers(chan); + while ((h_handler = AST_LIST_REMOVE_HEAD(handlers, node))) { + ast_free(h_handler); + } + + ast_channel_unlock(chan); +} + +int ast_pbx_hangup_handler_pop(struct ast_channel *chan) +{ + struct ast_hangup_handler_list *handlers; + struct ast_hangup_handler *h_handler; + + ast_channel_lock(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); + } + ast_channel_unlock(chan); + if (h_handler) { + ast_free(h_handler); + return 1; + } + return 0; +} + +void ast_pbx_hangup_handler_push(struct ast_channel *chan, const char *handler) +{ + struct ast_hangup_handler_list *handlers; + struct ast_hangup_handler *h_handler; + const char *expanded_handler; + + if (ast_strlen_zero(handler)) { + return; + } + + expanded_handler = ast_app_expand_sub_args(chan, handler); + if (!expanded_handler) { + return; + } + h_handler = ast_malloc(sizeof(*h_handler) + 1 + strlen(expanded_handler)); + if (!h_handler) { + ast_free((char *) expanded_handler); + return; + } + strcpy(h_handler->args, expanded_handler);/* Safe */ + ast_free((char *) expanded_handler); + + ast_channel_lock(chan); + + 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); + + ast_channel_unlock(chan); +} + +#define HANDLER_FORMAT "%-30s %s\n" + +/*! + * \internal + * \brief CLI output the hangup handler headers. + * \since 11.0 + * + * \param fd CLI file descriptor to use. + * + * \return Nothing + */ +static void ast_pbx_hangup_handler_headers(int fd) +{ + ast_cli(fd, HANDLER_FORMAT, "Channel", "Handler"); +} + +/*! + * \internal + * \brief CLI output the channel hangup handlers. + * \since 11.0 + * + * \param fd CLI file descriptor to use. + * \param chan Channel to show hangup handlers. + * + * \return Nothing + */ +static void ast_pbx_hangup_handler_show(int fd, struct ast_channel *chan) +{ + struct ast_hangup_handler_list *handlers; + struct ast_hangup_handler *h_handler; + int first = 1; + + ast_channel_lock(chan); + handlers = ast_channel_hangup_handlers(chan); + AST_LIST_TRAVERSE(handlers, h_handler, node) { + ast_cli(fd, HANDLER_FORMAT, first ? ast_channel_name(chan) : "", h_handler->args); + first = 0; + } + ast_channel_unlock(chan); +} + +/* + * \brief 'show hanguphandlers <channel>' CLI command implementation function... + */ +static char *handle_show_hangup_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + struct ast_channel *chan; + + switch (cmd) { + case CLI_INIT: + e->command = "core show hanguphandlers"; + e->usage = + "Usage: core show hanguphandlers <channel>\n" + " Show hangup handlers of a specified channel.\n"; + return NULL; + case CLI_GENERATE: + return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args); + } + + if (a->argc < 4) { + return CLI_SHOWUSAGE; + } + + chan = ast_channel_get_by_name(a->argv[3]); + if (!chan) { + ast_cli(a->fd, "Channel does not exist.\n"); + return CLI_FAILURE; + } + + ast_pbx_hangup_handler_headers(a->fd); + ast_pbx_hangup_handler_show(a->fd, chan); + + ast_channel_unref(chan); + + return CLI_SUCCESS; +} + +/* + * \brief 'show hanguphandlers all' CLI command implementation function... + */ +static char *handle_show_hangup_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + struct ast_channel_iterator *iter; + struct ast_channel *chan; + + switch (cmd) { + case CLI_INIT: + e->command = "core show hanguphandlers all"; + e->usage = + "Usage: core show hanguphandlers all\n" + " Show hangup handlers for all channels.\n"; + return NULL; + case CLI_GENERATE: + return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args); + } + + if (a->argc < 4) { + return CLI_SHOWUSAGE; + } + + iter = ast_channel_iterator_all_new(); + if (!iter) { + return CLI_FAILURE; + } + + ast_pbx_hangup_handler_headers(a->fd); + for (; (chan = ast_channel_iterator_next(iter)); ast_channel_unref(chan)) { + ast_pbx_hangup_handler_show(a->fd, chan); + } + ast_channel_iterator_destroy(iter); + + return CLI_SUCCESS; +} + /*! helper function to set extension and priority */ static void set_ext_pri(struct ast_channel *c, const char *exten, int pri) { @@ -5680,27 +6034,15 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c, if (!args || !args->no_hangup_chan) { ast_softhangup(c, AST_SOFTHANGUP_APPUNLOAD); - } - - if ((!args || !args->no_hangup_chan) - && !ast_test_flag(ast_channel_flags(c), AST_FLAG_BRIDGE_HANGUP_RUN) - && ast_exists_extension(c, ast_channel_context(c), "h", 1, - S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) { - set_ext_pri(c, "h", 1); - if (ast_channel_cdr(c) && ast_opt_end_cdr_before_h_exten) { - ast_cdr_end(ast_channel_cdr(c)); - } - while ((res = ast_spawn_extension(c, ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), - S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL), - &found, 1)) == 0) { - ast_channel_priority_set(c, ast_channel_priority(c) + 1); - } - if (found && res) { - /* Something bad happened, or a hangup has been requested. */ - ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c)); - ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c)); + if (!ast_test_flag(ast_channel_flags(c), AST_FLAG_BRIDGE_HANGUP_RUN) + && ast_exists_extension(c, ast_channel_context(c), "h", 1, + S_COR(ast_channel_caller(c)->id.number.valid, + ast_channel_caller(c)->id.number.str, NULL))) { + ast_pbx_h_exten_run(c, ast_channel_context(c)); } + ast_pbx_hangup_handler_run(c); } + ast_set2_flag(ast_channel_flags(c), autoloopflag, AST_FLAG_IN_AUTOLOOP); ast_clear_flag(ast_channel_flags(c), AST_FLAG_BRIDGE_HANGUP_RUN); /* from one round to the next, make sure this gets cleared */ pbx_destroy(ast_channel_pbx(c)); @@ -7590,6 +7932,8 @@ static struct ast_cli_entry pbx_cli[] = { #endif AST_CLI_DEFINE(handle_show_chanvar, "Show channel variables"), AST_CLI_DEFINE(handle_show_function, "Describe a specific dialplan function"), + AST_CLI_DEFINE(handle_show_hangup_all, "Show hangup handlers of all channels"), + AST_CLI_DEFINE(handle_show_hangup_channel, "Show hangup handlers of a specified channel"), AST_CLI_DEFINE(handle_show_application, "Describe a specific dialplan application"), AST_CLI_DEFINE(handle_set_global, "Set global dialplan variable"), AST_CLI_DEFINE(handle_set_chanvar, "Set a channel variable"), |