Skip to content
Snippets Groups Projects
cli.c 77.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	case CLI_GENERATE:
    		return NULL;
    
    	if (a->argc == e->args) {
    
    		if (!strcasecmp(a->argv[e->args-1],"concise"))
    
    			concise = 1;
    
    		else if (!strcasecmp(a->argv[e->args-1],"verbose"))
    
    			verbose = 1;
    
    		else if (!strcasecmp(a->argv[e->args-1],"count"))
    
    		else
    			return CLI_SHOWUSAGE;
    
    	} else if (a->argc != e->args - 1)
    
    		return CLI_SHOWUSAGE;
    
    	if (!(channels = stasis_cache_dump(ast_channel_cache_by_name(), ast_channel_snapshot_type()))) {
    
    		ast_cli(a->fd, "Failed to retrieve cached channels\n");
    		return CLI_SUCCESS;
    	}
    
    
    			ast_cli(a->fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
    
    			ast_cli(a->fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data",
    
    				"CallerID", "Duration", "Accountcode", "PeerAccount", "BridgeID");
    
    	it_chans = ao2_iterator_init(channels, 0);
    	for (; (msg = ao2_iterator_next(&it_chans)); ao2_ref(msg, -1)) {
    		struct ast_channel_snapshot *cs = stasis_message_data(msg);
    
    			if ((concise || verbose)  && !ast_tvzero(cs->creationtime)) {
    				int duration = (int)(ast_tvdiff_ms(ast_tvnow(), cs->creationtime) / 1000);
    
    				if (verbose) {
    					int durh = duration / 3600;
    					int durm = (duration % 3600) / 60;
    					int durs = duration % 60;
    					snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
    				} else {
    					snprintf(durbuf, sizeof(durbuf), "%d", duration);
    
    				ast_cli(a->fd, CONCISE_FORMAT_STRING, cs->name, cs->context, cs->exten, cs->priority, ast_state2str(cs->state),
    					S_OR(cs->appl, "(None)"),
    					cs->data,
    					cs->caller_number,
    					cs->accountcode,
    					cs->peeraccount,
    					cs->amaflags,
    
    				ast_cli(a->fd, VERBOSE_FORMAT_STRING, cs->name, cs->context, cs->exten, cs->priority, ast_state2str(cs->state),
    					S_OR(cs->appl, "(None)"),
    					S_OR(cs->data, "(Empty)"),
    					cs->caller_number,
    
    					cs->accountcode,
    					cs->peeraccount,
    					cs->bridgeid);
    
    				char locbuf[40] = "(None)";
    				char appdata[40] = "(None)";
    
    				if (!ast_strlen_zero(cs->context) && !ast_strlen_zero(cs->exten)) {
    
    					snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", cs->exten, cs->context, cs->priority);
    
    					snprintf(appdata, sizeof(appdata), "%s(%s)", cs->appl, S_OR(cs->data, ""));
    
    				ast_cli(a->fd, FORMAT_STRING, cs->name, locbuf, ast_state2str(cs->state), appdata);
    
    		ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
    
    		if (ast_option_maxcalls)
    
    			ast_cli(a->fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
    
    				ast_active_calls(), ast_option_maxcalls, ESS(ast_active_calls()),
    				((double)ast_active_calls() / (double)ast_option_maxcalls) * 100.0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else
    
    			ast_cli(a->fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
    
    		ast_cli(a->fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
    
    	return CLI_SUCCESS;
    
    #undef FORMAT_STRING
    #undef FORMAT_STRING2
    #undef CONCISE_FORMAT_STRING
    #undef VERBOSE_FORMAT_STRING
    #undef VERBOSE_FORMAT_STRING2
    
    static char *handle_softhangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct ast_channel *c = NULL;
    	static const char * const completions[] = { "all", NULL };
    	char *complete;
    
    		e->command = "channel request hangup";
    
    			"Usage: channel request hangup <channel>|<all>\n"
    
    			"       Request that a channel be hung up. The hangup takes effect\n"
    
    			"       the next time the driver reads or writes from the channel.\n"
    			"       If 'all' is specified instead of a channel name, all channels\n"
    			"       will see the hangup request.\n";
    
    		if (a->pos != e->args) {
    			return NULL;
    		}
    		complete = ast_cli_complete(a->word, completions, a->n);
    		if (!complete) {
    			complete = ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
    		}
    		return complete;
    
    	if (!strcasecmp(a->argv[3], "all")) {
    		struct ast_channel_iterator *iter = NULL;
    
    		if (!(iter = ast_channel_iterator_all_new())) {
    
    			return CLI_FAILURE;
    		}
    		for (; iter && (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
    			ast_channel_lock(c);
    
    			ast_cli(a->fd, "Requested Hangup on channel '%s'\n", ast_channel_name(c));
    
    			ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
    			ast_channel_unlock(c);
    		}
    		ast_channel_iterator_destroy(iter);
    	} else if ((c = ast_channel_get_by_name(a->argv[3]))) {
    
    		ast_cli(a->fd, "Requested Hangup on channel '%s'\n", ast_channel_name(c));
    
    		ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
    
    		ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
    
    /*! \brief handles CLI command 'cli show permissions' */
    static char *handle_cli_show_permissions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    	struct usergroup_cli_perm *cp;
    	struct cli_perm *perm;
    	struct passwd *pw = NULL;
    	struct group *gr = NULL;
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "cli show permissions";
    		e->usage =
    			"Usage: cli show permissions\n"
    			"       Shows CLI configured permissions.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	AST_RWLIST_RDLOCK(&cli_perms);
    	AST_LIST_TRAVERSE(&cli_perms, cp, list) {
    		if (cp->uid >= 0) {
    			pw = getpwuid(cp->uid);
    			if (pw) {
    				ast_cli(a->fd, "user: %s [uid=%d]\n", pw->pw_name, cp->uid);
    			}
    		} else {
    			gr = getgrgid(cp->gid);
    			if (gr) {
    				ast_cli(a->fd, "group: %s [gid=%d]\n", gr->gr_name, cp->gid);
    			}
    		}
    		ast_cli(a->fd, "Permissions:\n");
    		if (cp->perms) {
    			AST_LIST_TRAVERSE(cp->perms, perm, list) {
    				ast_cli(a->fd, "\t%s -> %s\n", perm->permit ? "permit" : "deny", perm->command);
    			}
    		}
    		ast_cli(a->fd, "\n");
    	}
    	AST_RWLIST_UNLOCK(&cli_perms);
    
    	return CLI_SUCCESS;
    }
    
    /*! \brief handles CLI command 'cli reload permissions' */
    static char *handle_cli_reload_permissions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "cli reload permissions";
    		e->usage =
    			"Usage: cli reload permissions\n"
    			"       Reload the 'cli_permissions.conf' file.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	ast_cli_perms_init(1);
    
    	return CLI_SUCCESS;
    }
    
    /*! \brief handles CLI command 'cli check permissions' */
    static char *handle_cli_check_permissions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    	struct passwd *pw = NULL;
    	struct group *gr;
    	int gid = -1, uid = -1;
    	char command[AST_MAX_ARGS] = "";
    	struct ast_cli_entry *ce = NULL;
    	int found = 0;
    	char *group, *tmp;
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "cli check permissions";
    		e->usage =
    			"Usage: cli check permissions {<username>|@<groupname>|<username>@<groupname>} [<command>]\n"
    			"       Check permissions config for a user@group or list the allowed commands for the specified user.\n"
    			"       The username or the groupname may be omitted.\n";
    		return NULL;
    	case CLI_GENERATE:
    		if (a->pos >= 4) {
    			return ast_cli_generator(a->line + strlen("cli check permissions") + strlen(a->argv[3]) + 1, a->word, a->n);
    		}
    		return NULL;
    	}
    
    	if (a->argc < 4) {
    		return CLI_SHOWUSAGE;
    	}
    
    	tmp = ast_strdupa(a->argv[3]);
    	group = strchr(tmp, '@');
    	if (group) {
    		gr = getgrnam(&group[1]);
    		if (!gr) {
    			ast_cli(a->fd, "Unknown group '%s'\n", &group[1]);
    			return CLI_FAILURE;
    		}
    		group[0] = '\0';
    		gid = gr->gr_gid;
    	}
    
    	if (!group && ast_strlen_zero(tmp)) {
    		ast_cli(a->fd, "You didn't supply a username\n");
    	} else if (!ast_strlen_zero(tmp) && !(pw = getpwnam(tmp))) {
    		ast_cli(a->fd, "Unknown user '%s'\n", tmp);
    		return CLI_FAILURE;
    	} else if (pw) {
    		uid = pw->pw_uid;
    	}
    
    	if (a->argc == 4) {
    		while ((ce = cli_next(ce))) {
    			/* Hide commands that start with '_' */
    			if (ce->_full_cmd[0] == '_') {
    				continue;
    			}
    			if (cli_has_permissions(uid, gid, ce->_full_cmd)) {
    				ast_cli(a->fd, "%30.30s %s\n", ce->_full_cmd, S_OR(ce->summary, "<no description available>"));
    				found++;
    			}
    		}
    		if (!found) {
    			ast_cli(a->fd, "You are not allowed to run any command on Asterisk\n");
    		}
    	} else {
    		ast_join(command, sizeof(command), a->argv + 4);
    		ast_cli(a->fd, "%s '%s%s%s' is %s to run command: '%s'\n", uid >= 0 ? "User" : "Group", tmp,
    			group && uid >= 0 ? "@" : "",
    			group ? &group[1] : "",
    			cli_has_permissions(uid, gid, command) ? "allowed" : "not allowed", command);
    	}
    
    	return CLI_SUCCESS;
    }
    
    
    static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
    
    static char *handle_commandmatchesarray(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	char *buf, *obuf;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int len = 0;
    	char **matches;
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "_command matchesarray";
    
    			"Usage: _command matchesarray \"<line>\" text \n"
    			"       This function is used internally to help with command completion and should.\n"
    			"       never be called by the user directly.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	buf[len] = '\0';
    
    	matches = ast_cli_completion_matches(a->argv[2], a->argv[3]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (matches) {
    		for (x=0; matches[x]; x++) {
    
    			matchlen = strlen(matches[x]) + 1;
    			if (len + matchlen >= buflen) {
    				buflen += matchlen * 3;
    				obuf = buf;
    
    				if (!(buf = ast_realloc(obuf, buflen)))
    
    					/* Memory allocation failure...  Just free old buffer and be done */
    
    			if (buf)
    				len += sprintf( buf + len, "%s ", matches[x]);
    
    			matches[x] = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (buf) {
    
    		ast_cli(a->fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else
    
    static char *handle_commandnummatches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int matches = 0;
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "_command nummatches";
    
    			"Usage: _command nummatches \"<line>\" text \n"
    			"       This function is used internally to help with command completion and should.\n"
    			"       never be called by the user directly.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	if (a->argc != 4)
    		return CLI_SHOWUSAGE;
    
    	matches = ast_cli_generatornummatches(a->argv[2], a->argv[3]);
    
    static char *handle_commandcomplete(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	char *buf;
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "_command complete";
    
    			"Usage: _command complete \"<line>\" text state\n"
    			"       This function is used internally to help with command completion and should.\n"
    			"       never be called by the user directly.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    	if (a->argc != 5)
    		return CLI_SHOWUSAGE;
    	buf = __ast_cli_generator(a->argv[2], a->argv[3], atoi(a->argv[4]), 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (buf) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else
    
    		ast_cli(a->fd, "NULL\n");
    	return CLI_SUCCESS;
    
    struct channel_set_debug_args {
    	int fd;
    	int is_off;
    };
    
    static int channel_set_debug(void *obj, void *arg, void *data, int flags)
    {
    	struct ast_channel *chan = obj;
    	struct channel_set_debug_args *args = data;
    
    	ast_channel_lock(chan);
    
    
    	if (!(ast_channel_fin(chan) & DEBUGCHAN_FLAG) || !(ast_channel_fout(chan) & DEBUGCHAN_FLAG)) {
    
    			ast_channel_fin_set(chan, ast_channel_fin(chan) & ~DEBUGCHAN_FLAG);
    			ast_channel_fout_set(chan, ast_channel_fout(chan) & ~DEBUGCHAN_FLAG);
    
    			ast_channel_fin_set(chan, ast_channel_fin(chan) | DEBUGCHAN_FLAG);
    			ast_channel_fout_set(chan, ast_channel_fout(chan) | DEBUGCHAN_FLAG);
    
    		}
    		ast_cli(args->fd, "Debugging %s on channel %s\n", args->is_off ? "disabled" : "enabled",
    
    static char *handle_core_set_debug_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    {
    	struct ast_channel *c = NULL;
    
    	static const char * const completions_all[] = { "all", NULL };
    	static const char * const completions_off[] = { "off", NULL };
    
    	struct channel_set_debug_args args = {
    		.fd = a->fd,
    	};
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "core set debug channel";
    		e->usage =
    			"Usage: core set debug channel <all|channel> [off]\n"
    			"       Enables/disables debugging on all or on a specific channel.\n";
    		return NULL;
    	case CLI_GENERATE:
    
    		if (a->pos == 4) {
    			char *complete = ast_cli_complete(a->word, completions_all, a->n);
    			if (!complete) {
    				complete = ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
    			}
    			return complete;
    		} else if (a->pos == 5) {
    			return ast_cli_complete(a->word, completions_off, a->n);
    		}
    
    	if (cmd == (CLI_HANDLER + 1000)) {
    		/* called from handle_nodebugchan_deprecated */
    		args.is_off = 1;
    	} else if (a->argc == e->args + 2) {
    		/* 'core set debug channel {all|chan_id}' */
    
    		if (!strcasecmp(a->argv[e->args + 1], "off"))
    
    	} else if (a->argc != e->args + 1) {
    
    	if (!strcasecmp("all", a->argv[e->args])) {
    		if (args.is_off) {
    
    			global_fin &= ~DEBUGCHAN_FLAG;
    			global_fout &= ~DEBUGCHAN_FLAG;
    		} else {
    			global_fin |= DEBUGCHAN_FLAG;
    			global_fout |= DEBUGCHAN_FLAG;
    		}
    
    		ast_channel_callback(channel_set_debug, NULL, &args, OBJ_NODATA | OBJ_MULTIPLE);
    
    		if ((c = ast_channel_get_by_name(a->argv[e->args]))) {
    			channel_set_debug(c, NULL, &args, 0);
    			ast_channel_unref(c);
    		} else {
    
    			ast_cli(a->fd, "No such channel %s\n", a->argv[e->args]);
    
    
    	ast_cli(a->fd, "Debugging on new channels is %s\n", args.is_off ? "disabled" : "enabled");
    
    
    static char *handle_nodebugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    		e->command = "no debug channel";
    
    		return NULL;
    	case CLI_HANDLER:
    		/* exit out of switch statement */
    		break;
    	default:
    		return NULL;
    	}
    
    	if (a->argc != e->args + 1)
    		return CLI_SHOWUSAGE;
    
    	/* add a 'magic' value to the CLI_HANDLER command so that
    	 * handle_core_set_debug_channel() will act as if 'off'
    	 * had been specified as part of the command
    	 */
    	res = handle_core_set_debug_channel(e, CLI_HANDLER + 1000, a);
    
    
    static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct timeval now;
    
    	struct ast_str *obuf;/*!< Buffer for CDR variables. */
    
    	struct ast_str *output;/*!< Accumulation buffer for all output. */
    
    	long elapsed_seconds=0;
    
    	struct ast_str *write_transpath = ast_str_alloca(256);
    	struct ast_str *read_transpath = ast_str_alloca(256);
    
    	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
    
    	int stream_num;
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "core show channel";
    
    			"Usage: core show channel <channel>\n"
    			"       Shows lots of information about the specified channel.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
    	}
    
    	obuf = ast_str_thread_get(&ast_str_thread_global_buf, 16);
    	if (!obuf) {
    		return CLI_FAILURE;
    	}
    
    	output = ast_str_create(8192);
    	if (!output) {
    		return CLI_FAILURE;
    	}
    
    
    	chan = ast_channel_get_by_name(a->argv[3]);
    	if (!chan) {
    		ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
    		return CLI_SUCCESS;
    	}
    
    	now = ast_tvnow();
    	ast_channel_lock(chan);
    
    	if (!ast_tvzero(ast_channel_creationtime(chan))) {
    		elapsed_seconds = now.tv_sec - ast_channel_creationtime(chan).tv_sec;
    
    		hour = elapsed_seconds / 3600;
    		min = (elapsed_seconds % 3600) / 60;
    		sec = elapsed_seconds % 60;
    		snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
    
    		strcpy(cdrtime, "N/A");
    
    	ast_translate_path_to_str(ast_channel_writetrans(chan), &write_transpath);
    	ast_translate_path_to_str(ast_channel_readtrans(chan), &read_transpath);
    
    	bridge = ast_channel_get_bridge(chan);
    
    	callid = ast_channel_callid(chan);
    	if (callid) {
    		ast_callid_strnprint(callid_buf, sizeof(callid_buf), callid);
    	}
    
    
    		" -- General --\n"
    		"           Name: %s\n"
    		"           Type: %s\n"
    		"       UniqueID: %s\n"
    
    		"Connected Line ID: %s\n"
    		"Connected Line ID Name: %s\n"
    
    		"Eff. Connected Line ID: %s\n"
    		"Eff. Connected Line ID Name: %s\n"
    
    		"          State: %s (%u)\n"
    
    		"  NativeFormats: %s\n"
    		"    WriteFormat: %s\n"
    		"     ReadFormat: %s\n"
    
    		" WriteTranscode: %s %s\n"
    		"  ReadTranscode: %s %s\n"
    
    		" --   PBX   --\n"
    		"        Context: %s\n"
    		"      Extension: %s\n"
    		"       Priority: %d\n"
    
    		"     Call Group: %llu\n"
    		"   Pickup Group: %llu\n"
    
    		ast_channel_name(chan),
    		ast_channel_tech(chan)->type,
    		ast_channel_uniqueid(chan),
    		ast_channel_linkedid(chan),
    		S_COR(ast_channel_caller(chan)->id.number.valid,
    		      ast_channel_caller(chan)->id.number.str, "(N/A)"),
    		S_COR(ast_channel_caller(chan)->id.name.valid,
    		      ast_channel_caller(chan)->id.name.str, "(N/A)"),
    		S_COR(ast_channel_connected(chan)->id.number.valid,
    		      ast_channel_connected(chan)->id.number.str, "(N/A)"),
    		S_COR(ast_channel_connected(chan)->id.name.valid,
    		      ast_channel_connected(chan)->id.name.str, "(N/A)"),
    		S_COR(ast_channel_connected_effective_id(chan).number.valid,
    		      ast_channel_connected_effective_id(chan).number.str, "(N/A)"),
    		S_COR(ast_channel_connected_effective_id(chan).name.valid,
    		      ast_channel_connected_effective_id(chan).name.str, "(N/A)"),
    		S_OR(ast_channel_dialed(chan)->number.str, "(N/A)"),
    		ast_channel_language(chan),
    		ast_state2str(ast_channel_state(chan)),
    		ast_channel_state(chan),
    
    		ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf),
    		ast_format_get_name(ast_channel_writeformat(chan)),
    		ast_format_get_name(ast_channel_readformat(chan)),
    
    		ast_str_strlen(write_transpath) ? "Yes" : "No",
    		ast_str_buffer(write_transpath),
    		ast_str_strlen(read_transpath) ? "Yes" : "No",
    		ast_str_buffer(read_transpath),
    
    		(long)ast_channel_whentohangup(chan)->tv_sec,
    
    		bridge ? bridge->uniqueid : "(Not bridged)",
    		ast_channel_context(chan),
    		ast_channel_exten(chan),
    		ast_channel_priority(chan),
    		ast_channel_callgroup(chan),
    		ast_channel_pickupgroup(chan),
    		S_OR(ast_channel_appl(chan), "(N/A)"),
    		S_OR(ast_channel_data(chan), "(Empty)"),
    		S_OR(callid_buf, "(None)")
    		);
    
    	ast_str_append(&output, 0, "      Variables:\n");
    
    
    	AST_LIST_TRAVERSE(ast_channel_varshead(chan), var, entries) {
    
    		ast_str_append(&output, 0, "%s=%s\n", ast_var_name(var), ast_var_value(var));
    
    	if (!(ast_channel_tech(chan)->properties & AST_CHAN_TP_INTERNAL)
    		&& ast_cdr_serialize_variables(ast_channel_name(chan), &obuf, '=', '\n')) {
    
    		ast_str_append(&output, 0, "  CDR Variables:\n%s\n", ast_str_buffer(obuf));
    	}
    
    	ast_str_append(&output, 0, " -- Streams --\n");
    	for (stream_num = 0; stream_num < ast_stream_topology_get_count(ast_channel_get_stream_topology(chan)); stream_num++) {
    		struct ast_stream *stream = ast_stream_topology_get_stream(ast_channel_get_stream_topology(chan), stream_num);
    
    		ast_str_append(&output, 0,
    			"Name: %s\n"
    			"    Type: %s\n"
    			"    State: %s\n"
    			"    Formats: %s\n",
    			ast_stream_get_name(stream),
    			ast_codec_media_type2str(ast_stream_get_type(stream)),
    			ast_stream_state2str(ast_stream_get_state(stream)),
    			ast_format_cap_get_names(ast_stream_get_formats(stream), &codec_buf)
    			);
    	}
    
    
    	ast_cli(a->fd, "%s", ast_str_buffer(output));
    	ast_free(output);
    
    /*
     * helper function to generate CLI matches from a fixed set of values.
     * A NULL word is acceptable.
     */
    
    char *ast_cli_complete(const char *word, const char * const choices[], int state)
    
    	int i, which = 0, len;
    	len = ast_strlen_zero(word) ? 0 : strlen(word);
    
    	for (i = 0; choices[i]; i++) {
    		if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
    			return ast_strdup(choices[i]);
    
    char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	int wordlen = strlen(word), which = 0;
    	RAII_VAR(struct ao2_container *, cached_channels, NULL, ao2_cleanup);
    	char *ret = NULL;
    	struct ao2_iterator iter;
    	struct stasis_message *msg;
    
    	if (!(cached_channels = stasis_cache_dump(ast_channel_cache(), ast_channel_snapshot_type()))) {
    
    	iter = ao2_iterator_init(cached_channels, 0);
    	for (; (msg = ao2_iterator_next(&iter)); ao2_ref(msg, -1)) {
    		struct ast_channel_snapshot *snapshot = stasis_message_data(msg);
    
    		if (!strncasecmp(word, snapshot->name, wordlen) && (++which > state)) {
    			ret = ast_strdup(snapshot->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    static char *group_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	struct ast_group_info *gi = NULL;
    
    	int numchans = 0;
    	regex_t regexbuf;
    	int havepattern = 0;
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "group show channels";
    
    			"Usage: group show channels [pattern]\n"
    			"       Lists all currently active channels with channel group(s) specified.\n"
    			"       Optional regular expression pattern is matched to group names for each\n"
    			"       channel.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	if (a->argc < 3 || a->argc > 4)
    		return CLI_SHOWUSAGE;
    
    	if (a->argc == 4) {
    		if (regcomp(&regexbuf, a->argv[3], REG_EXTENDED | REG_NOSUB))
    			return CLI_SHOWUSAGE;
    
    	ast_cli(a->fd, FORMAT_STRING, "Channel", "Group", "Category");
    
    	ast_app_group_list_rdlock();
    
    	gi = ast_app_group_list_head();
    	while (gi) {
    		if (!havepattern || !regexec(&regexbuf, gi->group, 0, NULL, 0)) {
    
    			ast_cli(a->fd, FORMAT_STRING, ast_channel_name(gi->chan), gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
    
    		gi = AST_LIST_NEXT(gi, group_list);
    
    	ast_app_group_list_unlock();
    
    	ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
    	return CLI_SUCCESS;
    
    static char *handle_cli_wait_fullybooted(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "core waitfullybooted";
    		e->usage =
    			"Usage: core waitfullybooted\n"
    			"	Wait until Asterisk has fully booted.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	while (!ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
    		usleep(100);
    	}
    
    	ast_cli(a->fd, "Asterisk has fully booted.\n");
    
    	return CLI_SUCCESS;
    }
    
    
    static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    
    static struct ast_cli_entry cli_cli[] = {
    
    	/* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
    
    	AST_CLI_DEFINE(handle_commandcomplete, "Command complete"),
    	AST_CLI_DEFINE(handle_commandnummatches, "Returns number of command matches"),
    	AST_CLI_DEFINE(handle_commandmatchesarray, "Returns command matches array"),
    
    	AST_CLI_DEFINE(handle_nodebugchan_deprecated, "Disable debugging on channel(s)"),
    
    	AST_CLI_DEFINE(handle_chanlist, "Display information on channels"),
    
    	AST_CLI_DEFINE(handle_showcalls, "Display information on calls"),
    
    
    	AST_CLI_DEFINE(handle_showchan, "Display information on a specific channel"),
    
    	AST_CLI_DEFINE(handle_core_set_debug_channel, "Enable/disable debugging on a channel"),
    
    	AST_CLI_DEFINE(handle_debug, "Set level of debug chattiness"),
    	AST_CLI_DEFINE(handle_verbose, "Set level of verbose chattiness"),
    
    	AST_CLI_DEFINE(group_show_channels, "Display active channels with group(s)"),
    
    	AST_CLI_DEFINE(handle_help, "Display help list, or specific help on a command"),
    
    	AST_CLI_DEFINE(handle_logger_mute, "Toggle logging output to a console"),
    
    	AST_CLI_DEFINE(handle_modlist, "List modules and info"),
    
    	AST_CLI_DEFINE(handle_load, "Load a module by name"),
    
    	AST_CLI_DEFINE(handle_reload, "Reload configuration for a module"),
    
    	AST_CLI_DEFINE(handle_core_reload, "Global reload"),
    
    	AST_CLI_DEFINE(handle_unload, "Unload a module by name"),
    
    	AST_CLI_DEFINE(handle_showuptime, "Show uptime information"),
    
    	AST_CLI_DEFINE(handle_softhangup, "Request a hangup on a given channel"),
    
    
    	AST_CLI_DEFINE(handle_cli_reload_permissions, "Reload CLI permissions config"),
    
    	AST_CLI_DEFINE(handle_cli_show_permissions, "Show CLI permissions"),
    
    	AST_CLI_DEFINE(handle_cli_check_permissions, "Try a permissions config for a user"),
    
    
    	AST_CLI_DEFINE(handle_cli_wait_fullybooted, "Wait for Asterisk to be fully booted"),
    
    /*!
     * Some regexp characters in cli arguments are reserved and used as separators.
     */
    
    static const char cli_rsvd[] = "[]{}|*%";
    
    
    /*!
     * initialize the _full_cmd string and related parameters,
     * return 0 on success, -1 on error.
     */
    static int set_full_cmd(struct ast_cli_entry *e)
    {
    	int i;
    	char buf[80];
    
    	ast_join(buf, sizeof(buf), e->cmda);
    
    	e->_full_cmd = ast_strdup(buf);
    
    	if (!e->_full_cmd) {
    		ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
    		return -1;
    	}
    	e->cmdlen = strcspn(e->_full_cmd, cli_rsvd);
    	for (i = 0; e->cmda[i]; i++)
    		;
    	e->args = i;
    	return 0;
    }
    
    
    /*! \brief cleanup (free) cli_perms linkedlist. */
    
    static void destroy_user_perms(void)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct cli_perm *perm;
    	struct usergroup_cli_perm *user_perm;
    
    	AST_RWLIST_WRLOCK(&cli_perms);
    	while ((user_perm = AST_LIST_REMOVE_HEAD(&cli_perms, list))) {
    		while ((perm = AST_LIST_REMOVE_HEAD(user_perm->perms, list))) {
    			ast_free(perm->command);
    			ast_free(perm);
    		}
    		ast_free(user_perm);
    	}
    	AST_RWLIST_UNLOCK(&cli_perms);
    
    int ast_cli_perms_init(int reload)
    {
    
    	struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
    	struct ast_config *cfg;
    	char *cat = NULL;
    	struct ast_variable *v;
    	struct usergroup_cli_perm *user_group, *cp_entry;
    	struct cli_perm *perm = NULL;
    	struct passwd *pw;
    	struct group *gr;
    
    	if (ast_mutex_trylock(&permsconfiglock)) {
    		ast_log(LOG_NOTICE, "You must wait until last 'cli reload permissions' command finish\n");
    		return 1;
    	}
    
    	cfg = ast_config_load2(perms_config, "" /* core, can't reload */, config_flags);
    	if (!cfg) {
    		ast_mutex_unlock(&permsconfiglock);
    		return 1;
    	} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
    		ast_mutex_unlock(&permsconfiglock);
    		return 0;
    
    
    	/* free current structures. */
    	destroy_user_perms();
    
    	while ((cat = ast_category_browse(cfg, cat))) {
    		if (!strcasecmp(cat, "general")) {
    			/* General options */
    			for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
    				if (!strcasecmp(v->name, "default_perm")) {
    					cli_default_perm = (!strcasecmp(v->value, "permit")) ? 1: 0;
    				}
    			}
    			continue;
    		}
    
    		/* users or groups */
    		gr = NULL, pw = NULL;
    		if (cat[0] == '@') {
    			/* This is a group */
    			gr = getgrnam(&cat[1]);
    			if (!gr) {
    				ast_log (LOG_WARNING, "Unknown group '%s'\n", &cat[1]);
    				continue;
    			}
    		} else {
    			/* This is a user */
    			pw = getpwnam(cat);
    			if (!pw) {
    				ast_log (LOG_WARNING, "Unknown user '%s'\n", cat);
    				continue;
    			}
    		}
    		user_group = NULL;
    		/* Check for duplicates */
    		AST_RWLIST_WRLOCK(&cli_perms);
    		AST_LIST_TRAVERSE(&cli_perms, cp_entry, list) {
    			if ((pw && cp_entry->uid == pw->pw_uid) || (gr && cp_entry->gid == gr->gr_gid)) {
    
    				/* if it is duplicated, just added this new settings, to
    
    				the current list. */
    				user_group = cp_entry;
    				break;
    			}
    		}
    		AST_RWLIST_UNLOCK(&cli_perms);
    
    		if (!user_group) {
    			/* alloc space for the new user config. */
    			user_group = ast_calloc(1, sizeof(*user_group));