diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 9cf313fc43..e5f792f1f4 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -4717,4 +4717,21 @@ enum ast_channel_error { */ enum ast_channel_error ast_channel_errno(void); +/*! + * \brief Am I currently running an intercept dialplan routine. + * \since 13.14.0 + * + * \details + * A dialplan intercept routine is equivalent to an interrupt + * routine. As such, the routine must be done quickly and you + * do not have access to the media stream. These restrictions + * are necessary because the media stream is the responsibility + * of some other code and interfering with or delaying that + * processing is bad. + * + * \retval 0 Not in an intercept routine. + * \retval 1 In an intercept routine. + */ +int ast_channel_get_intercept_mode(void); + #endif /* _ASTERISK_CHANNEL_H */ diff --git a/main/channel.c b/main/channel.c index 7c8d3a9890..1c7743a2ff 100644 --- a/main/channel.c +++ b/main/channel.c @@ -10300,6 +10300,36 @@ void ast_channel_queue_redirecting_update(struct ast_channel *chan, const struct ast_queue_control_data(chan, AST_CONTROL_REDIRECTING, data, datalen); } +/*! + * Storage to determine if the current thread is running an intercept dialplan routine. + */ +AST_THREADSTORAGE_RAW(in_intercept_routine); + +/*! + * \internal + * \brief Set the current intercept dialplan routine status mode. + * \since 13.14.0 + * + * \param in_intercept_mode New intercept mode. (Non-zero if in intercept mode) + * + * \return Nothing + */ +static void channel_set_intercept_mode(int in_intercept_mode) +{ + int status; + + status = ast_threadstorage_set_ptr(&in_intercept_routine, + in_intercept_mode ? (void *) 1 : (void *) 0); + if (status) { + ast_log(LOG_ERROR, "Failed to set dialplan intercept mode\n"); + } +} + +int ast_channel_get_intercept_mode(void) +{ + return ast_threadstorage_get_ptr(&in_intercept_routine) ? 1 : 0; +} + int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const void *connected_info, int is_caller, int is_frame) { static int deprecation_warning = 0; @@ -10335,7 +10365,9 @@ int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struc } ast_channel_unlock(macro_chan); + channel_set_intercept_mode(1); retval = ast_app_run_macro(autoservice_chan, macro_chan, macro, macro_args); + channel_set_intercept_mode(0); if (!retval) { struct ast_party_connected_line saved_connected; @@ -10385,7 +10417,9 @@ int ast_channel_redirecting_macro(struct ast_channel *autoservice_chan, struct a } ast_channel_unlock(macro_chan); + channel_set_intercept_mode(1); retval = ast_app_run_macro(autoservice_chan, macro_chan, macro, macro_args); + channel_set_intercept_mode(0); if (!retval) { struct ast_party_redirecting saved_redirecting; @@ -10428,7 +10462,9 @@ int ast_channel_connected_line_sub(struct ast_channel *autoservice_chan, struct } ast_channel_unlock(sub_chan); + channel_set_intercept_mode(1); retval = ast_app_run_sub(autoservice_chan, sub_chan, sub, sub_args, 0); + channel_set_intercept_mode(0); if (!retval) { struct ast_party_connected_line saved_connected; @@ -10471,7 +10507,9 @@ int ast_channel_redirecting_sub(struct ast_channel *autoservice_chan, struct ast } ast_channel_unlock(sub_chan); + channel_set_intercept_mode(1); retval = ast_app_run_sub(autoservice_chan, sub_chan, sub, sub_args, 0); + channel_set_intercept_mode(0); if (!retval) { struct ast_party_redirecting saved_redirecting; diff --git a/res/res_agi.c b/res/res_agi.c index 5e047d4c3d..557f349715 100644 --- a/res/res_agi.c +++ b/res/res_agi.c @@ -4073,7 +4073,7 @@ static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, ch break; } } else if (c) { - ami_res = "Command Not Permitted on a dead channel"; + ami_res = "Command Not Permitted on a dead channel or intercept routine"; resultcode = 511; ast_agi_send(agi->fd, chan, "%d %s\n", resultcode, ami_res); @@ -4109,6 +4109,8 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi const char *sighup_str; const char *exit_on_hangup_str; int exit_on_hangup; + /*! Running in an interception routine is like DeadAGI mode. No touchy the channel frames. */ + int in_intercept = ast_channel_get_intercept_mode(); ast_channel_lock(chan); sighup_str = pbx_builtin_getvar_helper(chan, "AGISIGHUP"); @@ -4143,7 +4145,7 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi } } ms = -1; - if (dead) { + if (dead || in_intercept) { c = ast_waitfor_nandfds(&chan, 0, &agi->ctrl, 1, NULL, &outfd, &ms); } else if (!ast_check_hangup(chan)) { c = ast_waitfor_nandfds(&chan, 1, &agi->ctrl, 1, NULL, &outfd, &ms); @@ -4221,10 +4223,10 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi if (agidebug) ast_verbose("<%s>AGI Rx << %s\n", ast_channel_name(chan), buf); - cmd_status = agi_handle_command(chan, agi, buf, dead); + cmd_status = agi_handle_command(chan, agi, buf, dead || in_intercept); switch (cmd_status) { case AGI_RESULT_FAILURE: - if (dead || !ast_check_hangup(chan)) { + if (dead || in_intercept || !ast_check_hangup(chan)) { /* The failure was not because of a hangup. */ returnstatus = AGI_RESULT_FAILURE; }