diff options
-rw-r--r-- | main/bridge.c | 67 | ||||
-rw-r--r-- | main/bridge_channel.c | 24 | ||||
-rw-r--r-- | main/features.c | 2 |
3 files changed, 63 insertions, 30 deletions
diff --git a/main/bridge.c b/main/bridge.c index b8f69fe3a..bc983b1b3 100644 --- a/main/bridge.c +++ b/main/bridge.c @@ -1439,7 +1439,7 @@ int ast_bridge_join(struct ast_bridge *bridge, int pass_reference) { struct ast_bridge_channel *bridge_channel; - int res; + int res = 0; bridge_channel = bridge_channel_internal_alloc(bridge); if (pass_reference) { @@ -1460,16 +1460,21 @@ int ast_bridge_join(struct ast_bridge *bridge, bridge_channel->tech_args = *tech_args; } - /* Initialize various other elements of the bridge channel structure that we can't do above */ ast_channel_lock(chan); - ast_channel_internal_bridge_channel_set(chan, bridge_channel); + if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) { + res = -1; + } else { + ast_channel_internal_bridge_channel_set(chan, bridge_channel); + } ast_channel_unlock(chan); bridge_channel->thread = pthread_self(); bridge_channel->chan = chan; bridge_channel->swap = swap; bridge_channel->features = features; - res = bridge_channel_internal_join(bridge_channel); + if (!res) { + res = bridge_channel_internal_join(bridge_channel); + } /* Cleanup all the data in the bridge channel after it leaves the bridge. */ ast_channel_lock(chan); @@ -1546,9 +1551,16 @@ static void *bridge_channel_ind_thread(void *data) int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, int independent) { - int res; + int res = 0; struct ast_bridge_channel *bridge_channel; + /* Imparted channels cannot have a PBX. */ + if (ast_channel_pbx(chan)) { + ast_log(AST_LOG_WARNING, "Channel %s has a PBX thread and cannot be imparted into bridge %s\n", + ast_channel_name(chan), bridge->uniqueid); + return -1; + } + /* Supply an empty features structure if the caller did not. */ if (!features) { features = ast_bridge_features_new(); @@ -1564,9 +1576,14 @@ int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struc return -1; } - /* Setup various parameters */ ast_channel_lock(chan); - ast_channel_internal_bridge_channel_set(chan, bridge_channel); + if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) { + ast_log(AST_LOG_NOTICE, "Channel %s is a zombie and cannot be imparted into bridge %s\n", + ast_channel_name(chan), bridge->uniqueid); + res = -1; + } else { + ast_channel_internal_bridge_channel_set(chan, bridge_channel); + } ast_channel_unlock(chan); bridge_channel->chan = chan; bridge_channel->swap = swap; @@ -1575,18 +1592,14 @@ int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struc bridge_channel->callid = ast_read_threadstorage_callid(); /* Actually create the thread that will handle the channel */ - if (independent) { - /* Independently imparted channels cannot have a PBX. */ - ast_assert(!ast_channel_pbx(chan)); - - res = ast_pthread_create_detached(&bridge_channel->thread, NULL, - bridge_channel_ind_thread, bridge_channel); - } else { - /* Imparted channels to be departed should not have a PBX either. */ - ast_assert(!ast_channel_pbx(chan)); - - res = ast_pthread_create(&bridge_channel->thread, NULL, - bridge_channel_depart_thread, bridge_channel); + if (!res) { + if (independent) { + res = ast_pthread_create_detached(&bridge_channel->thread, NULL, + bridge_channel_ind_thread, bridge_channel); + } else { + res = ast_pthread_create(&bridge_channel->thread, NULL, + bridge_channel_depart_thread, bridge_channel); + } } if (res) { @@ -2105,10 +2118,11 @@ int ast_bridge_add_channel(struct ast_bridge *bridge, struct ast_channel *chan, ast_channel_unlock(chan); if (chan_bridge) { - RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup); + struct ast_bridge_channel *bridge_channel; ast_bridge_lock_both(bridge, chan_bridge); bridge_channel = bridge_find_channel(chan_bridge, chan); + if (bridge_move_locked(bridge, chan_bridge, chan, NULL, 1)) { ast_bridge_unlock(chan_bridge); ast_bridge_unlock(bridge); @@ -2145,8 +2159,17 @@ int ast_bridge_add_channel(struct ast_bridge *bridge, struct ast_channel *chan, } ast_channel_ref(yanked_chan); if (ast_bridge_impart(bridge, yanked_chan, NULL, features, 1)) { - ast_log(LOG_WARNING, "Could not add %s to the bridge\n", ast_channel_name(chan)); - ast_hangup(yanked_chan); + /* It is possible for us to yank a channel and have some other + * thread start a PBX on the channl after we yanked it. In particular, + * this can theoretically happen on the ;2 of a Local channel if we + * yank it prior to the ;1 being answered. Make sure that it isn't + * executing a PBX before hanging it up. + */ + if (ast_channel_pbx(yanked_chan)) { + ast_channel_unref(yanked_chan); + } else { + ast_hangup(yanked_chan); + } return -1; } } diff --git a/main/bridge_channel.c b/main/bridge_channel.c index 830abb750..589337057 100644 --- a/main/bridge_channel.c +++ b/main/bridge_channel.c @@ -1827,22 +1827,30 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel) bridge_channel, ast_channel_name(bridge_channel->chan)); /* - * Get "in the bridge" before pushing the channel for any - * masquerades on the channel to happen before bridging. + * Directly locking the bridge is safe here because nobody else + * knows about this bridge_channel yet. + */ + ast_bridge_lock(bridge_channel->bridge); + + /* Make sure we're still good to be put into a bridge */ ast_channel_lock(bridge_channel->chan); + if (ast_channel_internal_bridge(bridge_channel->chan) + || ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_ZOMBIE)) { + ast_channel_unlock(bridge_channel->chan); + ast_bridge_unlock(bridge_channel->bridge); + ast_debug(1, "Bridge %s: %p(%s) failed to join Bridge\n", + bridge_channel->bridge->uniqueid, + bridge_channel, + ast_channel_name(bridge_channel->chan)); + return -1; + } ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge); ast_channel_unlock(bridge_channel->chan); /* Add the jitterbuffer if the channel requires it */ ast_jb_enable_for_channel(bridge_channel->chan); - /* - * Directly locking the bridge is safe here because nobody else - * knows about this bridge_channel yet. - */ - ast_bridge_lock(bridge_channel->bridge); - if (!bridge_channel->bridge->callid) { bridge_channel->bridge->callid = ast_read_threadstorage_callid(); } diff --git a/main/features.c b/main/features.c index 9e5dfddb3..6585aeccd 100644 --- a/main/features.c +++ b/main/features.c @@ -1142,6 +1142,8 @@ static int action_bridge(struct mansession *s, const struct message *m) return 0; } + ast_debug(1, "Performing Bridge action on %s and %s\n", channela, channelb); + /* Start with chana */ chana = ast_channel_get_by_name_prefix(channela, strlen(channela)); if (!chana) { |