Index: include/asterisk/channel.h =================================================================== --- include/asterisk/channel.h (revision 377984) +++ include/asterisk/channel.h (working copy) @@ -888,6 +888,12 @@ * some non-traditional dialplans (like AGI) to continue to function. */ AST_FLAG_DISABLE_WORKAROUNDS = (1 << 20), + /*! + * This flag indicates that a dual channel redirect is in + * progress. The bridge needs to wait until the flag is cleared + * to continue. + */ + AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT = (1 << 21), }; /*! \brief ast_bridge_config flags */ Index: main/manager.c =================================================================== --- main/manager.c (revision 377984) +++ main/manager.c (working copy) @@ -3677,6 +3677,7 @@ /*! \brief action_redirect: The redirect manager command */ static int action_redirect(struct mansession *s, const struct message *m) { + char buf[256]; const char *name = astman_get_header(m, "Channel"); const char *name2 = astman_get_header(m, "ExtraChannel"); const char *exten = astman_get_header(m, "Exten"); @@ -3685,8 +3686,10 @@ const char *context2 = astman_get_header(m, "ExtraContext"); const char *priority = astman_get_header(m, "Priority"); const char *priority2 = astman_get_header(m, "ExtraPriority"); - struct ast_channel *chan, *chan2 = NULL; - int pi, pi2 = 0; + struct ast_channel *chan; + struct ast_channel *chan2; + int pi = 0; + int pi2 = 0; int res; if (ast_strlen_zero(name)) { @@ -3694,84 +3697,134 @@ return 0; } - if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) { - if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) { - astman_send_error(s, m, "Invalid priority"); - return 0; - } + if (ast_strlen_zero(context)) { + astman_send_error(s, m, "Context not specified"); + return 0; } + if (ast_strlen_zero(exten)) { + astman_send_error(s, m, "Exten not specified"); + return 0; + } + if (ast_strlen_zero(priority)) { + astman_send_error(s, m, "Priority not specified"); + return 0; + } + if (sscanf(priority, "%30d", &pi) != 1) { + pi = ast_findlabel_extension(NULL, context, exten, priority, NULL); + } + if (pi < 1) { + astman_send_error(s, m, "Priority is invalid"); + return 0; + } - if (!ast_strlen_zero(priority2) && (sscanf(priority2, "%30d", &pi2) != 1)) { - if ((pi2 = ast_findlabel_extension(NULL, context2, exten2, priority2, NULL)) < 1) { - astman_send_error(s, m, "Invalid ExtraPriority"); + if (!ast_strlen_zero(name2) && !ast_strlen_zero(context2)) { + /* We have an ExtraChannel and an ExtraContext */ + if (ast_strlen_zero(exten2)) { + astman_send_error(s, m, "ExtraExten not specified"); return 0; } + if (ast_strlen_zero(priority2)) { + astman_send_error(s, m, "ExtraPriority not specified"); + return 0; + } + if (sscanf(priority2, "%30d", &pi2) != 1) { + pi2 = ast_findlabel_extension(NULL, context2, exten2, priority2, NULL); + } + if (pi2 < 1) { + astman_send_error(s, m, "ExtraPriority is invalid"); + return 0; + } } - if (!(chan = ast_channel_get_by_name(name))) { - char buf[256]; + chan = ast_channel_get_by_name(name); + if (!chan) { snprintf(buf, sizeof(buf), "Channel does not exist: %s", name); astman_send_error(s, m, buf); return 0; } - if (ast_check_hangup_locked(chan)) { astman_send_error(s, m, "Redirect failed, channel not up."); chan = ast_channel_unref(chan); return 0; } - if (!ast_strlen_zero(name2)) { - chan2 = ast_channel_get_by_name(name2); + if (ast_strlen_zero(name2)) { + /* Single channel redirect in progress. */ + if (ast_channel_pbx(chan)) { + ast_channel_lock(chan); + /* don't let the after-bridge code run the h-exten */ + ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT); + ast_channel_unlock(chan); + } + res = ast_async_goto(chan, context, exten, pi); + if (!res) { + astman_send_ack(s, m, "Redirect successful"); + } else { + astman_send_error(s, m, "Redirect failed"); + } + chan = ast_channel_unref(chan); + return 0; } - if (chan2 && ast_check_hangup_locked(chan2)) { + chan2 = ast_channel_get_by_name(name2); + if (!chan2) { + snprintf(buf, sizeof(buf), "ExtraChannel does not exist: %s", name2); + astman_send_error(s, m, buf); + chan = ast_channel_unref(chan); + return 0; + } + if (ast_check_hangup_locked(chan2)) { astman_send_error(s, m, "Redirect failed, extra channel not up."); + chan2 = ast_channel_unref(chan2); chan = ast_channel_unref(chan); - chan2 = ast_channel_unref(chan2); return 0; } + /* Dual channel redirect in progress. */ if (ast_channel_pbx(chan)) { ast_channel_lock(chan); - ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */ + /* don't let the after-bridge code run the h-exten */ + ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT + | AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT); ast_channel_unlock(chan); } - + if (ast_channel_pbx(chan2)) { + ast_channel_lock(chan2); + /* don't let the after-bridge code run the h-exten */ + ast_set_flag(ast_channel_flags(chan2), AST_FLAG_BRIDGE_HANGUP_DONT + | AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT); + ast_channel_unlock(chan2); + } res = ast_async_goto(chan, context, exten, pi); if (!res) { - if (!ast_strlen_zero(name2)) { - if (chan2) { - if (ast_channel_pbx(chan2)) { - ast_channel_lock(chan2); - ast_set_flag(ast_channel_flags(chan2), AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */ - ast_channel_unlock(chan2); - } - if (!ast_strlen_zero(context2)) { - res = ast_async_goto(chan2, context2, exten2, pi2); - } else { - res = ast_async_goto(chan2, context, exten, pi); - } - } else { - res = -1; - } - if (!res) { - astman_send_ack(s, m, "Dual Redirect successful"); - } else { - astman_send_error(s, m, "Secondary redirect failed"); - } + if (!ast_strlen_zero(context2)) { + res = ast_async_goto(chan2, context2, exten2, pi2); } else { - astman_send_ack(s, m, "Redirect successful"); + res = ast_async_goto(chan2, context, exten, pi); } + if (!res) { + astman_send_ack(s, m, "Dual Redirect successful"); + } else { + astman_send_error(s, m, "Secondary redirect failed"); + } } else { astman_send_error(s, m, "Redirect failed"); } - chan = ast_channel_unref(chan); - if (chan2) { - chan2 = ast_channel_unref(chan2); + /* Release the bridge wait. */ + if (ast_channel_pbx(chan)) { + ast_channel_lock(chan); + ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT); + ast_channel_unlock(chan); } + if (ast_channel_pbx(chan2)) { + ast_channel_lock(chan2); + ast_clear_flag(ast_channel_flags(chan2), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT); + ast_channel_unlock(chan2); + } + chan2 = ast_channel_unref(chan2); + chan = ast_channel_unref(chan); return 0; } Index: main/features.c =================================================================== --- main/features.c (revision 377984) +++ main/features.c (working copy) @@ -4722,6 +4722,11 @@ silgen = NULL; } + /* Wait for any dual redirect to complete. */ + while (ast_test_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT)) { + sched_yield(); + } + if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT)) { ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT); /* its job is done */ if (bridge_cdr) {