Newer
Older
Richard Mudgett
committed
* 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)
Richard Mudgett
committed
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);
}
static int complete_bridge_live_search(void *obj, void *arg, int flags)
Richard Mudgett
committed
{
struct ast_bridge *bridge = obj;
Richard Mudgett
committed
if (ast_cli_completion_add(ast_strdup(bridge->uniqueid))) {
return CMP_STOP;
Richard Mudgett
committed
}
Richard Mudgett
committed
return 0;
}
static char *complete_bridge_live(const char *word)
Richard Mudgett
committed
{
ao2_callback(bridges, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
complete_bridge_live_search, (char *) word);
Richard Mudgett
committed
Richard Mudgett
committed
}
Richard Mudgett
committed
static char *handle_bridge_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
#define FORMAT_HDR "%-36s %5s %-15s %-15s %s\n"
#define FORMAT_ROW "%-36s %5u %-15s %-15s %s\n"
Richard Mudgett
committed
struct ao2_iterator iter;
Richard Mudgett
committed
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;
ast_cli(a->fd, FORMAT_HDR, "Bridge-ID", "Chans", "Type", "Technology", "Duration");
iter = ao2_iterator_init(bridges, 0);
for (; (bridge = ao2_iterator_next(&iter)); ao2_ref(bridge, -1)) {
struct ast_bridge_snapshot *snapshot = ast_bridge_get_snapshot(bridge);
char print_time[32];
ast_format_duration_hh_mm_ss(ast_tvnow().tv_sec - snapshot->creationtime.tv_sec, print_time, sizeof(print_time));
if (snapshot) {
ast_cli(a->fd, FORMAT_ROW,
snapshot->uniqueid,
snapshot->num_channels,
S_OR(snapshot->subclass, "<unknown>"),
S_OR(snapshot->technology, "<unknown>"),
print_time);
ao2_ref(snapshot, -1);
}
Richard Mudgett
committed
}
ao2_iterator_destroy(&iter);
Richard Mudgett
committed
return CLI_SUCCESS;
Richard Mudgett
committed
#undef FORMAT_HDR
#undef FORMAT_ROW
/*! \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;
struct ast_channel_snapshot *snapshot;
snapshot = ast_channel_snapshot_get_latest(uniqueid);
if (!snapshot) {
return 0;
}
ast_cli(a->fd, "Channel: %s\n", snapshot->base->name);
ao2_ref(snapshot, -1);
return 0;
}
Richard Mudgett
committed
static char *handle_bridge_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
struct ast_bridge_snapshot *snapshot;
Richard Mudgett
committed
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_live(a->word);
Richard Mudgett
committed
}
return NULL;
}
if (a->argc != 3) {
return CLI_SHOWUSAGE;
snapshot = ast_bridge_get_snapshot_by_uniqueid(a->argv[2]);
if (!snapshot) {
Richard Mudgett
committed
ast_cli(a->fd, "Bridge '%s' not found\n", a->argv[2]);
return CLI_SUCCESS;
}
ast_format_duration_hh_mm_ss(ast_tvnow().tv_sec - snapshot->creationtime.tv_sec, print_time, sizeof(print_time));
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, "Subclass: %s\n", snapshot->subclass);
ast_cli(a->fd, "Creator: %s\n", snapshot->creator);
ast_cli(a->fd, "Name: %s\n", snapshot->name);
ast_cli(a->fd, "Video-Source-Id: %s\n", snapshot->video_source_id);
ast_cli(a->fd, "Num-Channels: %u\n", snapshot->num_channels);
ast_cli(a->fd, "Num-Active: %u\n", snapshot->num_active);
ast_cli(a->fd, "Duration: %s\n", print_time);
ao2_callback(snapshot->channels, OBJ_NODATA, bridge_show_specific_print_channel, a);
Richard Mudgett
committed
return CLI_SUCCESS;
Joshua Colp
committed
#ifdef AST_DEVMODE
Richard Mudgett
committed
static char *handle_bridge_destroy_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Richard Mudgett
committed
struct ast_bridge *bridge;
Richard Mudgett
committed
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) {
return complete_bridge_live(a->word);
Richard Mudgett
committed
}
return NULL;
Richard Mudgett
committed
if (a->argc != 3) {
return CLI_SHOWUSAGE;
}
bridge = ast_bridge_find_by_id(a->argv[2]);
Richard Mudgett
committed
if (!bridge) {
ast_cli(a->fd, "Bridge '%s' not found\n", a->argv[2]);
return CLI_SUCCESS;
}
Richard Mudgett
committed
ast_cli(a->fd, "Destroying bridge '%s'\n", a->argv[2]);
Richard Mudgett
committed
ast_bridge_destroy(bridge, 0);
Richard Mudgett
committed
return CLI_SUCCESS;
Joshua Colp
committed
#endif
static char *complete_bridge_participant(const char *bridge_name, const char *word)
Joshua Colp
committed
struct ast_bridge *bridge;
Richard Mudgett
committed
struct ast_bridge_channel *bridge_channel;
int wordlen;
bridge = ast_bridge_find_by_id(bridge_name);
Richard Mudgett
committed
if (!bridge) {
return NULL;
Richard Mudgett
committed
ast_bridge_lock(bridge);
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
if (!strncasecmp(ast_channel_name(bridge_channel->chan), word, wordlen)) {
if (ast_cli_completion_add(ast_strdup(ast_channel_name(bridge_channel->chan)))) {
break;
Richard Mudgett
committed
}
Joshua Colp
committed
ao2_ref(bridge, -1);
Richard Mudgett
committed
return NULL;
Richard Mudgett
committed
static char *handle_bridge_kick_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static const char * const completions[] = { "all", NULL };
Joshua Colp
committed
struct ast_bridge *bridge;
Richard Mudgett
committed
switch (cmd) {
case CLI_INIT:
e->command = "bridge kick";
e->usage =
Joshua Colp
committed
"Usage: bridge kick <bridge-id> <channel-name | all>\n"
" Kick the <channel-name> channel out of the <bridge-id> bridge\n"
" If all is specified as the channel name then all channels will be\n"
" kicked out of the bridge.\n";
Richard Mudgett
committed
return NULL;
case CLI_GENERATE:
if (a->pos == 2) {
return complete_bridge_live(a->word);
Richard Mudgett
committed
}
if (a->pos == 3) {
ast_cli_complete(a->word, completions, -1);
return complete_bridge_participant(a->argv[2], a->word);
Richard Mudgett
committed
}
return NULL;
}
Richard Mudgett
committed
if (a->argc != 4) {
return CLI_SHOWUSAGE;
}
bridge = ast_bridge_find_by_id(a->argv[2]);
Richard Mudgett
committed
if (!bridge) {
ast_cli(a->fd, "Bridge '%s' not found\n", a->argv[2]);
return CLI_SUCCESS;
}
Joshua Colp
committed
if (!strcasecmp(a->argv[3], "all")) {
struct ast_bridge_channel *bridge_channel;
Joshua Colp
committed
ast_cli(a->fd, "Kicking all channels from bridge '%s'\n", a->argv[2]);
Richard Mudgett
committed
Joshua Colp
committed
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
ast_bridge_lock(bridge);
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
ast_bridge_channel_queue_callback(bridge_channel, 0, kick_it, NULL, 0);
}
ast_bridge_unlock(bridge);
} else {
struct ast_channel *chan;
chan = ast_channel_get_by_name_prefix(a->argv[3], strlen(a->argv[3]));
if (!chan) {
ast_cli(a->fd, "Channel '%s' not found\n", a->argv[3]);
ao2_ref(bridge, -1);
return CLI_SUCCESS;
}
ast_cli(a->fd, "Kicking channel '%s' from bridge '%s'\n",
ast_channel_name(chan), a->argv[2]);
ast_bridge_kick(bridge, chan);
ast_channel_unref(chan);
}
ao2_ref(bridge, -1);
Richard Mudgett
committed
return CLI_SUCCESS;
Richard Mudgett
committed
/*! Bridge technology capabilities to string. */
static const char *tech_capability2str(uint32_t capabilities)
Richard Mudgett
committed
const char *type;
if (capabilities & AST_BRIDGE_CAPABILITY_HOLDING) {
type = "Holding";
} else if (capabilities & AST_BRIDGE_CAPABILITY_EARLY) {
type = "Early";
} else if (capabilities & AST_BRIDGE_CAPABILITY_NATIVE) {
type = "Native";
} else if (capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX) {
type = "1to1Mix";
} else if (capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) {
type = "MultiMix";
} else {
type = "<Unknown>";
Richard Mudgett
committed
return type;
Richard Mudgett
committed
static char *handle_bridge_technology_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Richard Mudgett
committed
#define FORMAT_HDR "%-20s %-20s %8s %s\n"
#define FORMAT_ROW "%-20s %-20s %8u %s\n"
Richard Mudgett
committed
struct ast_bridge_technology *cur;
Richard Mudgett
committed
switch (cmd) {
case CLI_INIT:
e->command = "bridge technology show";
e->usage =
"Usage: bridge technology show\n"
" List registered bridge technologies\n";
return NULL;
case CLI_GENERATE:
return NULL;
Richard Mudgett
committed
ast_cli(a->fd, FORMAT_HDR, "Name", "Type", "Priority", "Suspended");
AST_RWLIST_RDLOCK(&bridge_technologies);
AST_RWLIST_TRAVERSE(&bridge_technologies, cur, entry) {
const char *type;
Richard Mudgett
committed
/* Decode type for display */
type = tech_capability2str(cur->capabilities);
Richard Mudgett
committed
ast_cli(a->fd, FORMAT_ROW, cur->name, type, cur->preference,
AST_CLI_YESNO(cur->suspended));
}
AST_RWLIST_UNLOCK(&bridge_technologies);
return CLI_SUCCESS;
#undef FORMAT
static char *complete_bridge_technology(const char *word)
Richard Mudgett
committed
struct ast_bridge_technology *cur;
int wordlen;
Richard Mudgett
committed
wordlen = strlen(word);
AST_RWLIST_RDLOCK(&bridge_technologies);
AST_RWLIST_TRAVERSE(&bridge_technologies, cur, entry) {
if (!strncasecmp(cur->name, word, wordlen)) {
if (ast_cli_completion_add(ast_strdup(cur->name))) {
break;
}
Richard Mudgett
committed
}
}
AST_RWLIST_UNLOCK(&bridge_technologies);
Richard Mudgett
committed
return NULL;
Richard Mudgett
committed
static char *handle_bridge_technology_suspend(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Richard Mudgett
committed
struct ast_bridge_technology *cur;
int suspend;
int successful;
switch (cmd) {
case CLI_INIT:
e->command = "bridge technology {suspend|unsuspend}";
e->usage =
"Usage: bridge technology {suspend|unsuspend} <technology-name>\n"
" Suspend or unsuspend a bridge technology.\n";
return NULL;
case CLI_GENERATE:
if (a->pos == 3) {
return complete_bridge_technology(a->word);
Richard Mudgett
committed
return NULL;
}
if (a->argc != 4) {
return CLI_SHOWUSAGE;
}
suspend = !strcasecmp(a->argv[2], "suspend");
successful = 0;
AST_RWLIST_WRLOCK(&bridge_technologies);
AST_RWLIST_TRAVERSE(&bridge_technologies, cur, entry) {
if (!strcasecmp(cur->name, a->argv[3])) {
successful = 1;
if (suspend) {
ast_bridge_technology_suspend(cur);
} else {
ast_bridge_technology_unsuspend(cur);
}
break;
Richard Mudgett
committed
}
AST_RWLIST_UNLOCK(&bridge_technologies);
if (successful) {
if (suspend) {
ast_cli(a->fd, "Suspended bridge technology '%s'\n", a->argv[3]);
} else {
ast_cli(a->fd, "Unsuspended bridge technology '%s'\n", a->argv[3]);
Richard Mudgett
committed
} else {
ast_cli(a->fd, "Bridge technology '%s' not found\n", a->argv[3]);
Richard Mudgett
committed
return CLI_SUCCESS;
Richard Mudgett
committed
static struct ast_cli_entry bridge_cli[] = {
AST_CLI_DEFINE(handle_bridge_show_all, "List all bridges"),
AST_CLI_DEFINE(handle_bridge_show_specific, "Show information about a bridge"),
Joshua Colp
committed
#ifdef AST_DEVMODE
Richard Mudgett
committed
AST_CLI_DEFINE(handle_bridge_destroy_specific, "Destroy a bridge"),
Joshua Colp
committed
#endif
Richard Mudgett
committed
AST_CLI_DEFINE(handle_bridge_kick_channel, "Kick a channel from a bridge"),
AST_CLI_DEFINE(handle_bridge_technology_show, "List registered bridge technologies"),
AST_CLI_DEFINE(handle_bridge_technology_suspend, "Suspend/unsuspend a bridge technology"),
};
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
static int handle_manager_bridge_tech_suspend(struct mansession *s, const struct message *m, int suspend)
{
const char *name = astman_get_header(m, "BridgeTechnology");
struct ast_bridge_technology *cur;
int successful = 0;
if (ast_strlen_zero(name)) {
astman_send_error(s, m, "BridgeTechnology must be provided");
return 0;
}
AST_RWLIST_RDLOCK(&bridge_technologies);
AST_RWLIST_TRAVERSE(&bridge_technologies, cur, entry) {
if (!strcasecmp(cur->name, name)) {
successful = 1;
if (suspend) {
ast_bridge_technology_suspend(cur);
} else {
ast_bridge_technology_unsuspend(cur);
}
break;
}
}
AST_RWLIST_UNLOCK(&bridge_technologies);
if (!successful) {
astman_send_error(s, m, "BridgeTechnology not found");
return 0;
}
astman_send_ack(s, m, (suspend ? "Suspended bridge technology" : "Unsuspended bridge technology"));
return 0;
}
static int manager_bridge_tech_suspend(struct mansession *s, const struct message *m)
{
return handle_manager_bridge_tech_suspend(s, m, 1);
}
static int manager_bridge_tech_unsuspend(struct mansession *s, const struct message *m)
{
return handle_manager_bridge_tech_suspend(s, m, 0);
}
static int manager_bridge_tech_list(struct mansession *s, const struct message *m)
{
const char *id = astman_get_header(m, "ActionID");
RAII_VAR(struct ast_str *, id_text, ast_str_create(128), ast_free);
struct ast_bridge_technology *cur;
int num_items = 0;
if (!id_text) {
astman_send_error(s, m, "Internal error");
return -1;
}
if (!ast_strlen_zero(id)) {
ast_str_set(&id_text, 0, "ActionID: %s\r\n", id);
}
Richard Mudgett
committed
astman_send_listack(s, m, "Bridge technology listing will follow", "start");
AST_RWLIST_RDLOCK(&bridge_technologies);
AST_RWLIST_TRAVERSE(&bridge_technologies, cur, entry) {
const char *type;
type = tech_capability2str(cur->capabilities);
astman_append(s,
"Event: BridgeTechnologyListItem\r\n"
"BridgeTechnology: %s\r\n"
"BridgeType: %s\r\n"
"BridgeSuspended: %s\r\n"
"%s"
"\r\n",
cur->name, type, cur->preference, AST_YESNO(cur->suspended),
ast_str_buffer(id_text));
++num_items;
}
AST_RWLIST_UNLOCK(&bridge_technologies);
astman_send_list_complete_start(s, m, "BridgeTechnologyListComplete", num_items);
astman_send_list_complete_end(s);
return 0;
}
/*!
* \internal
* \brief Print bridge object key (name).
* \since 12.0.0
*
* \param v_obj A pointer to the object we want the key printed.
* \param where User data needed by prnt to determine where to put output.
* \param prnt Print output callback function to use.
*
* \return Nothing
*/
static void bridge_prnt_obj(void *v_obj, void *where, ao2_prnt_fn *prnt)
{
struct ast_bridge *bridge = v_obj;
if (!bridge) {
return;
}
bridge->uniqueid, bridge->v_table->name, bridge->num_channels);
}
Richard Mudgett
committed
/*!
* \internal
* \brief Shutdown the bridging system. Stuff to do on graceful shutdown.
* \since 13.3.0
Richard Mudgett
committed
*
* \return Nothing
*/
static void bridge_cleanup(void)
ast_manager_unregister("BridgeTechnologyList");
ast_manager_unregister("BridgeTechnologySuspend");
ast_manager_unregister("BridgeTechnologyUnsuspend");
Richard Mudgett
committed
ast_cli_unregister_multiple(bridge_cli, ARRAY_LEN(bridge_cli));
ao2_container_unregister("bridges");
Richard Mudgett
committed
ao2_cleanup(bridges);
bridges = NULL;
ao2_cleanup(bridge_manager);
bridge_manager = NULL;
Richard Mudgett
committed
int ast_bridging_init(void)
ast_register_cleanup(bridge_cleanup);
Richard Mudgett
committed
if (ast_stasis_bridging_init()) {
return -1;
Richard Mudgett
committed
bridge_manager = bridge_manager_create();
if (!bridge_manager) {
return -1;
Richard Mudgett
committed
bridges = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, bridge_sort_cmp, NULL);
if (!bridges) {
return -1;
ao2_container_register("bridges", bridges, bridge_prnt_obj);
Richard Mudgett
committed
ast_bridging_init_basic();
Richard Mudgett
committed
ast_cli_register_multiple(bridge_cli, ARRAY_LEN(bridge_cli));
ast_manager_register_xml_core("BridgeTechnologyList", 0, manager_bridge_tech_list);
ast_manager_register_xml_core("BridgeTechnologySuspend", 0, manager_bridge_tech_suspend);
ast_manager_register_xml_core("BridgeTechnologyUnsuspend", 0, manager_bridge_tech_unsuspend);
Richard Mudgett
committed
return 0;