Newer
Older
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. */
new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX;
}
}
Richard Mudgett
committed
/* Find a bridge technology to satisfy the new capabilities. */
new_technology = find_best_technology(new_capabilities, bridge);
if (!new_technology) {
int is_compatible = 0;
Richard Mudgett
committed
if (old_technology->compatible) {
is_compatible = old_technology->compatible(bridge);
} else if (old_technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) {
is_compatible = 1;
} else if (bridge->num_channels <= 2
&& (old_technology->capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX)) {
is_compatible = 1;
Richard Mudgett
committed
}
if (is_compatible) {
ast_debug(1, "Bridge %s could not get a new technology, staying with old technology.\n",
bridge->uniqueid);
return 0;
Richard Mudgett
committed
}
ast_log(LOG_WARNING, "Bridge %s has no technology available to support it.\n",
bridge->uniqueid);
return -1;
}
if (new_technology == old_technology) {
ast_debug(1, "Bridge %s is already using the new technology.\n",
bridge->uniqueid);
ast_module_unref(old_technology->mod);
return 0;
}
Richard Mudgett
committed
if (old_technology->destroy) {
struct tech_deferred_destroy deferred_tech_destroy = {
.tech = dummy_bridge.technology,
.tech_pvt = dummy_bridge.tech_pvt,
};
struct ast_frame action = {
.frametype = AST_FRAME_BRIDGE_ACTION,
.subclass.integer = BRIDGE_CHANNEL_ACTION_DEFERRED_TECH_DESTROY,
.data.ptr = &deferred_tech_destroy,
.datalen = sizeof(deferred_tech_destroy),
};
Richard Mudgett
committed
/*
* We need to defer the bridge technology destroy callback
* because we have the bridge locked.
Richard Mudgett
committed
*/
deferred_action = ast_frdup(&action);
if (!deferred_action) {
ast_module_unref(new_technology->mod);
return -1;
Richard Mudgett
committed
}
} else {
deferred_action = NULL;
Richard Mudgett
committed
}
/*
* We are now committed to changing the bridge technology. We
* must not release the bridge lock until we have installed the
* new bridge technology.
*/
ast_verb(4, "Bridge %s: switching from %s technology to %s\n",
bridge->uniqueid, old_technology->name, new_technology->name);
Richard Mudgett
committed
/*
* Since we are soon going to pass this bridge to a new
* technology we need to NULL out the tech_pvt pointer but
* don't worry as it still exists in dummy_bridge, ditto for the
* old technology.
*/
bridge->tech_pvt = NULL;
bridge->technology = new_technology;
/* Setup the new bridge technology. */
ast_debug(1, "Bridge %s: calling %s technology constructor\n",
bridge->uniqueid, new_technology->name);
if (new_technology->create && new_technology->create(bridge)) {
ast_log(LOG_WARNING, "Bridge %s: failed to setup bridge technology %s\n",
bridge->uniqueid, new_technology->name);
bridge->tech_pvt = dummy_bridge.tech_pvt;
bridge->technology = dummy_bridge.technology;
ast_module_unref(new_technology->mod);
return -1;
Richard Mudgett
committed
Joshua Colp
committed
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
/* To ensure that things are sane for the old technology move the channels it
* expects to the dummy bridge
*/
AST_LIST_TRAVERSE_SAFE_BEGIN(&bridge->channels, bridge_channel, entry) {
if (bridge_channel->just_joined) {
continue;
}
ast_debug(1, "Bridge %s: moving %p(%s) to dummy bridge temporarily\n",
bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan));
AST_LIST_REMOVE_CURRENT(entry);
AST_LIST_INSERT_TAIL(&dummy_bridge.channels, bridge_channel, entry);
dummy_bridge.num_channels++;
if (ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_LONELY)) {
dummy_bridge.num_lonely++;
}
if (!bridge_channel->suspended) {
dummy_bridge.num_active++;
}
}
AST_LIST_TRAVERSE_SAFE_END;
/* Take all the channels out of the old technology */
AST_LIST_TRAVERSE_SAFE_BEGIN(&dummy_bridge.channels, bridge_channel, entry) {
ast_debug(1, "Bridge %s: %p(%s) is leaving %s technology (dummy)\n",
dummy_bridge.uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
old_technology->name);
if (old_technology->leave) {
old_technology->leave(&dummy_bridge, bridge_channel);
}
AST_LIST_REMOVE_CURRENT(entry);
AST_LIST_INSERT_TAIL(&bridge->channels, bridge_channel, entry);
dummy_bridge.num_channels--;
if (ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_LONELY)) {
dummy_bridge.num_lonely--;
}
if (!bridge_channel->suspended) {
dummy_bridge.num_active--;
}
}
AST_LIST_TRAVERSE_SAFE_END;
ast_debug(1, "Bridge %s: calling %s technology stop\n",
dummy_bridge.uniqueid, old_technology->name);
if (old_technology->stop) {
old_technology->stop(&dummy_bridge);
}
Richard Mudgett
committed
Joshua Colp
committed
/* Add any new channels or re-add existing channels to the bridge. */
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
bridge_channel_complete_join(bridge, bridge_channel);
Richard Mudgett
committed
}
ast_debug(1, "Bridge %s: calling %s technology start\n",
bridge->uniqueid, new_technology->name);
if (new_technology->start && new_technology->start(bridge)) {
ast_log(LOG_WARNING, "Bridge %s: failed to start bridge technology %s\n",
bridge->uniqueid, new_technology->name);
Richard Mudgett
committed
}
/*
* Now that all the channels have been moved over we need to get
* rid of all the information the old technology may have left
* around.
*/
if (old_technology->destroy) {
ast_debug(1, "Bridge %s: deferring %s technology destructor\n",
dummy_bridge.uniqueid, old_technology->name);
bridge_queue_action_nodup(bridge, deferred_action);
} else {
ast_debug(1, "Bridge %s: calling %s technology destructor\n",
dummy_bridge.uniqueid, old_technology->name);
ast_module_unref(old_technology->mod);
}
Richard Mudgett
committed
Richard Mudgett
committed
}
/*!
* \internal
* \brief Bridge channel to check if a BRIDGE_PLAY_SOUND needs to be played.
* \since 12.0.0
*
* \param bridge_channel What to check.
*
* \return Nothing
*/
static void check_bridge_play_sound(struct ast_bridge_channel *bridge_channel)
Mark Michelson
committed
{
const char *play_file;
ast_channel_lock(bridge_channel->chan);
play_file = pbx_builtin_getvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND");
if (!ast_strlen_zero(play_file)) {
play_file = ast_strdupa(play_file);
pbx_builtin_setvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND", NULL);
} else {
play_file = NULL;
ast_channel_unlock(bridge_channel->chan);
Joshua Colp
committed
if (play_file) {
ast_bridge_channel_queue_playfile(bridge_channel, NULL, play_file, NULL);
Joshua Colp
committed
}
Mark Michelson
committed
}
/*!
* \internal
* \brief Check for any BRIDGE_PLAY_SOUND channel variables in the bridge.
* \since 12.0.0
*
* \param bridge What to operate on.
*
* \note On entry, the bridge is already locked.
*
* \return Nothing
*/
static void check_bridge_play_sounds(struct ast_bridge *bridge)
Mark Michelson
committed
{
struct ast_bridge_channel *bridge_channel;
Mark Michelson
committed
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
check_bridge_play_sound(bridge_channel);
}
Mark Michelson
committed
}
void ast_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid)
Mark Michelson
committed
{
ast_channel_stage_snapshot(chan);
pbx_builtin_setvar_helper(chan, "BRIDGEPEER", name);
pbx_builtin_setvar_helper(chan, "BRIDGEPVTCALLID", pvtid);
ast_channel_stage_snapshot_done(chan);
Mark Michelson
committed
}
Richard Mudgett
committed
/*!
* \internal
* \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a 2 party bridge.
Richard Mudgett
committed
* \since 12.0.0
*
* \param c0 Party of the first part.
* \param c1 Party of the second part.
*
* \note On entry, the bridge is already locked.
* \note The bridge is expected to have exactly two parties.
Richard Mudgett
committed
*
* \return Nothing
*/
static void set_bridge_peer_vars_2party(struct ast_channel *c0, struct ast_channel *c1)
Richard Mudgett
committed
{
const char *c0_name;
const char *c1_name;
const char *c0_pvtid = NULL;
const char *c1_pvtid = NULL;
#define UPDATE_BRIDGE_VARS_GET(chan, name, pvtid) \
do { \
name = ast_strdupa(ast_channel_name(chan)); \
if (ast_channel_tech(chan)->get_pvt_uniqueid) { \
pvtid = ast_strdupa(ast_channel_tech(chan)->get_pvt_uniqueid(chan)); \
} \
} while (0)
ast_channel_lock(c1);
UPDATE_BRIDGE_VARS_GET(c1, c1_name, c1_pvtid);
ast_channel_unlock(c1);
ast_channel_lock(c0);
ast_bridge_vars_set(c0, c1_name, c1_pvtid);
UPDATE_BRIDGE_VARS_GET(c0, c0_name, c0_pvtid);
ast_channel_unlock(c0);
ast_channel_lock(c1);
ast_bridge_vars_set(c1, c0_name, c0_pvtid);
ast_channel_unlock(c1);
Richard Mudgett
committed
}
/*!
* \internal
* \brief Fill the BRIDGEPEER value buffer with a comma separated list of channel names.
Richard Mudgett
committed
* \since 12.0.0
*
* \param buf Buffer to fill. The caller must guarantee the buffer is large enough.
* \param cur_idx Which index into names[] to skip.
* \param names Channel names to put in the buffer.
* \param num_names Number of names in the array.
Richard Mudgett
committed
*
* \return Nothing
*/
static void fill_bridgepeer_buf(char *buf, unsigned int cur_idx, const char *names[], unsigned int num_names)
Richard Mudgett
committed
{
int need_separator = 0;
unsigned int idx;
const char *src;
char *pos;
Richard Mudgett
committed
pos = buf;
for (idx = 0; idx < num_names; ++idx) {
if (idx == cur_idx) {
continue;
Richard Mudgett
committed
}
if (need_separator) {
*pos++ = ',';
Richard Mudgett
committed
}
need_separator = 1;
/* Copy name into buffer. */
src = names[idx];
while (*src) {
*pos++ = *src++;
Richard Mudgett
committed
}
}
Richard Mudgett
committed
}
/*!
* \internal
* \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a multi-party bridge.
Richard Mudgett
committed
* \since 12.0.0
*
* \param bridge What to operate on.
*
* \note On entry, the bridge is already locked.
* \note The bridge is expected to have more than two parties.
Richard Mudgett
committed
*
* \return Nothing
*/
static void set_bridge_peer_vars_multiparty(struct ast_bridge *bridge)
Richard Mudgett
committed
{
/*
* Set a maximum number of channel names for the BRIDGEPEER
* list. The plus one is for the current channel which is not
* put in the list.
*/
#define MAX_BRIDGEPEER_CHANS (10 + 1)
Richard Mudgett
committed
unsigned int idx;
unsigned int num_names;
unsigned int len;
const char **names;
char *buf;
struct ast_bridge_channel *bridge_channel;
/* Get first MAX_BRIDGEPEER_CHANS channel names. */
num_names = MIN(bridge->num_channels, MAX_BRIDGEPEER_CHANS);
names = ast_alloca(num_names * sizeof(*names));
idx = 0;
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
if (num_names <= idx) {
break;
Richard Mudgett
committed
}
ast_channel_lock(bridge_channel->chan);
names[idx++] = ast_strdupa(ast_channel_name(bridge_channel->chan));
ast_channel_unlock(bridge_channel->chan);
Richard Mudgett
committed
}
/* Determine maximum buf size needed. */
len = num_names;
for (idx = 0; idx < num_names; ++idx) {
len += strlen(names[idx]);
Richard Mudgett
committed
}
buf = ast_alloca(len);
Richard Mudgett
committed
/* Set the bridge channel variables. */
idx = 0;
buf[0] = '\0';
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
if (idx < num_names) {
fill_bridgepeer_buf(buf, idx, names, num_names);
Richard Mudgett
committed
}
++idx;
ast_channel_lock(bridge_channel->chan);
ast_bridge_vars_set(bridge_channel->chan, buf, NULL);
ast_channel_unlock(bridge_channel->chan);
Richard Mudgett
committed
}
}
/*!
* \internal
* \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a holding bridge.
Richard Mudgett
committed
* \since 12.0.0
*
* \param bridge What to operate on.
Richard Mudgett
committed
*
* \note On entry, the bridge is already locked.
Richard Mudgett
committed
*
* \return Nothing
*/
static void set_bridge_peer_vars_holding(struct ast_bridge *bridge)
Richard Mudgett
committed
{
struct ast_bridge_channel *bridge_channel;
Richard Mudgett
committed
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
ast_channel_lock(bridge_channel->chan);
ast_bridge_vars_set(bridge_channel->chan, NULL, NULL);
ast_channel_unlock(bridge_channel->chan);
Richard Mudgett
committed
}
}
/*!
* \internal
* \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in the bridge.
Richard Mudgett
committed
* \since 12.0.0
*
* \param bridge What to operate on.
*
* \note On entry, the bridge is already locked.
Richard Mudgett
committed
*
* \return Nothing
*/
static void set_bridge_peer_vars(struct ast_bridge *bridge)
Richard Mudgett
committed
{
if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_HOLDING) {
set_bridge_peer_vars_holding(bridge);
return;
}
if (bridge->num_channels < 2) {
return;
}
if (bridge->num_channels == 2) {
set_bridge_peer_vars_2party(AST_LIST_FIRST(&bridge->channels)->chan,
AST_LIST_LAST(&bridge->channels)->chan);
} else {
set_bridge_peer_vars_multiparty(bridge);
Richard Mudgett
committed
}
}
void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update)
Richard Mudgett
committed
{
if (!bridge->reconfigured) {
return;
Richard Mudgett
committed
}
bridge->reconfigured = 0;
if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_SMART)
&& smart_bridge_operation(bridge)) {
/* Smart bridge failed. */
Richard Mudgett
committed
bridge_dissolve(bridge, 0);
Richard Mudgett
committed
}
bridge_complete_join(bridge);
Richard Mudgett
committed
if (bridge->dissolved) {
return;
Richard Mudgett
committed
}
check_bridge_play_sounds(bridge);
set_bridge_peer_vars(bridge);
ast_bridge_publish_state(bridge);
Richard Mudgett
committed
if (colp_update) {
bridge_reconfigured_connected_line_update(bridge);
Richard Mudgett
committed
}
}
struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct ast_channel *chan)
Richard Mudgett
committed
{
struct ast_bridge_channel *bridge_channel;
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
if (bridge_channel->chan == chan) {
Richard Mudgett
committed
}
return bridge_channel;
Richard Mudgett
committed
}
void ast_bridge_notify_masquerade(struct ast_channel *chan)
{
struct ast_bridge_channel *bridge_channel;
struct ast_bridge *bridge;
/* Safely get the bridge_channel pointer for the chan. */
ast_channel_lock(chan);
bridge_channel = ast_channel_get_bridge_channel(chan);
ast_channel_unlock(chan);
if (!bridge_channel) {
/* Not in a bridge */
return;
}
ast_bridge_channel_lock_bridge(bridge_channel);
bridge = bridge_channel->bridge;
if (bridge_channel == bridge_find_channel(bridge, chan)) {
/*
* XXX ASTERISK-22366 this needs more work. The channels need
* to be made compatible again if the formats change. The
* bridge_channel thread needs to monitor for this case.
*/
Richard Mudgett
committed
/* The channel we want to notify is still in a bridge. */
bridge->v_table->notify_masquerade(bridge, bridge_channel);
Joshua Colp
committed
bridge_reconfigured(bridge, 1);
Richard Mudgett
committed
}
ast_bridge_unlock(bridge);
ao2_ref(bridge_channel, -1);
}
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
/*!
* \brief Internal bridge impart wait condition and associated conditional.
*/
struct bridge_channel_impart_cond {
AST_LIST_ENTRY(bridge_channel_impart_cond) node;
/*! Lock for the data structure */
ast_mutex_t lock;
/*! Wait condition */
ast_cond_t cond;
/*! Wait until done */
int done;
};
AST_LIST_HEAD_NOLOCK(bridge_channel_impart_ds_head, bridge_channel_impart_cond);
/*!
* \internal
* \brief Signal imparting threads to wake up.
* \since 13.9.0
*
* \param ds_head List of imparting threads to wake up.
*
* \return Nothing
*/
static void bridge_channel_impart_ds_head_signal(struct bridge_channel_impart_ds_head *ds_head)
{
if (ds_head) {
struct bridge_channel_impart_cond *cond;
while ((cond = AST_LIST_REMOVE_HEAD(ds_head, node))) {
ast_mutex_lock(&cond->lock);
cond->done = 1;
ast_cond_signal(&cond->cond);
ast_mutex_unlock(&cond->lock);
}
}
}
static void bridge_channel_impart_ds_head_dtor(void *doomed)
{
bridge_channel_impart_ds_head_signal(doomed);
ast_free(doomed);
}
/*!
* \internal
* \brief Fixup the bridge impart datastore.
* \since 13.9.0
*
* \param data Bridge impart datastore data to fixup from old_chan.
* \param old_chan The datastore is moving from this channel.
* \param new_chan The datastore is moving to this channel.
*
* \return Nothing
*/
static void bridge_channel_impart_ds_head_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
{
/*
* Signal any waiting impart threads. The masquerade is going to kill
* old_chan and we don't need to be waiting on new_chan.
*/
bridge_channel_impart_ds_head_signal(data);
}
static const struct ast_datastore_info bridge_channel_impart_ds_info = {
.type = "bridge-impart-ds",
.destroy = bridge_channel_impart_ds_head_dtor,
.chan_fixup = bridge_channel_impart_ds_head_fixup,
};
/*!
* \internal
* \brief Add impart wait datastore conditional to channel.
* \since 13.9.0
*
* \param chan Channel to add the impart wait conditional.
* \param cond Imparting conditional to add.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int bridge_channel_impart_add(struct ast_channel *chan, struct bridge_channel_impart_cond *cond)
{
struct ast_datastore *datastore;
struct bridge_channel_impart_ds_head *ds_head;
ast_channel_lock(chan);
datastore = ast_channel_datastore_find(chan, &bridge_channel_impart_ds_info, NULL);
if (!datastore) {
datastore = ast_datastore_alloc(&bridge_channel_impart_ds_info, NULL);
if (!datastore) {
ast_channel_unlock(chan);
return -1;
}
ds_head = ast_calloc(1, sizeof(*ds_head));
if (!ds_head) {
ast_channel_unlock(chan);
ast_datastore_free(datastore);
return -1;
}
datastore->data = ds_head;
ast_channel_datastore_add(chan, datastore);
} else {
ds_head = datastore->data;
ast_assert(ds_head != NULL);
}
AST_LIST_INSERT_TAIL(ds_head, cond, node);
ast_channel_unlock(chan);
return 0;
}
void bridge_channel_impart_signal(struct ast_channel *chan)
{
struct ast_datastore *datastore;
ast_channel_lock(chan);
datastore = ast_channel_datastore_find(chan, &bridge_channel_impart_ds_info, NULL);
if (datastore) {
bridge_channel_impart_ds_head_signal(datastore->data);
}
ast_channel_unlock(chan);
}
/*!
* \internal
* \brief Block imparting channel thread until signaled.
* \since 13.9.0
*
* \param cond Imparting conditional to wait for.
*
* \return Nothing
*/
static void bridge_channel_impart_wait(struct bridge_channel_impart_cond *cond)
{
ast_mutex_lock(&cond->lock);
while (!cond->done) {
ast_cond_wait(&cond->cond, &cond->lock);
}
ast_mutex_unlock(&cond->lock);
}
Richard Mudgett
committed
/*
* XXX ASTERISK-21271 make ast_bridge_join() require features to be allocated just like ast_bridge_impart() and not expect the struct back.
Richard Mudgett
committed
*
* This change is really going to break ConfBridge. All other
* users are easily changed. However, it is needed so the
* bridging code can manipulate features on all channels
* consistently no matter how they joined.
*
* Need to update the features parameter doxygen when this
* change is made to be like ast_bridge_impart().
*/
int ast_bridge_join(struct ast_bridge *bridge,
Richard Mudgett
committed
struct ast_channel *chan,
struct ast_channel *swap,
struct ast_bridge_features *features,
struct ast_bridge_tech_optimizations *tech_args,
enum ast_bridge_join_flags flags)
Richard Mudgett
committed
{
struct ast_bridge_channel *bridge_channel;
int res = 0;
Richard Mudgett
committed
bridge_channel = bridge_channel_internal_alloc(bridge);
if (flags & AST_BRIDGE_JOIN_PASS_REFERENCE) {
Richard Mudgett
committed
ao2_ref(bridge, -1);
}
if (!bridge_channel) {
ao2_t_cleanup(swap, "Error exit: bridge_channel alloc failed");
res = -1;
Richard Mudgett
committed
goto join_exit;
}
/* XXX ASTERISK-21271 features cannot be NULL when passed in. When it is changed to allocated we can do like ast_bridge_impart() and allocate one. */
Richard Mudgett
committed
ast_assert(features != NULL);
if (!features) {
ao2_ref(bridge_channel, -1);
ao2_t_cleanup(swap, "Error exit: features is NULL");
res = -1;
Richard Mudgett
committed
goto join_exit;
}
if (tech_args) {
bridge_channel->tech_args = *tech_args;
}
ast_channel_lock(chan);
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) {
res = -1;
} else {
ast_channel_internal_bridge_channel_set(chan, bridge_channel);
}
Richard Mudgett
committed
ast_channel_unlock(chan);
bridge_channel->thread = pthread_self();
bridge_channel->chan = chan;
bridge_channel->swap = swap;
bridge_channel->features = features;
bridge_channel->inhibit_colp = !!(flags & AST_BRIDGE_JOIN_INHIBIT_JOIN_COLP);
Richard Mudgett
committed
/* allow subclass to peek at upcoming push operation */
if (bridge->v_table->push_peek && !res) {
struct ast_bridge_channel *bcswap = NULL;
ast_bridge_lock(bridge);
if (bridge_channel->swap) {
bcswap = bridge_find_channel(bridge, bridge_channel->swap);
}
res = bridge->v_table->push_peek(bridge, bridge_channel, bcswap);
ast_bridge_unlock(bridge);
}
if (!res) {
res = bridge_channel_internal_join(bridge_channel);
}
Richard Mudgett
committed
/* Cleanup all the data in the bridge channel after it leaves the bridge. */
ast_channel_lock(chan);
ast_channel_internal_bridge_channel_set(chan, NULL);
ast_channel_unlock(chan);
bridge_channel->chan = NULL;
/* If bridge_channel->swap is not NULL then the join failed. */
ao2_t_cleanup(bridge_channel->swap, "Bridge complete: join failed");
Richard Mudgett
committed
bridge_channel->swap = NULL;
bridge_channel->features = NULL;
ao2_ref(bridge_channel, -1);
join_exit:;
ast_bridge_run_after_callback(chan);
bridge_channel_impart_signal(chan);
Richard Mudgett
committed
if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)
&& !ast_bridge_setup_after_goto(chan)) {
Richard Mudgett
committed
/* Claim the after bridge goto is an async goto destination. */
ast_channel_lock(chan);
ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
ast_channel_unlock(chan);
}
return res;
Richard Mudgett
committed
}
/*! \brief Thread responsible for imparted bridged channels to be departed */
static void *bridge_channel_depart_thread(void *data)
{
struct ast_bridge_channel *bridge_channel = data;
Richard Mudgett
committed
if (bridge_channel->callid) {
ast_callid_threadassoc_add(bridge_channel->callid);
}
res = bridge_channel_internal_join(bridge_channel);
Richard Mudgett
committed
/*
* cleanup
*
* If bridge_channel->swap is not NULL then the join failed.
*/
ao2_t_cleanup(bridge_channel->swap, "Bridge complete: Departable impart join failed");
Richard Mudgett
committed
bridge_channel->swap = NULL;
ast_bridge_features_destroy(bridge_channel->features);
bridge_channel->features = NULL;
ast_bridge_discard_after_callback(bridge_channel->chan,
res ? AST_BRIDGE_AFTER_CB_REASON_IMPART_FAILED : AST_BRIDGE_AFTER_CB_REASON_DEPART);
/* If join failed there will be impart threads waiting. */
bridge_channel_impart_signal(bridge_channel->chan);
ast_bridge_discard_after_goto(bridge_channel->chan);
Richard Mudgett
committed
return NULL;
}
/*! \brief Thread responsible for independent imparted bridged channels */
static void *bridge_channel_ind_thread(void *data)
{
struct ast_bridge_channel *bridge_channel = data;
Richard Mudgett
committed
struct ast_channel *chan;
if (bridge_channel->callid) {
ast_callid_threadassoc_add(bridge_channel->callid);
}
bridge_channel_internal_join(bridge_channel);
Richard Mudgett
committed
chan = bridge_channel->chan;
/* cleanup */
ast_channel_lock(chan);
ast_channel_internal_bridge_channel_set(chan, NULL);
ast_channel_unlock(chan);
bridge_channel->chan = NULL;
/* If bridge_channel->swap is not NULL then the join failed. */
ao2_t_cleanup(bridge_channel->swap, "Bridge complete: Independent impart join failed");
Richard Mudgett
committed
bridge_channel->swap = NULL;
ast_bridge_features_destroy(bridge_channel->features);
bridge_channel->features = NULL;
ao2_ref(bridge_channel, -1);
ast_bridge_run_after_callback(chan);
/* If join failed there will be impart threads waiting. */
bridge_channel_impart_signal(chan);
ast_bridge_run_after_goto(chan);
Richard Mudgett
committed
return NULL;
}
static int bridge_impart_internal(struct ast_bridge *bridge,
struct ast_channel *chan,
struct ast_channel *swap,
struct ast_bridge_features *features,
enum ast_bridge_impart_flags flags,
struct bridge_channel_impart_cond *cond)
Richard Mudgett
committed
{
int res = 0;
Richard Mudgett
committed
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);
Matthew Jordan
committed
ast_bridge_features_destroy(features);
return -1;
}
Richard Mudgett
committed
/* Supply an empty features structure if the caller did not. */
if (!features) {
features = ast_bridge_features_new();
if (!features) {
return -1;
}
}
/* Try to allocate a structure for the bridge channel */
bridge_channel = bridge_channel_internal_alloc(bridge);
Richard Mudgett
committed
if (!bridge_channel) {
ast_bridge_features_destroy(features);
return -1;
}
ast_channel_lock(chan);
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);
}
Richard Mudgett
committed
ast_channel_unlock(chan);
bridge_channel->chan = chan;
bridge_channel->swap = ao2_t_bump(swap, "Setting up bridge impart");
Richard Mudgett
committed
bridge_channel->features = features;
bridge_channel->inhibit_colp = !!(flags & AST_BRIDGE_IMPART_INHIBIT_JOIN_COLP);
bridge_channel->depart_wait =
(flags & AST_BRIDGE_IMPART_CHAN_MASK) == AST_BRIDGE_IMPART_CHAN_DEPARTABLE;
Richard Mudgett
committed
bridge_channel->callid = ast_read_threadstorage_callid();
/* allow subclass to peek at swap channel before it can hangup */
if (bridge->v_table->push_peek && !res) {
struct ast_bridge_channel *bcswap = NULL;
ast_bridge_lock(bridge);
if (bridge_channel->swap) {
bcswap = bridge_find_channel(bridge, bridge_channel->swap);
}
res = bridge->v_table->push_peek(bridge, bridge_channel, bcswap);
ast_bridge_unlock(bridge);
}
Richard Mudgett
committed
/* Actually create the thread that will handle the channel */
if (!res) {
res = bridge_channel_impart_add(chan, cond);
}
if (!res) {
if ((flags & AST_BRIDGE_IMPART_CHAN_MASK) == AST_BRIDGE_IMPART_CHAN_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) {
bridge_channel_impart_wait(cond);
Richard Mudgett
committed
}
if (res) {
/* cleanup */
ast_channel_lock(chan);
ast_channel_internal_bridge_channel_set(chan, NULL);
ast_channel_unlock(chan);
bridge_channel->chan = NULL;
ao2_t_cleanup(bridge_channel->swap, "Bridge complete: Impart failed");
Richard Mudgett
committed
bridge_channel->swap = NULL;
ast_bridge_features_destroy(bridge_channel->features);
bridge_channel->features = NULL;
ao2_ref(bridge_channel, -1);
return -1;
}
return 0;
}
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
int ast_bridge_impart(struct ast_bridge *bridge,
struct ast_channel *chan,
struct ast_channel *swap,
struct ast_bridge_features *features,
enum ast_bridge_impart_flags flags)
{
struct bridge_channel_impart_cond cond = {
.done = 0,
};
int res;
ast_mutex_init(&cond.lock);
ast_cond_init(&cond.cond, NULL);
res = bridge_impart_internal(bridge, chan, swap, features, flags, &cond);
if (res) {
/* Impart failed. Signal any other waiting impart threads */
bridge_channel_impart_signal(chan);
}
ast_cond_destroy(&cond.cond);
ast_mutex_destroy(&cond.lock);
return res;
}
Richard Mudgett
committed
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
int ast_bridge_depart(struct ast_channel *chan)
{
struct ast_bridge_channel *bridge_channel;
int departable;
ast_channel_lock(chan);
bridge_channel = ast_channel_internal_bridge_channel(chan);
departable = bridge_channel && bridge_channel->depart_wait;
ast_channel_unlock(chan);
if (!departable) {
ast_log(LOG_ERROR, "Channel %s cannot be departed.\n",
ast_channel_name(chan));
/*
* Should never happen. It likely means that
* ast_bridge_depart() is called by two threads for the same
* channel, the channel was never imparted to be departed, or it
* has already been departed.
*/
ast_assert(0);
return -1;
}
/*
Richard Mudgett
committed
* We are claiming the bridge_channel reference held by
* bridge_channel_depart_thread().
Richard Mudgett
committed
*/
Richard Mudgett
committed
ast_bridge_channel_leave_bridge(bridge_channel,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING);
Richard Mudgett
committed
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
/* Wait for the depart thread to die */
ast_debug(1, "Waiting for %p(%s) bridge thread to die.\n",
bridge_channel, ast_channel_name(bridge_channel->chan));
pthread_join(bridge_channel->thread, NULL);
ast_channel_lock(chan);
ast_channel_internal_bridge_channel_set(chan, NULL);
ast_channel_unlock(chan);
/* We can get rid of the bridge_channel after the depart thread has died. */
ao2_ref(bridge_channel, -1);
return 0;
}
int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan)
{
struct ast_bridge_channel *bridge_channel;
ast_bridge_lock(bridge);
/* Try to find the channel that we want to remove */
if (!(bridge_channel = bridge_find_channel(bridge, chan))) {
Richard Mudgett
committed
ast_bridge_unlock(bridge);
return -1;
}
Richard Mudgett
committed
ast_bridge_channel_leave_bridge(bridge_channel,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING);
Richard Mudgett
committed
ast_bridge_unlock(bridge);
return 0;
}
Richard Mudgett
committed
static void kick_it(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
{
Richard Mudgett
committed
ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING);
Richard Mudgett
committed
}
int ast_bridge_kick(struct ast_bridge *bridge, struct ast_channel *chan)
{
struct ast_bridge_channel *bridge_channel;