Newer
Older
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007 - 2009, Digium, Inc.
*
* Joshua Colp <jcolp@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
*
* \author Joshua Colp <jcolp@digium.com>
*/
/*** MODULEINFO
<support_level>core</support_level>
***/
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/*** DOCUMENTATION
<manager name="BridgeTechnologyList" language="en_US">
<synopsis>
List available bridging technologies and their statuses.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
</syntax>
<description>
<para>Returns detailed information about the available bridging technologies.</para>
</description>
</manager>
<manager name="BridgeTechnologySuspend" language="en_US">
<synopsis>
Suspend a bridging technology.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
<parameter name="BridgeTechnology" required="true">
<para>The name of the bridging technology to suspend.</para>
</parameter>
</syntax>
<description>
<para>Marks a bridging technology as suspended, which prevents subsequently created bridges from using it.</para>
</description>
</manager>
<manager name="BridgeTechnologyUnsuspend" language="en_US">
<synopsis>
Unsuspend a bridging technology.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
<parameter name="BridgeTechnology" required="true">
<para>The name of the bridging technology to unsuspend.</para>
</parameter>
</syntax>
<description>
<para>Clears a previously suspended bridging technology, which allows subsequently created bridges to use it.</para>
</description>
</manager>
***/
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/options.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/linkedlists.h"
#include "asterisk/bridge.h"
#include "asterisk/bridge_internal.h"
#include "asterisk/bridge_channel_internal.h"
#include "asterisk/bridge_features.h"
#include "asterisk/bridge_basic.h"
#include "asterisk/bridge_technology.h"
#include "asterisk/bridge_channel.h"
#include "asterisk/bridge_after.h"
#include "asterisk/stasis_bridges.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/stasis_cache_pattern.h"
#include "asterisk/app.h"
#include "asterisk/file.h"
#include "asterisk/module.h"
#include "asterisk/astobj2.h"
Richard Mudgett
committed
#include "asterisk/pbx.h"
#include "asterisk/test.h"
Richard Mudgett
committed
#include "asterisk/_private.h"
#include "asterisk/heap.h"
#include "asterisk/say.h"
#include "asterisk/timing.h"
#include "asterisk/stringfields.h"
#include "asterisk/musiconhold.h"
#include "asterisk/features.h"
#include "asterisk/cli.h"
#include "asterisk/parking.h"
Mark Michelson
committed
#include "asterisk/core_local.h"
Matthew Jordan
committed
#include "asterisk/core_unreal.h"
Richard Mudgett
committed
#include "asterisk/causes.h"
Richard Mudgett
committed
/*! All bridges container. */
static struct ao2_container *bridges;
static AST_RWLIST_HEAD_STATIC(bridge_technologies, ast_bridge_technology);
/* Initial starting point for the bridge array of channels */
#define BRIDGE_ARRAY_START 128
/* Grow rate of bridge array of channels */
#define BRIDGE_ARRAY_GROW 32
/* Variable name - stores peer information about the most recent blind transfer */
#define BLINDTRANSFER "BLINDTRANSFER"
/* Variable name - stores peer information about the most recent attended transfer */
#define ATTENDEDTRANSFER "ATTENDEDTRANSFER"
static void cleanup_video_mode(struct ast_bridge *bridge);
/*! Default DTMF keys for built in features */
static char builtin_features_dtmf[AST_BRIDGE_BUILTIN_END][MAXIMUM_DTMF_FEATURE_STRING];
/*! Function handlers for the built in features */
static ast_bridge_hook_callback builtin_features_handlers[AST_BRIDGE_BUILTIN_END];
Richard Mudgett
committed
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/*! Function handlers for built in interval features */
static ast_bridge_builtin_set_limits_fn builtin_interval_handlers[AST_BRIDGE_BUILTIN_INTERVAL_END];
/*! Bridge manager service request */
struct bridge_manager_request {
/*! List of bridge service requests. */
AST_LIST_ENTRY(bridge_manager_request) node;
/*! Refed bridge requesting service. */
struct ast_bridge *bridge;
};
struct bridge_manager_controller {
/*! Condition, used to wake up the bridge manager thread. */
ast_cond_t cond;
/*! Queue of bridge service requests. */
AST_LIST_HEAD_NOLOCK(, bridge_manager_request) service_requests;
/*! Manager thread */
pthread_t thread;
/*! TRUE if the manager needs to stop. */
unsigned int stop:1;
};
/*! Bridge manager controller. */
static struct bridge_manager_controller *bridge_manager;
/*!
* \internal
* \brief Request service for a bridge from the bridge manager.
* \since 12.0.0
*
* \param bridge Requesting service.
*
* \return Nothing
*/
static void bridge_manager_service_req(struct ast_bridge *bridge)
{
struct bridge_manager_request *request;
ao2_lock(bridge_manager);
if (bridge_manager->stop) {
ao2_unlock(bridge_manager);
return;
}
/* Create the service request. */
request = ast_calloc(1, sizeof(*request));
if (!request) {
/* Well. This isn't good. */
ao2_unlock(bridge_manager);
return;
}
ao2_ref(bridge, +1);
request->bridge = bridge;
/* Put request into the queue and wake the bridge manager. */
AST_LIST_INSERT_TAIL(&bridge_manager->service_requests, request, node);
ast_cond_signal(&bridge_manager->cond);
ao2_unlock(bridge_manager);
}
int __ast_bridge_technology_register(struct ast_bridge_technology *technology, struct ast_module *module)
{
Richard Mudgett
committed
struct ast_bridge_technology *current;
/* Perform a sanity check to make sure the bridge technology conforms to our needed requirements */
Richard Mudgett
committed
if (ast_strlen_zero(technology->name)
|| !technology->capabilities
|| !technology->write) {
ast_log(LOG_WARNING, "Bridge technology %s failed registration sanity check.\n",
technology->name);
return -1;
}
AST_RWLIST_WRLOCK(&bridge_technologies);
/* Look for duplicate bridge technology already using this name, or already registered */
AST_RWLIST_TRAVERSE(&bridge_technologies, current, entry) {
if ((!strcasecmp(current->name, technology->name)) || (current == technology)) {
Richard Mudgett
committed
ast_log(LOG_WARNING, "A bridge technology of %s already claims to exist in our world.\n",
technology->name);
AST_RWLIST_UNLOCK(&bridge_technologies);
return -1;
}
}
/* Copy module pointer so reference counting can keep the module from unloading */
technology->mod = module;
/* Insert our new bridge technology into the list and print out a pretty message */
AST_RWLIST_INSERT_TAIL(&bridge_technologies, technology, entry);
AST_RWLIST_UNLOCK(&bridge_technologies);
Tilghman Lesher
committed
ast_verb(2, "Registered bridge technology %s\n", technology->name);
return 0;
}
int ast_bridge_technology_unregister(struct ast_bridge_technology *technology)
{
Richard Mudgett
committed
struct ast_bridge_technology *current;
AST_RWLIST_WRLOCK(&bridge_technologies);
/* Ensure the bridge technology is registered before removing it */
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&bridge_technologies, current, entry) {
if (current == technology) {
AST_RWLIST_REMOVE_CURRENT(entry);
Tilghman Lesher
committed
ast_verb(2, "Unregistered bridge technology %s\n", technology->name);
break;
}
}
Tilghman Lesher
committed
AST_RWLIST_TRAVERSE_SAFE_END;
AST_RWLIST_UNLOCK(&bridge_technologies);
return current ? 0 : -1;
}
Richard Mudgett
committed
/*!
* \internal
Richard Mudgett
committed
* \brief Put an action onto the specified bridge. Don't dup the action frame.
Richard Mudgett
committed
* \since 12.0.0
*
Richard Mudgett
committed
* \param bridge What to queue the action on.
* \param action What to do.
Richard Mudgett
committed
*
* \return Nothing
*/
Richard Mudgett
committed
static void bridge_queue_action_nodup(struct ast_bridge *bridge, struct ast_frame *action)
Richard Mudgett
committed
{
ast_debug(1, "Bridge %s: queueing action type:%u sub:%d\n",
Richard Mudgett
committed
bridge->uniqueid, action->frametype, action->subclass.integer);
Richard Mudgett
committed
Richard Mudgett
committed
ast_bridge_lock(bridge);
AST_LIST_INSERT_TAIL(&bridge->action_queue, action, frame_list);
ast_bridge_unlock(bridge);
bridge_manager_service_req(bridge);
Richard Mudgett
committed
}
Richard Mudgett
committed
int ast_bridge_queue_action(struct ast_bridge *bridge, struct ast_frame *action)
Richard Mudgett
committed
struct ast_frame *dup;
dup = ast_frdup(action);
if (!dup) {
return -1;
Richard Mudgett
committed
bridge_queue_action_nodup(bridge, dup);
return 0;
}
Richard Mudgett
committed
void bridge_dissolve(struct ast_bridge *bridge, int cause)
Richard Mudgett
committed
{
struct ast_bridge_channel *bridge_channel;
Richard Mudgett
committed
struct ast_frame action = {
.frametype = AST_FRAME_BRIDGE_ACTION,
.subclass.integer = BRIDGE_CHANNEL_ACTION_DEFERRED_DISSOLVING,
Richard Mudgett
committed
};
Richard Mudgett
committed
Richard Mudgett
committed
if (bridge->dissolved) {
return;
Richard Mudgett
committed
}
Richard Mudgett
committed
bridge->dissolved = 1;
Richard Mudgett
committed
Richard Mudgett
committed
if (cause <= 0) {
cause = AST_CAUSE_NORMAL_CLEARING;
}
bridge->cause = cause;
ast_debug(1, "Bridge %s: dissolving bridge with cause %d(%s)\n",
bridge->uniqueid, cause, ast_cause2str(cause));
Richard Mudgett
committed
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
Richard Mudgett
committed
ast_bridge_channel_leave_bridge(bridge_channel,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, cause);
Richard Mudgett
committed
/* Must defer dissolving bridge because it is already locked. */
ast_bridge_queue_action(bridge, &action);
/*!
* \internal
* \brief Check if a bridge should dissolve because of a stolen channel and do it.
* \since 12.0.0
*
* \param bridge Bridge to check.
* \param bridge_channel Stolen channel causing the check. It is not in the bridge to check and may be in another bridge.
* \note On entry, bridge and bridge_channel->bridge are already locked.
*
* \return Nothing
*/
static void bridge_dissolve_check_stolen(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
if (bridge->dissolved) {
return;
}
if (bridge_channel->features->usable
&& ast_test_flag(&bridge_channel->features->feature_flags,
AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP)) {
/* The stolen channel controlled the bridge it was stolen from. */
Richard Mudgett
committed
bridge_dissolve(bridge, 0);
return;
}
if (bridge->num_channels < 2
&& ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP)) {
/*
* The stolen channel has not left enough channels to keep the
* bridge alive. Assume the stolen channel hung up.
*/
Richard Mudgett
committed
bridge_dissolve(bridge, 0);
Joshua Colp
committed
}
Joshua Colp
committed
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
/*!
* \internal
* \brief Update connected line information after a bridge has been reconfigured.
*
* \param bridge The bridge itself.
*
* \return Nothing
*/
static void bridge_reconfigured_connected_line_update(struct ast_bridge *bridge)
{
struct ast_party_connected_line connected;
struct ast_bridge_channel *bridge_channel = AST_LIST_FIRST(&bridge->channels), *peer;
unsigned char data[1024];
size_t datalen;
if (!bridge_channel ||
!(bridge->technology->capabilities & (AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE)) ||
!(peer = ast_bridge_channel_peer(bridge_channel)) ||
ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_ZOMBIE) ||
ast_test_flag(ast_channel_flags(peer->chan), AST_FLAG_ZOMBIE) ||
ast_check_hangup_locked(bridge_channel->chan) ||
ast_check_hangup_locked(peer->chan)) {
return;
}
ast_party_connected_line_init(&connected);
ast_channel_lock(bridge_channel->chan);
ast_connected_line_copy_from_caller(&connected, ast_channel_caller(bridge_channel->chan));
ast_channel_unlock(bridge_channel->chan);
if ((datalen = ast_connected_line_build_data(data, sizeof(data), &connected, NULL)) != (size_t) -1) {
ast_bridge_channel_queue_control_data(peer, AST_CONTROL_CONNECTED_LINE, data, datalen);
}
ast_channel_lock(peer->chan);
ast_connected_line_copy_from_caller(&connected, ast_channel_caller(peer->chan));
ast_channel_unlock(peer->chan);
if ((datalen = ast_connected_line_build_data(data, sizeof(data), &connected, NULL)) != (size_t) -1) {
ast_bridge_channel_queue_control_data(bridge_channel, AST_CONTROL_CONNECTED_LINE, data, datalen);
}
ast_party_connected_line_free(&connected);
}
Richard Mudgett
committed
/*!
* \internal
* \brief Complete joining a channel to the bridge.
Richard Mudgett
committed
* \since 12.0.0
*
* \param bridge What to operate upon.
* \param bridge_channel What is joining the bridge technology.
Richard Mudgett
committed
*
* \note On entry, bridge is already locked.
Richard Mudgett
committed
*
* \return Nothing
*/
static void bridge_channel_complete_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
/* Tell the bridge technology we are joining so they set us up */
ast_debug(1, "Bridge %s: %p(%s) is joining %s technology\n",
bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
bridge->technology->name);
if (bridge->technology->join
&& bridge->technology->join(bridge, bridge_channel)) {
ast_debug(1, "Bridge %s: %p(%s) failed to join %s technology\n",
Richard Mudgett
committed
bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
bridge->technology->name);
bridge_channel->just_joined = 1;
return;
bridge_channel->just_joined = 0;
Richard Mudgett
committed
/*!
* \internal
* \brief Complete joining new channels to the bridge.
Richard Mudgett
committed
* \since 12.0.0
*
* \param bridge Check for new channels on this bridge.
Richard Mudgett
committed
*
* \note On entry, bridge is already locked.
Richard Mudgett
committed
*
Richard Mudgett
committed
*/
static void bridge_complete_join(struct ast_bridge *bridge)
struct ast_bridge_channel *bridge_channel;
if (bridge->dissolved) {
/*
* No sense in completing the join on channels for a dissolved
* bridge. They are just going to be removed soon anyway.
* However, we do have reason to abort here because the bridge
* technology may not be able to handle the number of channels
* still in the bridge.
*/
return;
Richard Mudgett
committed
}
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
if (!bridge_channel->just_joined) {
continue;
}
bridge_channel_complete_join(bridge, bridge_channel);
Richard Mudgett
committed
}
Matthew Jordan
committed
/*! \brief Helper function used to find the "best" bridge technology given specified capabilities */
static struct ast_bridge_technology *find_best_technology(uint32_t capabilities, struct ast_bridge *bridge)
{
struct ast_bridge_technology *current;
struct ast_bridge_technology *best = NULL;
Matthew Jordan
committed
AST_RWLIST_RDLOCK(&bridge_technologies);
AST_RWLIST_TRAVERSE(&bridge_technologies, current, entry) {
if (current->suspended) {
ast_debug(1, "Bridge technology %s is suspended. Skipping.\n",
current->name);
continue;
}
if (!(current->capabilities & capabilities)) {
ast_debug(1, "Bridge technology %s does not have any capabilities we want.\n",
current->name);
continue;
}
if (best && current->preference <= best->preference) {
ast_debug(1, "Bridge technology %s has less preference than %s (%u <= %u). Skipping.\n",
current->name, best->name, current->preference, best->preference);
continue;
}
if (current->compatible && !current->compatible(bridge)) {
ast_debug(1, "Bridge technology %s is not compatible with properties of existing bridge.\n",
current->name);
continue;
}
best = current;
if (best) {
/* Increment it's module reference count if present so it does not get unloaded while in use */
ast_module_ref(best->mod);
ast_debug(1, "Chose bridge technology %s\n", best->name);
}
Richard Mudgett
committed
AST_RWLIST_UNLOCK(&bridge_technologies);
return best;
struct tech_deferred_destroy {
struct ast_bridge_technology *tech;
void *tech_pvt;
};
Richard Mudgett
committed
/*!
* \internal
* \brief Deferred destruction of bridge tech private structure.
* \since 12.0.0
*
* \param bridge What to execute the action on.
* \param action Deferred bridge tech destruction.
*
* \note On entry, bridge must not be locked.
*
* \return Nothing
*/
static void bridge_tech_deferred_destroy(struct ast_bridge *bridge, struct ast_frame *action)
{
struct tech_deferred_destroy *deferred = action->data.ptr;
struct ast_bridge dummy_bridge = {
.technology = deferred->tech,
.tech_pvt = deferred->tech_pvt,
.creator = bridge->creator,
.name = bridge->name,
.uniqueid = bridge->uniqueid,
ast_debug(1, "Bridge %s: calling %s technology destructor (deferred, dummy)\n",
dummy_bridge.uniqueid, dummy_bridge.technology->name);
dummy_bridge.technology->destroy(&dummy_bridge);
ast_module_unref(dummy_bridge.technology->mod);
Richard Mudgett
committed
}
Richard Mudgett
committed
/*!
* \internal
* \brief Handle bridge action frame.
Richard Mudgett
committed
* \since 12.0.0
*
* \param bridge What to execute the action on.
* \param action What to do.
*
* \note On entry, bridge is already locked.
* \note Can be called by the bridge destructor.
Richard Mudgett
committed
*
* \return Nothing
*/
static void bridge_action_bridge(struct ast_bridge *bridge, struct ast_frame *action)
Richard Mudgett
committed
{
#if 0 /* In case we need to know when the destructor is calling us. */
int in_destructor = !ao2_ref(bridge, 0);
#endif
Richard Mudgett
committed
switch (action->subclass.integer) {
case BRIDGE_CHANNEL_ACTION_DEFERRED_TECH_DESTROY:
ast_bridge_unlock(bridge);
bridge_tech_deferred_destroy(bridge, action);
ast_bridge_lock(bridge);
break;
case BRIDGE_CHANNEL_ACTION_DEFERRED_DISSOLVING:
ast_bridge_unlock(bridge);
bridge->v_table->dissolving(bridge);
ast_bridge_lock(bridge);
break;
default:
/* Unexpected deferred action type. Should never happen. */
ast_assert(0);
break;
Richard Mudgett
committed
}
/*!
* \internal
* \brief Do any pending bridge actions.
* \since 12.0.0
*
* \param bridge What to do actions on.
*
* \note On entry, bridge is already locked.
* \note Can be called by the bridge destructor.
*
* \return Nothing
*/
static void bridge_handle_actions(struct ast_bridge *bridge)
Richard Mudgett
committed
{
struct ast_frame *action;
while ((action = AST_LIST_REMOVE_HEAD(&bridge->action_queue, frame_list))) {
switch (action->frametype) {
case AST_FRAME_BRIDGE_ACTION:
bridge_action_bridge(bridge, action);
break;
default:
/* Unexpected deferred frame type. Should never happen. */
ast_assert(0);
break;
}
ast_frfree(action);
}
static struct stasis_message *create_bridge_snapshot_message(struct ast_bridge *bridge)
RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
if (!ast_bridge_snapshot_type()) {
return NULL;
}
ast_bridge_lock(bridge);
snapshot = ast_bridge_snapshot_create(bridge);
ast_bridge_unlock(bridge);
if (!snapshot) {
return NULL;
}
return stasis_message_create(ast_bridge_snapshot_type(), snapshot);
Richard Mudgett
committed
}
static void destroy_bridge(void *obj)
Richard Mudgett
committed
{
struct ast_bridge *bridge = obj;
ast_debug(1, "Bridge %s: actually destroying %s bridge, nobody wants it anymore\n",
bridge->uniqueid, bridge->v_table->name);
if (bridge->construction_completed) {
RAII_VAR(struct stasis_message *, clear_msg, NULL, ao2_cleanup);
clear_msg = create_bridge_snapshot_message(bridge);
if (clear_msg) {
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
msg = stasis_cache_clear_create(clear_msg);
if (msg) {
stasis_publish(ast_bridge_topic(bridge), msg);
}
}
}
/* Do any pending actions in the context of destruction. */
ast_bridge_lock(bridge);
bridge_handle_actions(bridge);
ast_bridge_unlock(bridge);
/* There should not be any channels left in the bridge. */
ast_assert(AST_LIST_EMPTY(&bridge->channels));
ast_debug(1, "Bridge %s: calling %s bridge destructor\n",
bridge->uniqueid, bridge->v_table->name);
bridge->v_table->destroy(bridge);
/* Pass off the bridge to the technology to destroy if needed */
if (bridge->technology) {
ast_debug(1, "Bridge %s: calling %s technology stop\n",
bridge->uniqueid, bridge->technology->name);
if (bridge->technology->stop) {
ast_bridge_lock(bridge);
bridge->technology->stop(bridge);
ast_bridge_unlock(bridge);
}
ast_debug(1, "Bridge %s: calling %s technology destructor\n",
bridge->uniqueid, bridge->technology->name);
if (bridge->technology->destroy) {
bridge->technology->destroy(bridge);
}
ast_module_unref(bridge->technology->mod);
bridge->technology = NULL;
bridge->callid = 0;
cleanup_video_mode(bridge);
stasis_cp_single_unsubscribe(bridge->topics);
ast_string_field_free_memory(bridge);
struct ast_bridge *bridge_register(struct ast_bridge *bridge)
if (bridge) {
bridge->construction_completed = 1;
ast_bridge_lock(bridge);
ast_bridge_publish_state(bridge);
ast_bridge_unlock(bridge);
if (!ao2_link(bridges, bridge)) {
Richard Mudgett
committed
ast_bridge_destroy(bridge, 0);
bridge = NULL;
}
}
return bridge;
struct ast_bridge *bridge_alloc(size_t size, const struct ast_bridge_methods *v_table)
Richard Mudgett
committed
{
struct ast_bridge *bridge;
/* Check v_table that all methods are present. */
if (!v_table
|| !v_table->name
|| !v_table->destroy
|| !v_table->dissolving
|| !v_table->push
|| !v_table->pull
|| !v_table->notify_masquerade
|| !v_table->get_merge_priority) {
ast_log(LOG_ERROR, "Virtual method table for bridge class %s not complete.\n",
v_table && v_table->name ? v_table->name : "<unknown>");
ast_assert(0);
return NULL;
}
Richard Mudgett
committed
bridge = ao2_alloc(size, destroy_bridge);
if (!bridge) {
return NULL;
}
if (ast_string_field_init(bridge, 80)) {
ao2_cleanup(bridge);
return NULL;
bridge->v_table = v_table;
struct ast_bridge *bridge_base_init(struct ast_bridge *self, uint32_t capabilities, unsigned int flags, const char *creator, const char *name, const char *id)
char uuid_hold[AST_UUID_STR_LEN];
if (!self) {
return NULL;
if (!ast_strlen_zero(id)) {
ast_string_field_set(self, uniqueid, id);
} else {
ast_uuid_generate_str(uuid_hold, AST_UUID_STR_LEN);
ast_string_field_set(self, uniqueid, uuid_hold);
}
ast_string_field_set(self, creator, creator);
if (!ast_strlen_zero(creator)) {
ast_string_field_set(self, name, name);
}
ast_set_flag(&self->feature_flags, flags);
self->allowed_capabilities = capabilities;
if (bridge_topics_init(self) != 0) {
ast_log(LOG_WARNING, "Bridge %s: Could not initialize topics\n",
self->uniqueid);
ao2_ref(self, -1);
return NULL;
}
/* Use our helper function to find the "best" bridge technology. */
self->technology = find_best_technology(capabilities, self);
if (!self->technology) {
ast_log(LOG_WARNING, "Bridge %s: Could not create class %s. No technology to support it.\n",
self->uniqueid, self->v_table->name);
ao2_ref(self, -1);
return NULL;
Richard Mudgett
committed
}
/* Pass off the bridge to the technology to manipulate if needed */
ast_debug(1, "Bridge %s: calling %s technology constructor\n",
self->uniqueid, self->technology->name);
if (self->technology->create && self->technology->create(self)) {
ast_log(LOG_WARNING, "Bridge %s: failed to setup bridge technology %s\n",
self->uniqueid, self->technology->name);
ao2_ref(self, -1);
return NULL;
}
ast_debug(1, "Bridge %s: calling %s technology start\n",
self->uniqueid, self->technology->name);
if (self->technology->start && self->technology->start(self)) {
ast_log(LOG_WARNING, "Bridge %s: failed to start bridge technology %s\n",
self->uniqueid, self->technology->name);
ao2_ref(self, -1);
return NULL;
Richard Mudgett
committed
}
if (!ast_bridge_topic(self)) {
ao2_ref(self, -1);
return NULL;
}
return self;
}
Richard Mudgett
committed
/*!
* \internal
* \brief ast_bridge base class destructor.
Richard Mudgett
committed
* \since 12.0.0
*
* \param self Bridge to operate upon.
*
* \note Stub because of nothing to do.
Richard Mudgett
committed
*
* \return Nothing
*/
static void bridge_base_destroy(struct ast_bridge *self)
Richard Mudgett
committed
{
}
/*!
* \internal
* \brief The bridge is being dissolved.
* \since 12.0.0
*
* \param self Bridge to operate upon.
*
* \return Nothing
*/
static void bridge_base_dissolving(struct ast_bridge *self)
Richard Mudgett
committed
{
ao2_unlink(bridges, self);
}
Richard Mudgett
committed
/*!
* \internal
* \brief ast_bridge base push method.
* \since 12.0.0
*
* \param self Bridge to operate upon.
* \param bridge_channel Bridge channel to push.
* \param swap Bridge channel to swap places with if not NULL.
*
* \note On entry, self is already locked.
* \note Stub because of nothing to do.
*
* \retval 0 on success
* \retval -1 on failure
*/
static int bridge_base_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
Richard Mudgett
committed
}
Richard Mudgett
committed
/*!
* \internal
* \brief ast_bridge base pull method.
Richard Mudgett
committed
* \since 12.0.0
*
* \param self Bridge to operate upon.
* \param bridge_channel Bridge channel to pull.
*
* \note On entry, self is already locked.
Richard Mudgett
committed
*
* \return Nothing
*/
static void bridge_base_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
Richard Mudgett
committed
{
ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
Richard Mudgett
committed
}
Richard Mudgett
committed
/*!
* \internal
* \brief ast_bridge base notify_masquerade method.
Richard Mudgett
committed
* \since 12.0.0
*
* \param self Bridge to operate upon.
* \param bridge_channel Bridge channel that was masqueraded.
*
* \note On entry, self is already locked.
Richard Mudgett
committed
*
* \return Nothing
*/
static void bridge_base_notify_masquerade(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
Richard Mudgett
committed
{
self->reconfigured = 1;
Richard Mudgett
committed
}
/*!
* \internal
* \brief Get the merge priority of this bridge.
* \since 12.0.0
*
* \param self Bridge to operate upon.
*
* \note On entry, self is already locked.
*
* \return Merge priority
*/
static int bridge_base_get_merge_priority(struct ast_bridge *self)
Richard Mudgett
committed
{
Richard Mudgett
committed
}
/*!
* \internal
* \brief ast_bridge base push_peek method.
* \since 13.2.0
*
* \param self Bridge to operate upon.
* \param bridge_channel Bridge channel to push.
* \param swap Bridge channel to swap places with if not NULL.
*
* \note On entry, self is already locked.
* \note Stub because of nothing to do.
*
* \retval 0 on success
* \retval -1 on failure
*/
static int bridge_base_push_peek(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
{
return 0;
}
struct ast_bridge_methods ast_bridge_base_v_table = {
.name = "base",
.destroy = bridge_base_destroy,
.dissolving = bridge_base_dissolving,
.push = bridge_base_push,
.pull = bridge_base_pull,
.notify_masquerade = bridge_base_notify_masquerade,
.get_merge_priority = bridge_base_get_merge_priority,
.push_peek = bridge_base_push_peek,
Richard Mudgett
committed
};
struct ast_bridge *ast_bridge_base_new(uint32_t capabilities, unsigned int flags, const char *creator, const char *name, const char *id)
Richard Mudgett
committed
{
void *bridge;
bridge = bridge_alloc(sizeof(struct ast_bridge), &ast_bridge_base_v_table);
bridge = bridge_base_init(bridge, capabilities, flags, creator, name, id);
bridge = bridge_register(bridge);
return bridge;
Richard Mudgett
committed
}
Richard Mudgett
committed
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Richard Mudgett
committed
{
ast_debug(1, "Bridge %s: telling all channels to leave the party\n", bridge->uniqueid);
ast_bridge_lock(bridge);
Richard Mudgett
committed
bridge_dissolve(bridge, cause);
ast_bridge_unlock(bridge);
Richard Mudgett
committed
Richard Mudgett
committed
/*!
* \internal
* \brief Perform the smart bridge operation.
Richard Mudgett
committed
* \since 12.0.0
*
* \param bridge Work on this bridge.
*
* \details
* Basically see if a new bridge technology should be used instead
* of the current one.
Richard Mudgett
committed
*
* \note On entry, bridge is already locked.
*
* \retval 0 on success.
* \retval -1 on error.
Richard Mudgett
committed
*/
static int smart_bridge_operation(struct ast_bridge *bridge)
Richard Mudgett
committed
{
uint32_t new_capabilities;
struct ast_bridge_technology *new_technology;
struct ast_bridge_technology *old_technology = bridge->technology;
Richard Mudgett
committed
struct ast_bridge_channel *bridge_channel;
struct ast_frame *deferred_action;
struct ast_bridge dummy_bridge = {
.technology = bridge->technology,
.tech_pvt = bridge->tech_pvt,
.creator = bridge->creator,
.name = bridge->name,
.uniqueid = bridge->uniqueid,
Richard Mudgett
committed
if (bridge->dissolved) {
ast_debug(1, "Bridge %s is dissolved, not performing smart bridge operation.\n",
bridge->uniqueid);
return 0;
Richard Mudgett
committed
}
/* Determine new bridge technology capabilities needed. */
if (2 < bridge->num_channels) {
new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX;
new_capabilities &= bridge->allowed_capabilities;
} else {
new_capabilities = AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX;
new_capabilities &= bridge->allowed_capabilities;
if (!new_capabilities
&& (bridge->allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX)) {
/* Allow switching between different multimix bridge technologies. */