Skip to content
Snippets Groups Projects
bridge.c 157 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		app = ast_strdupa(ast_channel_appl(chan2));
    
    		res = ast_local_setup_masquerade(local_chan, chan2);
    	}
    
    	if (res) {
    		ast_hangup(local_chan);
    		return AST_BRIDGE_TRANSFER_FAIL;
    	}
    
    
    	/*
    	 * Since bridges need to be unlocked before entering ast_bridge_impart and
    	 * core_local may call into it then the bridges need to be unlocked here.
    	 */
    	ast_bridge_unlock(bridge1);
    	if (bridge2) {
    		ast_bridge_unlock(bridge2);
    	}
    
    
    	if (ast_call(local_chan, dest, 0)) {
    		ast_hangup(local_chan);
    
    		BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2);
    
    	/* Get a ref for use later since this one is being stolen */
    	ao2_ref(local_chan, +1);
    
    	if (ast_bridge_impart(bridge1, local_chan, chan1, NULL,
    		AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
    
    		ao2_cleanup(local_chan);
    
    		BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2);
    
    	BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2);
    
    		/* Have to lock everything just in case a hangup comes in early */
    		ast_local_lock_all(local_chan, &locals[0], &locals[1]);
    		if (!locals[0] || !locals[1]) {
    			ast_log(LOG_ERROR, "Transfer failed probably due to an early hangup - "
    				"missing other half of '%s'\n", ast_channel_name(local_chan));
    			ast_local_unlock_all(local_chan);
    			ao2_cleanup(local_chan);
    			return AST_BRIDGE_TRANSFER_FAIL;
    		}
    
    		/* Make sure the peer is properly set */
    		if (local_chan != locals[0]) {
    			SWAP(locals[0], locals[1]);
    		}
    
    
    		ast_attended_transfer_message_add_link(transfer_msg, locals);
    
    		ast_attended_transfer_message_add_app(transfer_msg, app, local_chan);
    
    	ao2_cleanup(local_chan);
    	return AST_BRIDGE_TRANSFER_SUCCESS;
    
    static enum ast_transfer_result try_parking(struct ast_channel *transferer,
    	const char *context, const char *exten, transfer_channel_cb new_channel_cb,
    	struct transfer_channel_data *user_data_wrapper)
    
    	RAII_VAR(struct ast_bridge_channel *, transferer_bridge_channel, NULL, ao2_cleanup);
    
    	if (!ast_parking_provider_registered()) {
    
    	transferer_bridge_channel = ast_channel_get_bridge_channel(transferer);
    
    	if (ast_parking_blind_transfer_park(transferer_bridge_channel,
    
    		context, exten, new_channel_cb, user_data_wrapper)) {
    
    	return AST_BRIDGE_TRANSFER_SUCCESS;
    
    void ast_bridge_set_transfer_variables(struct ast_channel *chan, const char *value, int attended)
    {
    	char *writevar;
    	char *erasevar;
    
    	if (attended) {
    		writevar = ATTENDEDTRANSFER;
    		erasevar = BLINDTRANSFER;
    	} else {
    		writevar = BLINDTRANSFER;
    		erasevar = ATTENDEDTRANSFER;
    	}
    
    	pbx_builtin_setvar_helper(chan, writevar, value);
    
    	pbx_builtin_setvar_helper(chan, erasevar, NULL);
    
     * \brief Set the transfer variable as appropriate on channels involved in the transfer
    
     * The transferer channel will have its variable set the same as its BRIDGEPEER
    
     * variable. This will account for all channels that it is bridged to. The other channels
    
     * involved in the transfer will have their variable set to the transferer
    
     * \param transferer The channel performing the transfer
    
     * \param channels The channels belonging to the bridge
    
     * \param is_attended false  set BLINDTRANSFER and unset ATTENDEDTRANSFER
     *                    true   set ATTENDEDTRANSFER and unset BLINDTRANSFER
    
    static void set_transfer_variables_all(struct ast_channel *transferer, struct ao2_container *channels, int is_attended)
    
    	struct ao2_iterator iter;
    	struct ast_channel *chan;
    	const char *transferer_name;
    	const char *transferer_bridgepeer;
    
    	ast_channel_lock(transferer);
    	transferer_name = ast_strdupa(ast_channel_name(transferer));
    	transferer_bridgepeer = ast_strdupa(S_OR(pbx_builtin_getvar_helper(transferer, "BRIDGEPEER"), ""));
    	ast_channel_unlock(transferer);
    
    	for (iter = ao2_iterator_init(channels, 0);
    			(chan = ao2_iterator_next(&iter));
    			ao2_cleanup(chan)) {
    		if (chan == transferer) {
    
    			ast_bridge_set_transfer_variables(chan, transferer_bridgepeer, is_attended);
    
    			ast_bridge_set_transfer_variables(chan, transferer_name, is_attended);
    
    static struct ast_bridge *acquire_bridge(struct ast_channel *chan)
    {
    	struct ast_bridge *bridge;
    
    	ast_channel_lock(chan);
    	bridge = ast_channel_get_bridge(chan);
    	ast_channel_unlock(chan);
    
    	if (bridge
    		&& ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)) {
    		ao2_ref(bridge, -1);
    		bridge = NULL;
    	}
    
    	return bridge;
    }
    
    
    enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
    		struct ast_channel *transferer, const char *exten, const char *context,
    
    		transfer_channel_cb new_channel_cb, void *user_data)
    {
    	RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
    
    	RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
    
    	RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
    
    	RAII_VAR(struct ast_channel *, transferee, NULL, ast_channel_cleanup);
    
    	RAII_VAR(struct transfer_channel_data *, user_data_wrapper, NULL, ao2_cleanup);
    
    	RAII_VAR(struct ast_blind_transfer_message *, transfer_message, NULL, ao2_cleanup);
    
    	int do_bridge_transfer;
    	int transfer_prohibited;
    
    	enum ast_transfer_result transfer_result;
    
    	transfer_message = ast_blind_transfer_message_create(is_external, transferer, exten, context);
    	if (!transfer_message) {
    		/* Out of memory. Not even possible to publish a Stasis message about the
    		 * failure
    		 */
    		ast_log(LOG_ERROR, "Unable to allocate memory for blind transfer publication from %s\n",
    				ast_channel_name(transferer));
    		return AST_BRIDGE_TRANSFER_FAIL;
    	}
    
    
    	bridge = acquire_bridge(transferer);
    	if (!bridge) {
    
    		transfer_result = AST_BRIDGE_TRANSFER_INVALID;
    		goto publish;
    
    	ast_bridge_lock(bridge);
    	transfer_message->bridge = ast_bridge_snapshot_create(bridge);
    	ast_bridge_unlock(bridge);
    	if (!transfer_message->bridge) {
    		transfer_result = AST_BRIDGE_TRANSFER_FAIL;
    		goto publish;
    	}
    
    
    	transferee = ast_bridge_peer(bridge, transferer);
    
    	if (transferee) {
    		transfer_message->transferee = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferee));
    		if (!transfer_message->transferee) {
    			transfer_result = AST_BRIDGE_TRANSFER_FAIL;
    			goto publish;
    		}
    	}
    
    	bridge_channel = ast_channel_get_bridge_channel(transferer);
    
    		transfer_result = AST_BRIDGE_TRANSFER_INVALID;
    		goto publish;
    
    	user_data_wrapper = ao2_alloc(sizeof(*user_data_wrapper), NULL);
    	if (!user_data_wrapper) {
    		transfer_result = AST_BRIDGE_TRANSFER_FAIL;
    		goto publish;
    	}
    
    	user_data_wrapper->data = user_data;
    
    
    	/* Take off hold if they are on hold. */
    	ast_bridge_channel_write_unhold(bridge_channel);
    
    
    	transfer_result = try_parking(transferer, context, exten, new_channel_cb, user_data_wrapper);
    
    	if (transfer_result == AST_BRIDGE_TRANSFER_SUCCESS) {
    
    	/* Since parking didn't take control of the user_data_wrapper, we are just going to raise the completed flag now. */
    	user_data_wrapper->completed = 1;
    
    
    	{
    		SCOPED_LOCK(lock, bridge, ast_bridge_lock, ast_bridge_unlock);
    
    		channels = ast_bridge_peers_nolock(bridge);
    		if (!channels) {
    
    			transfer_result = AST_BRIDGE_TRANSFER_FAIL;
    			goto publish;
    
    			transfer_result = AST_BRIDGE_TRANSFER_INVALID;
    			goto publish;
    
    		}
    		transfer_prohibited = ast_test_flag(&bridge->feature_flags,
    				AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
    		do_bridge_transfer = ast_test_flag(&bridge->feature_flags,
    				AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY) ||
    				ao2_container_count(channels) > 2;
    	}
    
    		transfer_result = AST_BRIDGE_TRANSFER_NOT_PERMITTED;
    		goto publish;
    
    	set_transfer_variables_all(transferer, channels, 0);
    
    		transfer_result = blind_transfer_bridge(is_external, transferer, bridge,
    
    			exten, context, transferee, new_channel_cb, user_data_wrapper, transfer_message);
    
    	/* Reaching this portion means that we're dealing with a two-party bridge */
    
    		transfer_result = AST_BRIDGE_TRANSFER_FAIL;
    		goto publish;
    
    	if (bridge_channel_internal_queue_blind_transfer(transferee, exten, context,
    
    				new_channel_cb, user_data_wrapper)) {
    
    		transfer_result = AST_BRIDGE_TRANSFER_FAIL;
    		goto publish;
    
    	transfer_result = AST_BRIDGE_TRANSFER_SUCCESS;
    
    publish:
    
    	transfer_message->result = transfer_result;
    	ast_bridge_publish_blind_transfer(transfer_message);
    
    /*!
     * \internal
     * \brief Performs an attended transfer by moving a channel from one bridge to another
     *
     * The channel that is bridged to the source_channel is moved into the dest_bridge from
     * the source_bridge_channel's bridge. The swap_channel is swapped out of the dest_bridge and placed in
     * the source_bridge_channel's bridge.
     *
     * \note dest_bridge and source_bridge_channel's bridge MUST be locked before calling this function.
     *
     * \param dest_bridge The final bridge for the attended transfer
     * \param source_channel Channel who is bridged to the channel that will move
     * \param swap_channel Channel to be swapped out of the dest_bridge
     * \return The success or failure of the swap attempt
     */
    static enum ast_transfer_result bridge_swap_attended_transfer(struct ast_bridge *dest_bridge,
    		struct ast_bridge_channel *source_bridge_channel, struct ast_channel *swap_channel)
    {
    	struct ast_bridge_channel *bridged_to_source;
    
    	bridged_to_source = ast_bridge_channel_peer(source_bridge_channel);
    
    	if (bridged_to_source
    
    		&& bridged_to_source->state == BRIDGE_CHANNEL_STATE_WAIT
    
    		&& !ast_test_flag(&bridged_to_source->features->feature_flags,
    			AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE)) {
    
    		if (bridge_do_move(dest_bridge, bridged_to_source, 1, 0)) {
    
    			return AST_BRIDGE_TRANSFER_FAIL;
    		}
    		/* Must kick the source channel out of its bridge. */
    
    		ast_bridge_channel_leave_bridge(source_bridge_channel,
    			BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING);
    
    		return AST_BRIDGE_TRANSFER_SUCCESS;
    
    	} else {
    		return AST_BRIDGE_TRANSFER_INVALID;
    	}
    }
    
    /*!
     * \internal
     * \brief Function that performs an attended transfer when both transferer channels are bridged
     *
     * The method by which the transfer is performed is dependent on whether the bridges allow for
     * optimization to occur between them. If no optimization is permitted, then an unreal channel
     * is placed as a link between the two bridges. If optimization is permitted, then that means
     * we are free to perform move or merge operations in order to perform the transfer.
     *
     * \note to_transferee_bridge and to_target_bridge MUST be locked before calling this function
     *
     * \param to_transferee The channel that is bridged to the transferee
     * \param to_transferee_bridge_channel to_transferee's bridge_channel
     * \param to_transfer_target The channel that is bridged to the transfer target
     * \param to_target_bridge_channel to_transfer_target's bridge_channel
     * \param to_transferee_bridge The bridge between to_transferee and the transferee
     * \param to_target_bridge The bridge between to_transfer_target and the transfer_target
    
     * \param publication Data to publish for a stasis attended transfer message
    
     * \return The success or failure of the attended transfer
     */
    static enum ast_transfer_result two_bridge_attended_transfer(struct ast_channel *to_transferee,
    		struct ast_bridge_channel *to_transferee_bridge_channel,
    		struct ast_channel *to_transfer_target,
    		struct ast_bridge_channel *to_target_bridge_channel,
    
    		struct ast_bridge *to_transferee_bridge, struct ast_bridge *to_target_bridge,
    
    		struct ast_attended_transfer_message *transfer_msg)
    
    {
    	struct ast_bridge_channel *kick_me[] = {
    			to_transferee_bridge_channel,
    			to_target_bridge_channel,
    	};
    
    	enum ast_transfer_result res;
    	struct ast_bridge *final_bridge = NULL;
    
    	RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
    
    	channels = ast_bridge_peers_nolock(to_transferee_bridge);
    
    	if (!channels) {
    		res = AST_BRIDGE_TRANSFER_FAIL;
    		goto end;
    	}
    
    	set_transfer_variables_all(to_transferee, channels, 1);
    
    
    	switch (ast_bridges_allow_optimization(to_transferee_bridge, to_target_bridge)) {
    	case AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE:
    
    		final_bridge = to_transferee_bridge;
    		res = bridge_swap_attended_transfer(to_transferee_bridge, to_target_bridge_channel, to_transferee);
    		goto end;
    
    		final_bridge = to_target_bridge;
    		res = bridge_swap_attended_transfer(to_target_bridge, to_transferee_bridge_channel, to_transfer_target);
    		goto end;
    
    		final_bridge = to_transferee_bridge;
    
    		bridge_do_merge(to_transferee_bridge, to_target_bridge, kick_me, ARRAY_LEN(kick_me), 0);
    
    		res = AST_BRIDGE_TRANSFER_SUCCESS;
    		goto end;
    
    		final_bridge = to_target_bridge;
    
    		bridge_do_merge(to_target_bridge, to_transferee_bridge, kick_me, ARRAY_LEN(kick_me), 0);
    
    		res = AST_BRIDGE_TRANSFER_SUCCESS;
    		goto end;
    
    	case AST_BRIDGE_OPTIMIZE_PROHIBITED:
    	default:
    		/* Just because optimization wasn't doable doesn't necessarily mean
    		 * that we can actually perform the transfer. Some reasons for non-optimization
    		 * indicate bridge invalidity, so let's check those before proceeding.
    		 */
    		if (to_transferee_bridge->inhibit_merge || to_transferee_bridge->dissolved ||
    				to_target_bridge->inhibit_merge || to_target_bridge->dissolved) {
    
    		return attended_transfer_bridge(to_transferee, to_transfer_target,
    
    			to_transferee_bridge, to_target_bridge, transfer_msg);
    
    
    end:
    	if (res == AST_BRIDGE_TRANSFER_SUCCESS) {
    
    		ast_attended_transfer_message_add_merge(transfer_msg, final_bridge);
    
    enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee,
    
    		struct ast_channel *to_transfer_target)
    {
    	RAII_VAR(struct ast_bridge *, to_transferee_bridge, NULL, ao2_cleanup);
    	RAII_VAR(struct ast_bridge *, to_target_bridge, NULL, ao2_cleanup);
    
    	RAII_VAR(struct ast_bridge_channel *, to_transferee_bridge_channel, NULL, ao2_cleanup);
    	RAII_VAR(struct ast_bridge_channel *, to_target_bridge_channel, NULL, ao2_cleanup);
    
    	RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
    	RAII_VAR(struct ast_channel *, transferee, NULL, ao2_cleanup);
    
    	RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
    
    	struct ast_bridge *the_bridge = NULL;
    
    	struct ast_channel *chan_bridged;
    	struct ast_channel *chan_unbridged;
    	int transfer_prohibited;
    	int do_bridge_transfer;
    
    	enum ast_transfer_result res;
    	const char *app = NULL;
    
    
    	to_transferee_bridge = acquire_bridge(to_transferee);
    	to_target_bridge = acquire_bridge(to_transfer_target);
    
    
    	transfer_msg = ast_attended_transfer_message_create(1, to_transferee, to_transferee_bridge,
    			to_transfer_target, to_target_bridge, NULL, NULL);
    	if (!transfer_msg) {
    		ast_log(LOG_ERROR, "Unable to create Stasis publication for attended transfer from %s\n",
    				ast_channel_name(to_transferee));
    		return AST_BRIDGE_TRANSFER_FAIL;
    	}
    
    	/* They can't both be unbridged, you silly goose! */
    	if (!to_transferee_bridge && !to_target_bridge) {
    
    		res = AST_BRIDGE_TRANSFER_INVALID;
    		goto end;
    
    	ast_channel_lock(to_transferee);
    	to_transferee_bridge_channel = ast_channel_get_bridge_channel(to_transferee);
    	ast_channel_unlock(to_transferee);
    
    	ast_channel_lock(to_transfer_target);
    	to_target_bridge_channel = ast_channel_get_bridge_channel(to_transfer_target);
    	ast_channel_unlock(to_transfer_target);
    
    	if (to_transferee_bridge_channel) {
    		/* Take off hold if they are on hold. */
    		ast_bridge_channel_write_unhold(to_transferee_bridge_channel);
    	}
    
    	if (to_target_bridge_channel) {
    
    		/* Take off hold if they are on hold. */
    		ast_bridge_channel_write_unhold(to_target_bridge_channel);
    
    
    		/* Is there a courtesy sound to play to the target? */
    		ast_channel_lock(to_transfer_target);
    		target_complete_sound = pbx_builtin_getvar_helper(to_transfer_target,
    			"ATTENDED_TRANSFER_COMPLETE_SOUND");
    		if (!ast_strlen_zero(target_complete_sound)) {
    			target_complete_sound = ast_strdupa(target_complete_sound);
    		} else {
    			target_complete_sound = NULL;
    		}
    		ast_channel_unlock(to_transfer_target);
    		if (!target_complete_sound) {
    			ast_channel_lock(to_transferee);
    			target_complete_sound = pbx_builtin_getvar_helper(to_transferee,
    				"ATTENDED_TRANSFER_COMPLETE_SOUND");
    			if (!ast_strlen_zero(target_complete_sound)) {
    				target_complete_sound = ast_strdupa(target_complete_sound);
    			} else {
    				target_complete_sound = NULL;
    			}
    			ast_channel_unlock(to_transferee);
    		}
    		if (target_complete_sound) {
    			ast_bridge_channel_write_playfile(to_target_bridge_channel, NULL,
    				target_complete_sound, NULL);
    		}
    
    	/* Let's get the easy one out of the way first */
    	if (to_transferee_bridge && to_target_bridge) {
    
    
    		if (!to_transferee_bridge_channel || !to_target_bridge_channel) {
    
    			res = AST_BRIDGE_TRANSFER_INVALID;
    			goto end;
    
    
    		ast_bridge_lock_both(to_transferee_bridge, to_target_bridge);
    		res = two_bridge_attended_transfer(to_transferee, to_transferee_bridge_channel,
    				to_transfer_target, to_target_bridge_channel,
    
    				to_transferee_bridge, to_target_bridge, transfer_msg);
    
    		ast_bridge_unlock(to_transferee_bridge);
    		ast_bridge_unlock(to_target_bridge);
    
    
    	}
    
    	the_bridge = to_transferee_bridge ?: to_target_bridge;
    	chan_bridged = to_transferee_bridge ? to_transferee : to_transfer_target;
    	chan_unbridged = to_transferee_bridge ? to_transfer_target : to_transferee;
    
    
    	/*
    	 * Race condition makes it possible for app to be NULL, so get the app prior to
    	 * transferring with a fallback of "unknown".
    	 */
    	app = ast_strdupa(ast_channel_appl(chan_unbridged) ?: "unknown");
    
    
    	{
    		int chan_count;
    		SCOPED_LOCK(lock, the_bridge, ast_bridge_lock, ast_bridge_unlock);
    
    		channels = ast_bridge_peers_nolock(the_bridge);
    		if (!channels) {
    
    			res = AST_BRIDGE_TRANSFER_FAIL;
    			goto end;
    
    		}
    		chan_count = ao2_container_count(channels);
    		if (chan_count <= 1) {
    
    			res = AST_BRIDGE_TRANSFER_INVALID;
    			goto end;
    
    		}
    		transfer_prohibited = ast_test_flag(&the_bridge->feature_flags,
    				AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
    		do_bridge_transfer = ast_test_flag(&the_bridge->feature_flags,
    				AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY) ||
    				chan_count > 2;
    	}
    
    	if (transfer_prohibited) {
    
    		res = AST_BRIDGE_TRANSFER_NOT_PERMITTED;
    		goto end;
    
    	set_transfer_variables_all(to_transferee, channels, 1);
    
    
    		/*
    		 * Hang up the target if it was bridged. Note, if it is not bridged
    		 * it is hung up during the masquerade.
    		 */
    		hangup_target = chan_bridged == to_transfer_target;
    
    		ast_bridge_lock(the_bridge);
    
    		res = attended_transfer_bridge(chan_bridged, chan_unbridged, the_bridge, NULL, transfer_msg);
    
    		ast_bridge_unlock(the_bridge);
    		goto end;
    
    	}
    
    	transferee = get_transferee(channels, chan_bridged);
    	if (!transferee) {
    
    		res = AST_BRIDGE_TRANSFER_FAIL;
    		goto end;
    
    	if (bridge_channel_internal_queue_attended_transfer(transferee, chan_unbridged)) {
    
    		res = AST_BRIDGE_TRANSFER_FAIL;
    		goto end;
    
    	ast_attended_transfer_message_add_app(transfer_msg, app, NULL);
    
    	res = AST_BRIDGE_TRANSFER_SUCCESS;
    
    	if (res == AST_BRIDGE_TRANSFER_SUCCESS && hangup_target) {
    		ast_softhangup(to_transfer_target, AST_SOFTHANGUP_DEV);
    	}
    
    
    	transfer_msg->result = res;
    	ast_bridge_publish_attended_transfer(transfer_msg);
    
    /*!
     * \internal
     * \brief Service the bridge manager request.
     * \since 12.0.0
     *
     * \param bridge requesting service.
     *
     * \return Nothing
     */
    static void bridge_manager_service(struct ast_bridge *bridge)
    
    	ast_bridge_lock(bridge);
    	if (bridge->callid) {
    		ast_callid_threadassoc_change(bridge->callid);
    	}
    
    	/* Do any pending bridge actions. */
    	bridge_handle_actions(bridge);
    	ast_bridge_unlock(bridge);
    }
    
    /*!
     * \internal
     * \brief Bridge manager service thread.
     * \since 12.0.0
     *
     * \return Nothing
     */
    static void *bridge_manager_thread(void *data)
    {
    	struct bridge_manager_controller *manager = data;
    	struct bridge_manager_request *request;
    
    	ao2_lock(manager);
    	while (!manager->stop) {
    		request = AST_LIST_REMOVE_HEAD(&manager->service_requests, node);
    		if (!request) {
    			ast_cond_wait(&manager->cond, ao2_object_get_lockaddr(manager));
    			continue;
    		}
    		ao2_unlock(manager);
    
    		/* Service the bridge. */
    		bridge_manager_service(request->bridge);
    		ao2_ref(request->bridge, -1);
    		ast_free(request);
    
    /*!
     * \internal
     * \brief Destroy the bridge manager controller.
     * \since 12.0.0
     *
     * \param obj Bridge manager to destroy.
     *
     * \return Nothing
     */
    static void bridge_manager_destroy(void *obj)
    
    	struct bridge_manager_controller *manager = obj;
    	struct bridge_manager_request *request;
    
    	if (manager->thread != AST_PTHREADT_NULL) {
    		/* Stop the manager thread. */
    		ao2_lock(manager);
    		manager->stop = 1;
    		ast_cond_signal(&manager->cond);
    		ao2_unlock(manager);
    		ast_debug(1, "Waiting for bridge manager thread to die.\n");
    		pthread_join(manager->thread, NULL);
    	}
    
    	/* Destroy the service request queue. */
    	while ((request = AST_LIST_REMOVE_HEAD(&manager->service_requests, node))) {
    		ao2_ref(request->bridge, -1);
    		ast_free(request);
    
    	ast_cond_destroy(&manager->cond);
    }
    
    /*!
     * \internal
     * \brief Create the bridge manager controller.
     * \since 12.0.0
     *
     * \retval manager on success.
     * \retval NULL on error.
     */
    static struct bridge_manager_controller *bridge_manager_create(void)
    {
    	struct bridge_manager_controller *manager;
    
    	manager = ao2_alloc(sizeof(*manager), bridge_manager_destroy);
    	if (!manager) {
    		/* Well. This isn't good. */
    		return NULL;
    	}
    	ast_cond_init(&manager->cond, NULL);
    	AST_LIST_HEAD_INIT_NOLOCK(&manager->service_requests);
    
    	/* Create the bridge manager thread. */
    	if (ast_pthread_create(&manager->thread, NULL, bridge_manager_thread, manager)) {
    		/* Well. This isn't good either. */
    		manager->thread = AST_PTHREADT_NULL;
    		ao2_ref(manager, -1);
    		manager = NULL;
    	}
    
    /*!
     * \internal
     * \brief Bridge ao2 container sort function.
     * \since 12.0.0
     *
     * \param obj_left pointer to the (user-defined part) of an object.
     * \param obj_right pointer to the (user-defined part) of an object.
     * \param flags flags from ao2_callback()
     *   OBJ_POINTER - if set, 'obj_right', is an object.
     *   OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
     *   OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
     *
     * \retval <0 if obj_left < obj_right
     * \retval =0 if obj_left == obj_right
     * \retval >0 if obj_left > obj_right
     */
    static int bridge_sort_cmp(const void *obj_left, const void *obj_right, int flags)
    
    	const struct ast_bridge *bridge_left = obj_left;
    	const struct ast_bridge *bridge_right = obj_right;
    	const char *right_key = obj_right;
    	int cmp;
    
    	switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
    	default:
    	case OBJ_POINTER:
    		right_key = bridge_right->uniqueid;
    		/* Fall through */
    	case OBJ_KEY:
    		cmp = strcmp(bridge_left->uniqueid, right_key);
    		break;
    	case OBJ_PARTIAL_KEY:
    		cmp = strncmp(bridge_left->uniqueid, right_key, strlen(right_key));
    		break;
    	}
    	return cmp;
    
    struct ast_bridge *ast_bridge_find_by_id(const char *bridge_id)
    {
    	return ao2_find(bridges, bridge_id, OBJ_SEARCH_KEY);
    }
    
    
    struct bridge_complete {
    	/*! Nth match to return. */
    	int state;
    	/*! Which match currently on. */
    	int which;
    };
    
    static int complete_bridge_live_search(void *obj, void *arg, void *data, int flags)
    {
    	struct bridge_complete *search = data;
    
    	if (++search->which > search->state) {
    		return CMP_MATCH;
    	}
    	return 0;
    }
    
    static char *complete_bridge_live(const char *word, int state)
    {
    	char *ret;
    	struct ast_bridge *bridge;
    	struct bridge_complete search = {
    		.state = state,
    		};
    
    	bridge = ao2_callback_data(bridges, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
    		complete_bridge_live_search, (char *) word, &search);
    	if (!bridge) {
    		return NULL;
    	}
    	ret = ast_strdup(bridge->uniqueid);
    	ao2_ref(bridge, -1);
    	return ret;
    }
    
    static char *complete_bridge_stasis(const char *word, int state)
    
    	char *ret = NULL;
    	int wordlen = strlen(word), which = 0;
    	RAII_VAR(struct ao2_container *, cached_bridges, NULL, ao2_cleanup);
    	struct ao2_iterator iter;
    	struct stasis_message *msg;
    
    	cached_bridges = stasis_cache_dump(ast_bridge_cache(), ast_bridge_snapshot_type());
    	if (!cached_bridges) {
    
    	iter = ao2_iterator_init(cached_bridges, 0);
    	for (; (msg = ao2_iterator_next(&iter)); ao2_ref(msg, -1)) {
    		struct ast_bridge_snapshot *snapshot = stasis_message_data(msg);
    
    		if (!strncasecmp(word, snapshot->uniqueid, wordlen) && (++which > state)) {
    			ret = ast_strdup(snapshot->uniqueid);
    
    static char *handle_bridge_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    #define FORMAT_HDR "%-36s %5s %-15s %s\n"
    #define FORMAT_ROW "%-36s %5u %-15s %s\n"
    
    
    	RAII_VAR(struct ao2_container *, cached_bridges, NULL, ao2_cleanup);
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "bridge show all";
    		e->usage =
    			"Usage: bridge show all\n"
    			"       List all bridges\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    
    	cached_bridges = stasis_cache_dump(ast_bridge_cache(), ast_bridge_snapshot_type());
    	if (!cached_bridges) {
    
    		ast_cli(a->fd, "Failed to retrieve cached bridges\n");
    		return CLI_SUCCESS;
    	}
    
    
    	ast_cli(a->fd, FORMAT_HDR, "Bridge-ID", "Chans", "Type", "Technology");
    
    
    	iter = ao2_iterator_init(cached_bridges, 0);
    	for (; (msg = ao2_iterator_next(&iter)); ao2_ref(msg, -1)) {
    		struct ast_bridge_snapshot *snapshot = stasis_message_data(msg);
    
    
    			snapshot->uniqueid,
    			snapshot->num_channels,
    			S_OR(snapshot->subclass, "<unknown>"),
    			S_OR(snapshot->technology, "<unknown>"));
    
    /*! \brief Internal callback function for sending channels in a bridge to the CLI */
    static int bridge_show_specific_print_channel(void *obj, void *arg, int flags)
    {
    	const char *uniqueid = obj;
    	struct ast_cli_args *a = arg;
    	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
    	struct ast_channel_snapshot *snapshot;
    
    
    	msg = stasis_cache_get(ast_channel_cache(), ast_channel_snapshot_type(), uniqueid);
    	if (!msg) {
    
    		return 0;
    	}
    	snapshot = stasis_message_data(msg);
    
    	ast_cli(a->fd, "Channel: %s\n", snapshot->name);
    
    	return 0;
    }
    
    
    static char *handle_bridge_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
    	struct ast_bridge_snapshot *snapshot;
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "bridge show";
    		e->usage =
    			"Usage: bridge show <bridge-id>\n"
    			"       Show information about the <bridge-id> bridge\n";
    		return NULL;
    	case CLI_GENERATE:
    		if (a->pos == 2) {
    
    			return complete_bridge_stasis(a->word, a->n);
    
    	msg = stasis_cache_get(ast_bridge_cache(), ast_bridge_snapshot_type(), a->argv[2]);
    
    		ast_cli(a->fd, "Bridge '%s' not found\n", a->argv[2]);
    		return CLI_SUCCESS;
    	}
    
    	snapshot = stasis_message_data(msg);
    	ast_cli(a->fd, "Id: %s\n", snapshot->uniqueid);
    	ast_cli(a->fd, "Type: %s\n", S_OR(snapshot->subclass, "<unknown>"));
    	ast_cli(a->fd, "Technology: %s\n", S_OR(snapshot->technology, "<unknown>"));
    	ast_cli(a->fd, "Num-Channels: %u\n", snapshot->num_channels);
    	ao2_callback(snapshot->channels, OBJ_NODATA, bridge_show_specific_print_channel, a);
    
    static char *handle_bridge_destroy_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "bridge destroy";
    		e->usage =
    			"Usage: bridge destroy <bridge-id>\n"
    			"       Destroy the <bridge-id> bridge\n";
    		return NULL;
    	case CLI_GENERATE:
    		if (a->pos == 2) {
    
    	bridge = ast_bridge_find_by_id(a->argv[2]);
    
    	if (!bridge) {
    		ast_cli(a->fd, "Bridge '%s' not found\n", a->argv[2]);
    		return CLI_SUCCESS;
    	}
    
    	ast_cli(a->fd, "Destroying bridge '%s'\n", a->argv[2]);
    
    static char *complete_bridge_participant(const char *bridge_name, const char *line, const char *word, int pos, int state)
    
    	struct ast_bridge_channel *bridge_channel;
    	int which;
    	int wordlen;
    
    
    	bridge = ast_bridge_find_by_id(bridge_name);
    
    	{
    		SCOPED_LOCK(bridge_lock, bridge, ast_bridge_lock, ast_bridge_unlock);
    
    		which = 0;
    		wordlen = strlen(word);
    		AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
    			if (!strncasecmp(ast_channel_name(bridge_channel->chan), word, wordlen)
    				&& ++which > state) {
    
    				return ast_strdup(ast_channel_name(bridge_channel->chan));
    			}
    
    static char *handle_bridge_kick_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "bridge kick";
    		e->usage =
    
    			"Usage: bridge kick <bridge-id> <channel-name | all>\n"