From: Richard Mudgett Date: Sat, 23 Jun 2012 00:12:27 +0000 (+0000) Subject: Fix Bridge application and AMI Bridge action error handling. X-Git-Tag: 10.7.0-rc1~3^2~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=58dc141755b4c14c685b337a60a7c680d446d58e;p=thirdparty%2Fasterisk.git Fix Bridge application and AMI Bridge action error handling. * Fix AMI Bridge action disconnecting the AMI link on error. * Fix AMI Bridge action and Bridge application not checking if their masquerades were successful. * Fix Bridge application running the h-exten when it should not. * Made do_bridge_masquerade() return if the masquerade was successful so the Bridge application and AMI Bridge action could deal with it correctly. * Made bridge_call_thread_launch() hangup the passed in channels if the bridge_call_thread fails to start. Those channels would have been orphaned. * Made builtin_atxfer() check the success of the transfer masquerade setup. ........ Merged revisions 369282 from http://svn.asterisk.org/svn/asterisk/branches/1.8 git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/10@369283 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- diff --git a/main/features.c b/main/features.c index f6b6ed030a..7282eca441 100644 --- a/main/features.c +++ b/main/features.c @@ -933,8 +933,9 @@ static void check_goto_on_transfer(struct ast_channel *chan) ast_clear_flag(xferchan, AST_FLAGS_ALL); ast_channel_clear_softhangup(xferchan, AST_SOFTHANGUP_ALL); - if (ast_do_masquerade(xferchan) || ast_pbx_start(xferchan)) { - /* Failed to do masquerade or could not start PBX. */ + ast_do_masquerade(xferchan); + if (ast_pbx_start(xferchan)) { + /* Failed to start PBX. */ ast_hangup(xferchan); } } @@ -955,7 +956,6 @@ static struct ast_channel *feature_request_and_dial(struct ast_channel *caller, static void *bridge_call_thread(void *data) { struct ast_bridge_thread_obj *tobj = data; - int res; tobj->chan->appl = !tobj->return_to_pbx ? "Transferred Call" : "ManagerBridge"; tobj->chan->data = tobj->peer->name; @@ -967,8 +967,7 @@ static void *bridge_call_thread(void *data) if (tobj->return_to_pbx) { if (!ast_check_hangup(tobj->peer)) { ast_log(LOG_VERBOSE, "putting peer %s into PBX again\n", tobj->peer->name); - res = ast_pbx_start(tobj->peer); - if (res != AST_PBX_SUCCESS) { + if (ast_pbx_start(tobj->peer)) { ast_log(LOG_WARNING, "FAILED continuing PBX on peer %s\n", tobj->peer->name); ast_hangup(tobj->peer); } @@ -977,8 +976,7 @@ static void *bridge_call_thread(void *data) } if (!ast_check_hangup(tobj->chan)) { ast_log(LOG_VERBOSE, "putting chan %s into PBX again\n", tobj->chan->name); - res = ast_pbx_start(tobj->chan); - if (res != AST_PBX_SUCCESS) { + if (ast_pbx_start(tobj->chan)) { ast_log(LOG_WARNING, "FAILED continuing PBX on chan %s\n", tobj->chan->name); ast_hangup(tobj->chan); } @@ -1001,7 +999,7 @@ static void *bridge_call_thread(void *data) * * Create thread and attributes, call bridge_call_thread */ -static void bridge_call_thread_launch(void *data) +static void bridge_call_thread_launch(struct ast_bridge_thread_obj *data) { pthread_t thread; pthread_attr_t attr; @@ -1010,7 +1008,9 @@ static void bridge_call_thread_launch(void *data) pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (ast_pthread_create(&thread, &attr, bridge_call_thread, data)) { - ast_log(LOG_WARNING, "Failed to create thread for parked call.\n"); + ast_hangup(data->chan); + ast_hangup(data->peer); + ast_log(LOG_ERROR, "Failed to create bridge_call_thread.\n"); } pthread_attr_destroy(&attr); memset(&sched, 0, sizeof(sched)); @@ -2809,7 +2809,12 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st xferchan->readformat = transferee->readformat; xferchan->writeformat = transferee->writeformat; - ast_channel_masquerade(xferchan, transferee); + if (ast_channel_masquerade(xferchan, transferee)) { + ast_hangup(xferchan); + ast_hangup(newchan); + ast_party_connected_line_free(&connected_line); + return -1; + } ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority); xferchan->_state = AST_STATE_UP; ast_clear_flag(xferchan, AST_FLAGS_ALL); @@ -6814,11 +6819,14 @@ static char *handle_features_reload(struct ast_cli_entry *e, int cmd, struct ast * \brief Actual bridge * \param chan * \param tmpchan - * + * * Stop hold music, lock both channels, masq channels, * after bridge return channel to next priority. + * + * \retval 0 on success. + * \retval -1 on error. */ -static void do_bridge_masquerade(struct ast_channel *chan, struct ast_channel *tmpchan) +static int do_bridge_masquerade(struct ast_channel *chan, struct ast_channel *tmpchan) { ast_moh_stop(chan); ast_channel_lock_both(chan, tmpchan); @@ -6828,28 +6836,30 @@ static void do_bridge_masquerade(struct ast_channel *chan, struct ast_channel *t ast_channel_unlock(chan); ast_channel_unlock(tmpchan); - ast_channel_masquerade(tmpchan, chan); - - /* must be done without any channel locks held */ + /* Masquerade setup and execution must be done without any channel locks held */ + if (ast_channel_masquerade(tmpchan, chan)) { + return -1; + } ast_do_masquerade(tmpchan); /* when returning from bridge, the channel will continue at the next priority */ ast_explicit_goto(tmpchan, chan->context, chan->exten, chan->priority + 1); + + return 0; } /*! * \brief Bridge channels together * \param s * \param m - * - * Make sure valid channels were specified, + * + * Make sure valid channels were specified, * send errors if any of the channels could not be found/locked, answer channels if needed, - * create the placeholder channels and grab the other channels - * make the channels compatible, send error if we fail doing so + * create the placeholder channels and grab the other channels + * make the channels compatible, send error if we fail doing so * setup the bridge thread object and start the bridge. - * - * \retval 0 on success or on incorrect use. - * \retval 1 on failure to bridge channels. + * + * \retval 0 */ static int action_bridge(struct mansession *s, const struct message *m) { @@ -6859,6 +6869,7 @@ static int action_bridge(struct mansession *s, const struct message *m) struct ast_channel *chana = NULL, *chanb = NULL, *chans[2]; struct ast_channel *tmpchana = NULL, *tmpchanb = NULL; struct ast_bridge_thread_obj *tobj = NULL; + char buf[256]; /* make sure valid channels were specified */ if (ast_strlen_zero(channela) || ast_strlen_zero(channelb)) { @@ -6868,10 +6879,7 @@ static int action_bridge(struct mansession *s, const struct message *m) /* Start with chana */ chana = ast_channel_get_by_name_prefix(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; @@ -6886,21 +6894,25 @@ static int action_bridge(struct mansession *s, const struct message *m) NULL, NULL, chana->linkedid, 0, "Bridge/%s", chana->name))) { astman_send_error(s, m, "Unable to create temporary channel!"); chana = ast_channel_unref(chana); - return 1; + return 0; } - do_bridge_masquerade(chana, tmpchana); + if (do_bridge_masquerade(chana, tmpchana)) { + snprintf(buf, sizeof(buf), "Unable to masquerade channel %s!", channela); + astman_send_error(s, m, buf); + ast_hangup(tmpchana); + chana = ast_channel_unref(chana); + return 0; + } chana = ast_channel_unref(chana); /* now do chanb */ chanb = ast_channel_get_by_name_prefix(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); + ast_hangup(tmpchana); return 0; } @@ -6914,10 +6926,17 @@ static int action_bridge(struct mansession *s, const struct message *m) astman_send_error(s, m, "Unable to create temporary channels!"); ast_hangup(tmpchana); chanb = ast_channel_unref(chanb); - return 1; + return 0; } - do_bridge_masquerade(chanb, tmpchanb); + if (do_bridge_masquerade(chanb, tmpchanb)) { + snprintf(buf, sizeof(buf), "Unable to masquerade channel %s!", channelb); + astman_send_error(s, m, buf); + ast_hangup(tmpchana); + ast_hangup(tmpchanb); + chanb = ast_channel_unref(chanb); + return 0; + } chanb = ast_channel_unref(chanb); @@ -6927,7 +6946,7 @@ static int action_bridge(struct mansession *s, const struct message *m) astman_send_error(s, m, "Could not make channels compatible for manager bridge"); ast_hangup(tmpchana); ast_hangup(tmpchanb); - return 1; + return 0; } /* setup the bridge thread object and start the bridge */ @@ -6936,7 +6955,7 @@ static int action_bridge(struct mansession *s, const struct message *m) astman_send_error(s, m, "Unable to spawn a new bridge thread"); ast_hangup(tmpchana); ast_hangup(tmpchanb); - return 1; + return 0; } tobj->chan = tmpchana; @@ -7512,7 +7531,7 @@ int ast_bridge_timelimit(struct ast_channel *chan, struct ast_bridge_config *con * \brief Bridge channels * \param chan * \param data channel to bridge with. - * + * * Split data, check we aren't bridging with ourself, check valid channel, * answer call if not already, check compatible channels, setup bridge config * now bridge call, if transfered party hangs up return to PBX extension. @@ -7530,7 +7549,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data) AST_APP_ARG(dest_chan); AST_APP_ARG(options); ); - + if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "Bridge require at least 1 argument specifying the other end of the bridge\n"); return -1; @@ -7545,11 +7564,11 @@ static int bridge_exec(struct ast_channel *chan, const char *data) if (!strcmp(chan->name, args.dest_chan)) { ast_log(LOG_WARNING, "Unable to bridge channel %s with itself\n", chan->name); ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec", - "Response: Failed\r\n" - "Reason: Unable to bridge channel to itself\r\n" - "Channel1: %s\r\n" - "Channel2: %s\r\n", - chan->name, args.dest_chan); + "Response: Failed\r\n" + "Reason: Unable to bridge channel to itself\r\n" + "Channel1: %s\r\n" + "Channel2: %s\r\n", + chan->name, args.dest_chan); pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "LOOP"); return 0; } @@ -7557,34 +7576,61 @@ static int bridge_exec(struct ast_channel *chan, const char *data) /* make sure we have a valid end point */ if (!(current_dest_chan = ast_channel_get_by_name_prefix(args.dest_chan, strlen(args.dest_chan)))) { - ast_log(LOG_WARNING, "Bridge failed because channel %s does not exists or we " - "cannot get its lock\n", args.dest_chan); + ast_log(LOG_WARNING, "Bridge failed because channel %s does not exist\n", + args.dest_chan); ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec", - "Response: Failed\r\n" - "Reason: Cannot grab end point\r\n" - "Channel1: %s\r\n" - "Channel2: %s\r\n", chan->name, args.dest_chan); + "Response: Failed\r\n" + "Reason: Channel2 does not exist\r\n" + "Channel1: %s\r\n" + "Channel2: %s\r\n", chan->name, args.dest_chan); pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "NONEXISTENT"); return 0; } - /* answer the channel if needed */ - if (current_dest_chan->_state != AST_STATE_UP) { - ast_answer(current_dest_chan); - } - /* try to allocate a place holder where current_dest_chan will be placed */ if (!(final_dest_chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, current_dest_chan->linkedid, 0, "Bridge/%s", current_dest_chan->name))) { ast_log(LOG_WARNING, "Cannot create placeholder channel for chan %s\n", args.dest_chan); ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec", - "Response: Failed\r\n" - "Reason: cannot create placeholder\r\n" - "Channel1: %s\r\n" - "Channel2: %s\r\n", chan->name, args.dest_chan); + "Response: Failed\r\n" + "Reason: Cannot create placeholder channel\r\n" + "Channel1: %s\r\n" + "Channel2: %s\r\n", chan->name, args.dest_chan); + pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE"); + ast_channel_unref(current_dest_chan); + return 0; + } + + if (ast_test_flag(&opts, OPT_DURATION_LIMIT) + && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT]) + && ast_bridge_timelimit(chan, &bconfig, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit)) { + ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec", + "Response: Failed\r\n" + "Reason: Cannot setup bridge time limit\r\n" + "Channel1: %s\r\n" + "Channel2: %s\r\n", chan->name, args.dest_chan); + ast_hangup(final_dest_chan); + pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE"); + current_dest_chan = ast_channel_unref(current_dest_chan); + goto done; + } + + if (do_bridge_masquerade(current_dest_chan, final_dest_chan)) { + ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec", + "Response: Failed\r\n" + "Reason: Cannot masquerade channels\r\n" + "Channel1: %s\r\n" + "Channel2: %s\r\n", chan->name, args.dest_chan); + ast_hangup(final_dest_chan); + pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE"); + current_dest_chan = ast_channel_unref(current_dest_chan); + goto done; } - do_bridge_masquerade(current_dest_chan, final_dest_chan); + /* answer the channel if needed */ + if (final_dest_chan->_state != AST_STATE_UP) { + ast_answer(final_dest_chan); + } chans[0] = current_dest_chan; chans[1] = final_dest_chan; @@ -7594,21 +7640,26 @@ static int bridge_exec(struct ast_channel *chan, const char *data) if (ast_channel_make_compatible(chan, final_dest_chan) < 0) { ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, final_dest_chan->name); ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeExec", 2, chans, - "Response: Failed\r\n" - "Reason: Could not make channels compatible for bridge\r\n" - "Channel1: %s\r\n" - "Channel2: %s\r\n", chan->name, final_dest_chan->name); - ast_hangup(final_dest_chan); /* may be we should return this channel to the PBX? */ + "Response: Failed\r\n" + "Reason: Could not make channels compatible for bridge\r\n" + "Channel1: %s\r\n" + "Channel2: %s\r\n", chan->name, final_dest_chan->name); + + /* Maybe we should return this channel to the PBX? */ + ast_hangup(final_dest_chan); + pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "INCOMPATIBLE"); current_dest_chan = ast_channel_unref(current_dest_chan); - return 0; + goto done; } /* Report that the bridge will be successfull */ ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeExec", 2, chans, - "Response: Success\r\n" - "Channel1: %s\r\n" - "Channel2: %s\r\n", chan->name, final_dest_chan->name); + "Response: Success\r\n" + "Channel1: %s\r\n" + "Channel2: %s\r\n", chan->name, final_dest_chan->name); + + current_dest_chan = ast_channel_unref(current_dest_chan); /* we have 2 valid channels to bridge, now it is just a matter of setting up the bridge config and starting the bridge */ if (ast_test_flag(&opts, BRIDGE_OPT_PLAYTONE) && !ast_strlen_zero(xfersound)) { @@ -7617,13 +7668,6 @@ static int bridge_exec(struct ast_channel *chan, const char *data) ast_log(LOG_WARNING, "Failed to play courtesy tone on %s\n", final_dest_chan->name); } } - - current_dest_chan = ast_channel_unref(current_dest_chan); - - if (ast_test_flag(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) { - if (ast_bridge_timelimit(chan, &bconfig, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit)) - goto done; - } if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER)) ast_set_flag(&(bconfig.features_callee), AST_FEATURE_REDIRECT); @@ -7642,34 +7686,40 @@ static int bridge_exec(struct ast_channel *chan, const char *data) if (ast_test_flag(&opts, OPT_CALLER_PARK)) ast_set_flag(&(bconfig.features_caller), AST_FEATURE_PARKCALL); + /* + * Don't let the after-bridge code run the h-exten. We want to + * continue in the dialplan. + */ + ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT); ast_bridge_call(chan, final_dest_chan, &bconfig); - /* the bridge has ended, set BRIDGERESULT to SUCCESS. If the other channel has not been hung up, return it to the PBX */ + /* The bridge has ended, set BRIDGERESULT to SUCCESS. */ pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "SUCCESS"); - if (!ast_check_hangup(final_dest_chan) && !ast_test_flag(&opts, OPT_CALLEE_KILL)) { - ast_debug(1, "starting new PBX in %s,%s,%d for chan %s\n", - final_dest_chan->context, final_dest_chan->exten, - final_dest_chan->priority, final_dest_chan->name); - if (ast_pbx_start(final_dest_chan) != AST_PBX_SUCCESS) { - ast_log(LOG_WARNING, "FAILED continuing PBX on dest chan %s\n", final_dest_chan->name); + /* If the other channel has not been hung up, return it to the PBX */ + if (!ast_check_hangup(final_dest_chan)) { + if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) { + ast_debug(1, "starting new PBX in %s,%s,%d for chan %s\n", + final_dest_chan->context, final_dest_chan->exten, + final_dest_chan->priority, final_dest_chan->name); + + if (ast_pbx_start(final_dest_chan)) { + ast_log(LOG_WARNING, "FAILED continuing PBX on dest chan %s\n", final_dest_chan->name); + ast_hangup(final_dest_chan); + } else { + ast_debug(1, "SUCCESS continuing PBX on chan %s\n", final_dest_chan->name); + } + } else { ast_hangup(final_dest_chan); - } else - ast_debug(1, "SUCCESS continuing PBX on chan %s\n", final_dest_chan->name); + } } else { - ast_debug(1, "hangup chan %s since the other endpoint has hung up or the x flag was passed\n", final_dest_chan->name); + ast_debug(1, "chan %s was hungup\n", final_dest_chan->name); ast_hangup(final_dest_chan); } done: - if (bconfig.warning_sound) { - ast_free((char *)bconfig.warning_sound); - } - if (bconfig.end_sound) { - ast_free((char *)bconfig.end_sound); - } - if (bconfig.start_sound) { - ast_free((char *)bconfig.start_sound); - } + ast_free((char *) bconfig.warning_sound); + ast_free((char *) bconfig.end_sound); + ast_free((char *) bconfig.start_sound); return 0; }