Skip to content
Snippets Groups Projects
cli.c 72.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			"       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";
    
    		return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
    
    	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;
    
    	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:
    		/* XXX remember to handle the optional "off" */
    		if (a->pos != e->args)
    			return NULL;
    
    		return a->n == 0 ? ast_strdup("all") : ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
    
    	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 ast_channel *c=NULL;
    
    	struct timeval now;
    
    	struct ast_str *write_transpath = ast_str_alloca(256);
    	struct ast_str *read_transpath = ast_str_alloca(256);
    
    	struct ast_str *obuf;/*!< Buffer for variable, CDR variable, and trace output. */
    	struct ast_str *output;/*!< Accumulation buffer for all output. */
    
    	long elapsed_seconds=0;
    
    	char call_identifier_str[AST_CALLID_BUFFER_LENGTH] = "";
    
    	struct ast_party_id effective_connected_id;
    
    #ifdef CHANNEL_TRACE
    	int trace_enabled;
    #endif
    
    
    	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);
    	}
    
    
    	if (!(c = ast_channel_get_by_name(a->argv[3]))) {
    
    		ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
    		return CLI_SUCCESS;
    
    	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;
    	}
    
    
    	if (ast_channel_cdr(c)) {
    		elapsed_seconds = now.tv_sec - ast_channel_cdr(c)->start.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");
    
    	/* Construct the call identifier string based on the status of the channel's call identifier */
    	if ((callid = ast_channel_callid(c))) {
    		ast_callid_strnprint(call_identifier_str, sizeof(call_identifier_str), callid);
    		ast_callid_unref(callid);
    	}
    
    
    	effective_connected_id = ast_channel_connected_effective_id(c);
    
    		" -- 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"
    
    		"  NativeFormats: %s\n"
    		"    WriteFormat: %s\n"
    		"     ReadFormat: %s\n"
    
    		" WriteTranscode: %s %s\n"
    		"  ReadTranscode: %s %s\n"
    
    		"1st File Descriptor: %d\n"
    		"      Frames in: %d%s\n"
    		"     Frames out: %d%s\n"
    		" Time to Hangup: %ld\n"
    		"   Elapsed Time: %s\n"
    
    		" --   PBX   --\n"
    		"        Context: %s\n"
    		"      Extension: %s\n"
    		"       Priority: %d\n"
    
    		"     Call Group: %llu\n"
    		"   Pickup Group: %llu\n"
    
    		"    Blocking in: %s\n"
    		" Call Identifer: %s\n",
    
    		ast_channel_name(c),
    		ast_channel_tech(c)->type,
    		ast_channel_uniqueid(c),
    		ast_channel_linkedid(c),
    
    		S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, "(N/A)"),
    		S_COR(ast_channel_caller(c)->id.name.valid, ast_channel_caller(c)->id.name.str, "(N/A)"),
    		S_COR(ast_channel_connected(c)->id.number.valid, ast_channel_connected(c)->id.number.str, "(N/A)"),
    		S_COR(ast_channel_connected(c)->id.name.valid, ast_channel_connected(c)->id.name.str, "(N/A)"),
    
    		S_COR(effective_connected_id.number.valid, effective_connected_id.number.str, "(N/A)"),
    		S_COR(effective_connected_id.name.valid, effective_connected_id.name.str, "(N/A)"),
    
    		S_OR(ast_channel_dialed(c)->number.str, "(N/A)"),
    
    		ast_channel_language(c),
    
    		ast_state2str(ast_channel_state(c)),
    		ast_channel_state(c),
    		ast_channel_rings(c),
    
    		ast_getformatname_multiple(nf, sizeof(nf), ast_channel_nativeformats(c)),
    
    		ast_getformatname(ast_channel_writeformat(c)),
    		ast_getformatname(ast_channel_readformat(c)),
    
    		ast_channel_writetrans(c) ? "Yes" : "No",
    		ast_translate_path_to_str(ast_channel_writetrans(c), &write_transpath),
    		ast_channel_readtrans(c) ? "Yes" : "No",
    		ast_translate_path_to_str(ast_channel_readtrans(c), &read_transpath),
    
    		ast_channel_fin(c) & ~DEBUGCHAN_FLAG,
    		(ast_channel_fin(c) & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
    		ast_channel_fout(c) & ~DEBUGCHAN_FLAG,
    		(ast_channel_fout(c) & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
    		(long) ast_channel_whentohangup(c)->tv_sec,
    		cdrtime,
    		bridge ? bridge->uniqueid : "(Not bridged)",
    		ast_channel_context(c),
    		ast_channel_exten(c),
    		ast_channel_priority(c),
    		ast_channel_callgroup(c),
    		ast_channel_pickupgroup(c),
    		(ast_channel_appl(c) ? ast_channel_appl(c) : "(N/A)" ),
    
    		(ast_channel_data(c) ? S_OR(ast_channel_data(c), "(Empty)") : "(None)"),
    
    		(ast_test_flag(ast_channel_flags(c), AST_FLAG_BLOCKING) ? ast_channel_blockproc(c) : "(Not Blocking)"),
    		S_OR(call_identifier_str, "(None)"));
    
    	if (pbx_builtin_serialize_variables(c, &obuf)) {
    		ast_str_append(&output, 0, "      Variables:\n%s\n", ast_str_buffer(obuf));
    
    	if (ast_channel_cdr(c) && ast_cdr_serialize_variables(ast_channel_cdr(c), &obuf, '=', '\n', 1)) {
    
    		ast_str_append(&output, 0, "  CDR Variables:\n%s\n", ast_str_buffer(obuf));
    
    #ifdef CHANNEL_TRACE
    	trace_enabled = ast_channel_trace_is_enabled(c);
    
    	ast_str_append(&output, 0, "  Context Trace: %s\n",
    		trace_enabled ? "Enabled" : "Disabled");
    	if (trace_enabled && ast_channel_trace_serialize(c, &obuf)) {
    		ast_str_append(&output, 0, "          Trace:\n%s\n", ast_str_buffer(obuf));
    	}
    
    	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
    {
    
    	char notfound = '\0';
    	char *ret = &notfound; /* so NULL can break the loop */
    
    	struct ast_channel_iterator *iter;
    
    	if (ast_strlen_zero(word)) {
    		iter = ast_channel_iterator_all_new();
    	} else {
    		iter = ast_channel_iterator_by_name_new(word, strlen(word));
    	}
    
    	if (!iter) {
    
    	while (ret == &notfound && (c = ast_channel_iterator_next(iter))) {
    		if (++which > state) {
    			ast_channel_lock(c);
    
    			ret = ast_strdup(ast_channel_name(c));
    
    			ast_channel_unlock(c);
    		}
    		ast_channel_unref(c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	return ret == &notfound ? NULL : ret;
    
    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_verbose, "Set level of debug/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));
    			if (!user_group) {
    				continue;
    			}
    			user_group->uid = (pw ? pw->pw_uid : -1);
    			user_group->gid = (gr ? gr->gr_gid : -1);
    			user_group->perms = ast_calloc(1, sizeof(*user_group->perms));
    			if (!user_group->perms) {
    				ast_free(user_group);
    				continue;
    			}
    		}
    		for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
    			if (ast_strlen_zero(v->value)) {
    				/* we need to check this condition cause it could break security. */
    				ast_log(LOG_WARNING, "Empty permit/deny option in user '%s'\n", cat);
    				continue;
    			}
    			if (!strcasecmp(v->name, "permit")) {
    				perm = ast_calloc(1, sizeof(*perm));
    				if (perm) {
    					perm->permit = 1;
    					perm->command = ast_strdup(v->value);
    				}
    			} else if (!strcasecmp(v->name, "deny")) {
    				perm = ast_calloc(1, sizeof(*perm));
    				if (perm) {
    					perm->permit = 0;
    					perm->command = ast_strdup(v->value);
    				}
    			} else {
    				/* up to now, only 'permit' and 'deny' are possible values. */
    				ast_log(LOG_WARNING, "Unknown '%s' option\n", v->name);
    				continue;
    			}
    			if (perm) {
    				/* Added the permission to the user's list. */
    				AST_LIST_INSERT_TAIL(user_group->perms, perm, list);
    				perm = NULL;
    			}
    		}
    		AST_RWLIST_WRLOCK(&cli_perms);
    		AST_RWLIST_INSERT_TAIL(&cli_perms, user_group, list);
    		AST_RWLIST_UNLOCK(&cli_perms);
    	}
    
    	ast_config_destroy(cfg);
    	ast_mutex_unlock(&permsconfiglock);
    	return 0;
    }
    
    
    static void cli_shutdown(void)
    {
    	ast_cli_unregister_multiple(cli_cli, ARRAY_LEN(cli_cli));
    }
    
    
    /*! \brief initialize the _full_cmd string in * each of the builtins. */
    void ast_builtins_init(void)
    {
    
    	ast_cli_register_multiple(cli_cli, ARRAY_LEN(cli_cli));
    
    	ast_register_atexit(cli_shutdown);
    
    /*!
     * match a word in the CLI entry.
     * returns -1 on mismatch, 0 on match of an optional word,
     * 1 on match of a full word.
    
     *   any_word           match for equal
     *   [foo|bar|baz]      optionally, one of these words
     *   {foo|bar|baz}      exactly, one of these words
     *   %                  any word
    
     */
    static int word_match(const char *cmd, const char *cli_word)
    {
    	int l;
    	char *pos;
    
    	if (ast_strlen_zero(cmd) || ast_strlen_zero(cli_word))
    		return -1;
    	if (!strchr(cli_rsvd, cli_word[0])) /* normal match */
    		return (strcasecmp(cmd, cli_word) == 0) ? 1 : -1;
    	l = strlen(cmd);
    
    	/* wildcard match - will extend in the future */
    	if (l > 0 && cli_word[0] == '%') {
    		return 1;	/* wildcard */
    	}
    
    
    	/* Start a search for the command entered against the cli word in question */
    
    	pos = strcasestr(cli_word, cmd);
    
    	while (pos) {
    
    		/*
    		 *Check if the word matched with is surrounded by reserved characters on both sides
    		 * and isn't at the beginning of the cli_word since that would make it check in a location we shouldn't know about.
    		 * If it is surrounded by reserved chars and isn't at the beginning, it's a match.
    		 */
    		if (pos != cli_word && strchr(cli_rsvd, pos[-1]) && strchr(cli_rsvd, pos[l])) {
    			return 1;	/* valid match */
    		}
    
    		/* Ok, that one didn't match, strcasestr to the next appearance of the command and start over.*/
    		pos = strcasestr(pos + 1, cmd);
    	}
    	/* If no matches were found over the course of the while loop, we hit the end of the string. It's a mismatch. */
    	return -1;
    
    }
    
    /*! \brief if word is a valid prefix for token, returns the pos-th
     * match as a malloced string, or NULL otherwise.
     * Always tell in *actual how many matches we got.
     */
    static char *is_prefix(const char *word, const char *token,
    	int pos, int *actual)
    {
    	int lw;
    	char *s, *t1;
    
    	*actual = 0;
    	if (ast_strlen_zero(token))
    		return NULL;
    	if (ast_strlen_zero(word))
    		word = "";	/* dummy */
    	lw = strlen(word);
    	if (strcspn(word, cli_rsvd) != lw)
    		return NULL;	/* no match if word has reserved chars */
    	if (strchr(cli_rsvd, token[0]) == NULL) {	/* regular match */
    		if (strncasecmp(token, word, lw))	/* no match */
    			return NULL;
    		*actual = 1;
    
    		return (pos != 0) ? NULL : ast_strdup(token);
    
    	}
    	/* now handle regexp match */