diff --git a/main/features.c b/main/features.c index 8f16526923e295b05d0c7c6a29418a32ce17b723..076988d28dea12413b7f6548a7d708ce79c8cee8 100644 --- a/main/features.c +++ b/main/features.c @@ -4064,55 +4064,69 @@ static int action_bridge(struct mansession *s, const struct message *m) struct ast_bridge_thread_obj *tobj = NULL; /* make sure valid channels were specified */ - if (!ast_strlen_zero(channela) && !ast_strlen_zero(channelb)) { - chana = ast_get_channel_by_name_prefix_locked(channela, strlen(channela)); - chanb = ast_get_channel_by_name_prefix_locked(channelb, strlen(channelb)); - if (chana) - ast_channel_unlock(chana); - if (chanb) - ast_channel_unlock(chanb); - - /* send errors if any of the channels could not be found/locked */ - if (!chana) { - char buf[256]; - snprintf(buf, sizeof(buf), "Channel1 does not exists: %s", channela); - astman_send_error(s, m, buf); - return 0; - } - if (!chanb) { - char buf[256]; - snprintf(buf, sizeof(buf), "Channel2 does not exists: %s", channelb); - astman_send_error(s, m, buf); - return 0; - } - } else { + if (ast_strlen_zero(channela) || ast_strlen_zero(channelb)) { astman_send_error(s, m, "Missing channel parameter in request"); return 0; } + /* The same code must be executed for chana and chanb. To avoid a + * theoretical deadlock, this code is separated so both chana and chanb will + * not hold locks at the same time. */ + + /* Start with chana */ + chana = ast_get_channel_by_name_prefix_locked(channela, strlen(channela)); + + /* send errors if any of the channels could not be found/locked */ + if (!chana) { + char buf[256]; + snprintf(buf, sizeof(buf), "Channel1 does not exists: %s", channela); + astman_send_error(s, m, buf); + return 0; + } + /* Answer the channels if needed */ if (chana->_state != AST_STATE_UP) ast_answer(chana); - if (chanb->_state != AST_STATE_UP) - ast_answer(chanb); /* create the placeholder channels and grab the other channels */ if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, "Bridge/%s", chana->name))) { astman_send_error(s, m, "Unable to create temporary channel!"); + ast_channel_unlock(chana); return 1; } + do_bridge_masquerade(chana, tmpchana); + ast_channel_unlock(chana); + chana = NULL; + + /* now do chanb */ + chanb = ast_get_channel_by_name_prefix_locked(channelb, strlen(channelb)); + /* send errors if any of the channels could not be found/locked */ + if (!chanb) { + char buf[256]; + snprintf(buf, sizeof(buf), "Channel2 does not exists: %s", channelb); + ast_hangup(tmpchana); + astman_send_error(s, m, buf); + return 0; + } + + /* Answer the channels if needed */ + if (chanb->_state != AST_STATE_UP) + ast_answer(chanb); + + /* create the placeholder channels and grab the other channels */ if (!(tmpchanb = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, "Bridge/%s", chanb->name))) { astman_send_error(s, m, "Unable to create temporary channels!"); - ast_channel_free(tmpchana); + ast_hangup(tmpchana); + ast_channel_unlock(chanb); return 1; } - - do_bridge_masquerade(chana, tmpchana); do_bridge_masquerade(chanb, tmpchanb); - + ast_channel_unlock(chanb); + chanb = NULL; + /* make the channels compatible, send error if we fail doing so */ if (ast_channel_make_compatible(tmpchana, tmpchanb)) { ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for manager bridge\n", tmpchana->name, tmpchanb->name); @@ -4134,7 +4148,7 @@ static int action_bridge(struct mansession *s, const struct message *m) tobj->chan = tmpchana; tobj->peer = tmpchanb; tobj->return_to_pbx = 1; - + if (ast_true(playtone)) { if (!ast_strlen_zero(xfersound) && !ast_streamfile(tmpchanb, xfersound, tmpchanb->language)) { if (ast_waitstream(tmpchanb, "") < 0) @@ -4452,7 +4466,6 @@ static int bridge_exec(struct ast_channel *chan, void *data) pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "NONEXISTENT"); return 0; } - ast_channel_unlock(current_dest_chan); /* answer the channel if needed */ if (current_dest_chan->_state != AST_STATE_UP) @@ -4470,6 +4483,8 @@ static int bridge_exec(struct ast_channel *chan, void *data) } do_bridge_masquerade(current_dest_chan, final_dest_chan); + ast_channel_unlock(current_dest_chan); + /* now current_dest_chan is a ZOMBIE and with softhangup set to 1 and final_dest_chan is our end point */ /* try to make compatible, send error if we fail */ if (ast_channel_make_compatible(chan, final_dest_chan) < 0) {