summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/asterisk/channel.h15
-rw-r--r--main/features.c5
-rw-r--r--main/manager.c139
3 files changed, 112 insertions, 47 deletions
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 9b408660b..858657ab0 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -903,12 +903,19 @@ enum {
* some non-traditional dialplans (like AGI) to continue to function.
*/
AST_FLAG_DISABLE_WORKAROUNDS = (1 << 20),
- /*! Disable device state event caching. This allows allows channel
- * drivers to selectively prevent device state events from being cached
- * by certain channels such as anonymous calls which have no persistent
- * represenatation that can be tracked.
+ /*!
+ * Disable device state event caching. This allows channel
+ * drivers to selectively prevent device state events from being
+ * cached by certain channels such as anonymous calls which have
+ * no persistent represenatation that can be tracked.
*/
AST_FLAG_DISABLE_DEVSTATE_CACHE = (1 << 21),
+ /*!
+ * 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 << 22),
};
/*! \brief ast_bridge_config flags */
diff --git a/main/features.c b/main/features.c
index 44f140e16..2f2716eb7 100644
--- a/main/features.c
+++ b/main/features.c
@@ -4772,6 +4772,11 @@ before_you_go:
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) {
diff --git a/main/manager.c b/main/manager.c
index ba5beb42e..99d03fbcf 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -3709,6 +3709,7 @@ static int action_sendtext(struct mansession *s, const struct message *m)
/*! \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");
@@ -3717,8 +3718,10 @@ static int action_redirect(struct mansession *s, const struct message *m)
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)) {
@@ -3726,84 +3729,134 @@ static int action_redirect(struct mansession *s, const struct message *m)
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)) {
- astman_send_error(s, m, "Redirect failed, extra channel not up.");
+ 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);
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;
}