Skip to content
Snippets Groups Projects
bridge.c 164 KiB
Newer Older
  • Learn to ignore specific revisions
  • static int complete_bridge_live_search(void *obj, void *arg, int flags)
    
    	struct ast_bridge *bridge = obj;
    
    	if (ast_cli_completion_add(ast_strdup(bridge->uniqueid))) {
    		return CMP_STOP;
    
    static char *complete_bridge_live(const char *word)
    
    	ao2_callback(bridges, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
    		complete_bridge_live_search, (char *) word);
    
    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"
    
    	struct ast_bridge *bridge;
    
    
    	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));
    
    			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);
    		}
    
    /*! \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) {
    
    	ast_cli(a->fd, "Channel: %s\n", snapshot->base->name);
    
    static char *handle_bridge_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	struct ast_bridge_snapshot *snapshot;
    
    	char print_time[32];
    
    
    	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);
    
    	snapshot = ast_bridge_get_snapshot_by_uniqueid(a->argv[2]);
    	if (!snapshot) {
    
    		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-Mode: %s\n", ast_bridge_video_mode_to_string(snapshot->video_mode));
    
    	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);
    
    	ao2_ref(snapshot, -1);
    
    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) {
    
    			return complete_bridge_live(a->word);
    
    	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 *word)
    
    	struct ast_bridge_channel *bridge_channel;
    	int wordlen;
    
    
    	bridge = ast_bridge_find_by_id(bridge_name);
    
    	wordlen = strlen(word);
    
    	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;
    
    	ast_bridge_unlock(bridge);
    
    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 };
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "bridge kick";
    		e->usage =
    
    			"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";
    
    			return complete_bridge_live(a->word);
    
    			ast_cli_complete(a->word, completions, -1);
    			return complete_bridge_participant(a->argv[2], a->word);
    
    	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;
    	}
    
    	if (!strcasecmp(a->argv[3], "all")) {
    		struct ast_bridge_channel *bridge_channel;
    
    		ast_cli(a->fd, "Kicking all channels from bridge '%s'\n", a->argv[2]);
    
    		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);
    
    /*! Bridge technology capabilities to string. */
    static const char *tech_capability2str(uint32_t capabilities)
    
    	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>";
    
    static char *handle_bridge_technology_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    #define FORMAT_ROW "%-20s %-20s %8u %s\n"
    
    	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;
    
    	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;
    
    		/* Decode type for display */
    		type = tech_capability2str(cur->capabilities);
    
    		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)
    
    	struct ast_bridge_technology *cur;
    	int wordlen;
    
    	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;
    			}
    
    static char *handle_bridge_technology_suspend(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	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);
    
    		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;
    
    	}
    	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]);
    
    	} else {
    		ast_cli(a->fd, "Bridge technology '%s' not found\n", a->argv[3]);
    
    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"),
    
    	AST_CLI_DEFINE(handle_bridge_destroy_specific, "Destroy a bridge"),
    
    	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"),
    };
    
    
    
    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;
    
    
    	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);
    	}
    
    
    	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"
    
    			"BridgePriority: %u\r\n"
    
    			"BridgeSuspended: %s\r\n"
    			"%s"
    			"\r\n",
    			cur->name, type, cur->preference, AST_YESNO(cur->suspended),
    			ast_str_buffer(id_text));
    
    	astman_send_list_complete_start(s, m, "BridgeTechnologyListComplete", num_items);
    	astman_send_list_complete_end(s);
    
    /*!
     * \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.
     */
    static void bridge_prnt_obj(void *v_obj, void *where, ao2_prnt_fn *prnt)
    {
    	struct ast_bridge *bridge = v_obj;
    
    	if (!bridge) {
    		return;
    	}
    
    	prnt(where, "%s %s chans:%u",
    
    		bridge->uniqueid, bridge->v_table->name, bridge->num_channels);
    }
    
    
     * \brief Shutdown the bridging system.  Stuff to do on graceful shutdown.
     * \since 13.3.0
    
    static void bridge_cleanup(void)
    
    	ast_manager_unregister("BridgeTechnologyList");
    	ast_manager_unregister("BridgeTechnologySuspend");
    	ast_manager_unregister("BridgeTechnologyUnsuspend");
    
    	ast_cli_unregister_multiple(bridge_cli, ARRAY_LEN(bridge_cli));
    
    	ao2_container_unregister("bridges");
    
    	ao2_cleanup(bridges);
    	bridges = NULL;
    	ao2_cleanup(bridge_manager);
    	bridge_manager = NULL;
    
    	ast_register_cleanup(bridge_cleanup);
    
    	bridge_manager = bridge_manager_create();
    	if (!bridge_manager) {
    		return -1;
    
    	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);
    
    	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);