Skip to content
Snippets Groups Projects
cli.c 77.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
    
     * Asterisk -- An open source telephony toolkit.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Copyright (C) 1999 - 2006, Digium, Inc.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Mark Spencer <markster@digium.com>
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * See http://www.asterisk.org for more information about
     * the Asterisk project. Please do not directly contact
     * any of the maintainers of this project for assistance;
     * the project provides a web site, mailing lists and IRC
     * channels for your use.
     *
    
    Mark Spencer's avatar
    Mark Spencer committed
     * This program is free software, distributed under the terms of
    
     * the GNU General Public License Version 2. See the LICENSE file
     * at the top of the source tree.
     */
    
    
     * \brief Standard Command Line Interface
    
     * \author Mark Spencer <markster@digium.com>
    
    /*! \li \ref cli.c uses the configuration file \ref cli_permissions.conf
     * \addtogroup configuration_file Configuration Files
     */
    
    /*!
     * \page cli_permissions.conf cli_permissions.conf
     * \verbinclude cli_permissions.conf.sample
     */
    
    
    /*** MODULEINFO
    	<support_level>core</support_level>
     ***/
    
    
    #include "asterisk/_private.h"
    
    #include "asterisk/paths.h"	/* use ast_config_AST_MODULE_DIR */
    
    #include <signal.h>
    #include <ctype.h>
    
    #include <pwd.h>
    #include <grp.h>
    
    #include "asterisk/linkedlists.h"
    
    #include "asterisk/module.h"
    #include "asterisk/pbx.h"
    #include "asterisk/channel.h"
    #include "asterisk/utils.h"
    
    #include "asterisk/threadstorage.h"
    
    #include "asterisk/translate.h"
    
    #include "asterisk/bridge.h"
    
    #include "asterisk/stasis_channels.h"
    
    #include "asterisk/stasis_bridges.h"
    
    #include "asterisk/vector.h"
    
    #include "asterisk/stream.h"
    
    /*!
     * \brief List of restrictions per user.
     */
    struct cli_perm {
    	unsigned int permit:1;				/*!< 1=Permit 0=Deny */
    	char *command;				/*!< Command name (to apply restrictions) */
    	AST_LIST_ENTRY(cli_perm) list;
    };
    
    AST_LIST_HEAD_NOLOCK(cli_perm_head, cli_perm);
    
    /*! \brief list of users to apply restrictions. */
    struct usergroup_cli_perm {
    	int uid;				/*!< User ID (-1 disabled) */
    	int gid;				/*!< Group ID (-1 disabled) */
    	struct cli_perm_head *perms;		/*!< List of permissions. */
    	AST_LIST_ENTRY(usergroup_cli_perm) list;/*!< List mechanics */
    };
    /*! \brief CLI permissions config file. */
    static const char perms_config[] = "cli_permissions.conf";
    /*! \brief Default permissions value 1=Permit 0=Deny */
    static int cli_default_perm = 1;
    
    /*! \brief mutex used to prevent a user from running the 'cli reload permissions' command while
     * it is already running. */
    AST_MUTEX_DEFINE_STATIC(permsconfiglock);
    /*! \brief  List of users and permissions. */
    
    static AST_RWLIST_HEAD_STATIC(cli_perms, usergroup_cli_perm);
    
    Russell Bryant's avatar
    Russell Bryant committed
    /*!
    
     * \brief map a debug or verbose level to a module name
    
    Russell Bryant's avatar
    Russell Bryant committed
     */
    
    Russell Bryant's avatar
    Russell Bryant committed
    	unsigned int level;
    
    	AST_RWLIST_ENTRY(module_level) entry;
    	char module[0];
    
    AST_RWLIST_HEAD(module_level_list, module_level);
    
    /*! list of module names and their debug levels */
    
    static struct module_level_list debug_modules = AST_RWLIST_HEAD_INIT_VALUE;
    
    AST_RWLOCK_DEFINE_STATIC(shutdown_commands_lock);
    static AST_VECTOR(, struct ast_cli_entry *) shutdown_commands;
    
    
    /*! \brief Initial buffer size for resulting strings in ast_cli() */
    
    void ast_cli(int fd, const char *fmt, ...)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct ast_str *buf;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	va_list ap;
    
    	if (!(buf = ast_str_thread_get(&ast_cli_buf, AST_CLI_INITLEN)))
    
    Mark Spencer's avatar
    Mark Spencer committed
    	va_start(ap, fmt);
    
    	res = ast_str_set_va(&buf, 0, fmt, ap);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	va_end(ap);
    
    	if (res != AST_DYNSTR_BUILD_FAILED) {
    		ast_carefulwrite(fd, ast_str_buffer(buf), ast_str_strlen(buf), 100);
    	}
    
    unsigned int ast_debug_get_by_module(const char *module)
    
    Russell Bryant's avatar
    Russell Bryant committed
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	unsigned int res = 0;
    
    
    	AST_RWLIST_RDLOCK(&debug_modules);
    	AST_LIST_TRAVERSE(&debug_modules, ml, entry) {
    		if (!strcasecmp(ml->module, module)) {
    			res = ml->level;
    
    Russell Bryant's avatar
    Russell Bryant committed
    			break;
    		}
    	}
    
    	AST_RWLIST_UNLOCK(&debug_modules);
    
    unsigned int ast_verbose_get_by_module(const char *module)
    
    Russell Bryant's avatar
    Russell Bryant committed
    {
    
    /*! \internal
     *  \brief Check if the user with 'uid' and 'gid' is allow to execute 'command',
     *	   if command starts with '_' then not check permissions, just permit
     *	   to run the 'command'.
     *	   if uid == -1 or gid == -1 do not check permissions.
     *	   if uid == -2 and gid == -2 is because rasterisk client didn't send
     *	   the credentials, so the cli_default_perm will be applied.
     *  \param uid User ID.
     *  \param gid Group ID.
     *  \param command Command name to check permissions.
     *  \retval 1 if has permission
     *  \retval 0 if it is not allowed.
     */
    static int cli_has_permissions(int uid, int gid, const char *command)
    {
    	struct usergroup_cli_perm *user_perm;
    	struct cli_perm *perm;
    	/* set to the default permissions general option. */
    	int isallowg = cli_default_perm, isallowu = -1, ispattern;
    	regex_t regexbuf;
    
    	/* if uid == -1 or gid == -1 do not check permissions.
    	   if uid == -2 and gid == -2 is because rasterisk client didn't send
    	   the credentials, so the cli_default_perm will be applied. */
    	if ((uid == CLI_NO_PERMS && gid == CLI_NO_PERMS) || command[0] == '_') {
    		return 1;
    	}
    
    	if (gid < 0 && uid < 0) {
    		return cli_default_perm;
    	}
    
    	AST_RWLIST_RDLOCK(&cli_perms);
    	AST_LIST_TRAVERSE(&cli_perms, user_perm, list) {
    		if (user_perm->gid != gid && user_perm->uid != uid) {
    			continue;
    		}
    		AST_LIST_TRAVERSE(user_perm->perms, perm, list) {
    			if (strcasecmp(perm->command, "all") && strncasecmp(perm->command, command, strlen(perm->command))) {
    				/* if the perm->command is a pattern, check it against command. */
    				ispattern = !regcomp(&regexbuf, perm->command, REG_EXTENDED | REG_NOSUB | REG_ICASE);
    				if (ispattern && regexec(&regexbuf, command, 0, NULL, 0)) {
    					regfree(&regexbuf);
    					continue;
    				}
    				if (!ispattern) {
    					continue;
    				}
    				regfree(&regexbuf);
    			}
    			if (user_perm->uid == uid) {
    				/* this is a user definition. */
    				isallowu = perm->permit;
    			} else {
    				/* otherwise is a group definition. */
    				isallowg = perm->permit;
    			}
    		}
    	}
    	AST_RWLIST_UNLOCK(&cli_perms);
    	if (isallowu > -1) {
    		/* user definition override group definition. */
    		isallowg = isallowu;
    	}
    
    	return isallowg;
    }
    
    
    static AST_RWLIST_HEAD_STATIC(helpers, ast_cli_entry);
    
    static char *complete_fn(const char *word, int state)
    {
    
    	char filename[PATH_MAX];
    
    
    	if (word[0] == '/')
    		ast_copy_string(filename, word, sizeof(filename));
    	else
    		snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
    
    
    	c = d = filename_completion_function(filename, state);
    
    	if (c && word[0] != '/')
    		c += (strlen(ast_config_AST_MODULE_DIR) + 1);
    
    	if (c)
    		c = ast_strdup(c);
    
    }
    
    static char *handle_load(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "module load";
    		e->usage =
    			"Usage: module load <module name>\n"
    			"       Loads the specified module into Asterisk.\n";
    		return NULL;
    
    	case CLI_GENERATE:
    
    			return NULL;
    		return complete_fn(a->word, a->n);
    
    	if (a->argc != e->args + 1)
    		return CLI_SHOWUSAGE;
    	if (ast_load_resource(a->argv[e->args])) {
    		ast_cli(a->fd, "Unable to load module %s\n", a->argv[e->args]);
    		return CLI_FAILURE;
    	}
    
    	ast_cli(a->fd, "Loaded %s\n", a->argv[e->args]);
    
    static char *handle_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "module reload";
    		e->usage =
    			"Usage: module reload [module ...]\n"
    			"       Reloads configuration files for all listed modules which support\n"
    			"       reloading, or for all supported modules if none are listed.\n";
    		return NULL;
    
    	case CLI_GENERATE:
    		return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 1);
    	}
    	if (a->argc == e->args) {
    
    		return CLI_SUCCESS;
    	}
    	for (x = e->args; x < a->argc; x++) {
    
    		enum ast_module_reload_result res = ast_module_reload(a->argv[x]);
    
    		case AST_MODULE_RELOAD_NOT_FOUND:
    
    			ast_cli(a->fd, "No such module '%s'\n", a->argv[x]);
    			break;
    
    		case AST_MODULE_RELOAD_NOT_IMPLEMENTED:
    			ast_cli(a->fd, "The module '%s' does not support reloads\n", a->argv[x]);
    			break;
    		case AST_MODULE_RELOAD_QUEUED:
    			ast_cli(a->fd, "Asterisk cannot reload a module yet; request queued\n");
    			break;
    		case AST_MODULE_RELOAD_ERROR:
    			ast_cli(a->fd, "The module '%s' reported a reload failure\n", a->argv[x]);
    			break;
    		case AST_MODULE_RELOAD_IN_PROGRESS:
    			ast_cli(a->fd, "A module reload request is already in progress; please be patient\n");
    			break;
    		case AST_MODULE_RELOAD_UNINITIALIZED:
    			ast_cli(a->fd, "The module '%s' was not properly initialized. Before reloading"
    					" the module, you must run \"module load %s\" and fix whatever is"
    					" preventing the module from being initialized.\n", a->argv[x], a->argv[x]);
    			break;
    		case AST_MODULE_RELOAD_SUCCESS:
    			ast_cli(a->fd, "Module '%s' reloaded successfully.\n", a->argv[x]);
    
    	return CLI_SUCCESS;
    
    static char *handle_core_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "core reload";
    		e->usage =
    			"Usage: core reload\n"
    			"       Execute a global reload.\n";
    		return NULL;
    
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	if (a->argc != e->args) {
    		return CLI_SHOWUSAGE;
    	}
    
    	ast_module_reload(NULL);
    
    	return CLI_SUCCESS;
    }
    
     * \brief Find the module level setting
     *
     * \param module Module name to look for.
     * \param mll List to search.
     *
     * \retval level struct found on success.
     * \retval NULL not found.
    
    Russell Bryant's avatar
    Russell Bryant committed
     */
    
    static struct module_level *find_module_level(const char *module, struct module_level_list *mll)
    
    Russell Bryant's avatar
    Russell Bryant committed
    {
    
    	AST_LIST_TRAVERSE(mll, ml, entry) {
    		if (!strcasecmp(ml->module, module))
    			return ml;
    
    static char *complete_number(const char *partial, unsigned int min, unsigned int max, int n)
    {
    	int i, count = 0;
    	unsigned int prospective[2];
    	unsigned int part = strtoul(partial, NULL, 10);
    	char next[12];
    
    	if (part < min || part > max) {
    		return NULL;
    	}
    
    	for (i = 0; i < 21; i++) {
    		if (i == 0) {
    			prospective[0] = prospective[1] = part;
    		} else if (part == 0 && !ast_strlen_zero(partial)) {
    			break;
    		} else if (i < 11) {
    			prospective[0] = prospective[1] = part * 10 + (i - 1);
    		} else {
    			prospective[0] = (part * 10 + (i - 11)) * 10;
    			prospective[1] = prospective[0] + 9;
    		}
    		if (i < 11 && (prospective[0] < min || prospective[0] > max)) {
    			continue;
    		} else if (prospective[1] < min || prospective[0] > max) {
    			continue;
    		}
    
    		if (++count > n) {
    			if (i < 11) {
    				snprintf(next, sizeof(next), "%u", prospective[0]);
    			} else {
    				snprintf(next, sizeof(next), "%u...", prospective[0] / 10);
    			}
    			return ast_strdup(next);
    		}
    	}
    	return NULL;
    }
    
    
    static void status_debug_verbose(struct ast_cli_args *a, const char *what, int old_val, int cur_val)
    {
    	char was_buf[30];
    	const char *was;
    
    	if (old_val) {
    		snprintf(was_buf, sizeof(was_buf), "%d", old_val);
    		was = was_buf;
    	} else {
    		was = "OFF";
    	}
    
    	if (old_val == cur_val) {
    		ast_cli(a->fd, "%s is still %s.\n", what, was);
    	} else {
    		char now_buf[30];
    		const char *now;
    
    		if (cur_val) {
    			snprintf(now_buf, sizeof(now_buf), "%d", cur_val);
    			now = now_buf;
    		} else {
    			now = "OFF";
    		}
    
    		ast_cli(a->fd, "%s was %s and is now %s.\n", what, was, now);
    	}
    }
    
    static char *handle_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	int newlevel;
    	int atleast = 0;
    
    	const char *argv3 = a->argv ? S_OR(a->argv[3], "") : "";
    
    		e->command = "core set debug";
    
    			"Usage: core set debug [atleast] <level> [module]\n"
    			"       core set debug off\n"
    			"\n"
    			"       Sets level of debug messages to be displayed or\n"
    
    			"       sets a module name to display debug messages from.\n"
    
    			"       0 or off means no messages should be displayed.\n"
    			"       Equivalent to -d[d[...]] on startup\n";
    
    		if (!strcasecmp(argv3, "atleast")) {
    			atleast = 1;
    		}
    		if (a->pos == 3 || (a->pos == 4 && atleast)) {
    
    			const char *pos = a->pos == 3 ? argv3 : S_OR(a->argv[4], "");
    
    			int numbermatch = (ast_strlen_zero(pos) || strchr("123456789", pos[0])) ? 0 : 21;
    
    			if (a->n < 21 && numbermatch == 0) {
    				return complete_number(pos, 0, 0x7fffffff, a->n);
    			} else if (pos[0] == '0') {
    				if (a->n == 0) {
    					return ast_strdup("0");
    				}
    			} else if (a->n == (21 - numbermatch)) {
    				if (a->pos == 3 && !strncasecmp(argv3, "off", strlen(argv3))) {
    					return ast_strdup("off");
    				} else if (a->pos == 3 && !strncasecmp(argv3, "atleast", strlen(argv3))) {
    					return ast_strdup("atleast");
    				}
    			} else if (a->n == (22 - numbermatch) && a->pos == 3 && ast_strlen_zero(argv3)) {
    				return ast_strdup("atleast");
    			}
    
    		} else if ((a->pos == 4 && !atleast && strcasecmp(argv3, "off") && strcasecmp(argv3, "channel"))
    
    			|| (a->pos == 5 && atleast)) {
    
    			return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
    
    	}
    	/* all the above return, so we proceed with the handler.
    	 * we are guaranteed to be called with argc >= e->args;
    	 */
    
    	if (a->argc <= e->args) {
    
    
    	if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args], "off")) {
    
    	} else {
    		if (!strcasecmp(a->argv[e->args], "atleast")) {
    			atleast = 1;
    		}
    		if (a->argc != e->args + atleast + 1 && a->argc != e->args + atleast + 2) {
    			return CLI_SHOWUSAGE;
    		}
    		if (sscanf(a->argv[e->args + atleast], "%30d", &newlevel) != 1) {
    			return CLI_SHOWUSAGE;
    		}
    
    		if (a->argc == e->args + atleast + 2) {
    			/* We have specified a module name. */
    			char *mod = ast_strdupa(a->argv[e->args + atleast + 1]);
    			int mod_len = strlen(mod);
    
    			if (3 < mod_len && !strcasecmp(mod + mod_len - 3, ".so")) {
    				mod[mod_len - 3] = '\0';
    			}
    
    			AST_RWLIST_WRLOCK(&debug_modules);
    
    			ml = find_module_level(mod, &debug_modules);
    			if (!newlevel) {
    				if (!ml) {
    					/* Specified off for a nonexistent entry. */
    					AST_RWLIST_UNLOCK(&debug_modules);
    					ast_cli(a->fd, "Core debug is still 0 for '%s'.\n", mod);
    					return CLI_SUCCESS;
    				}
    				AST_RWLIST_REMOVE(&debug_modules, ml, entry);
    				if (AST_RWLIST_EMPTY(&debug_modules)) {
    					ast_clear_flag(&ast_options, AST_OPT_FLAG_DEBUG_MODULE);
    				}
    				AST_RWLIST_UNLOCK(&debug_modules);
    
    				ast_cli(a->fd, "Core debug was %u and has been set to 0 for '%s'.\n",
    
    					ml->level, mod);
    				ast_free(ml);
    				return CLI_SUCCESS;
    			}
    
    			if (ml) {
    				if ((atleast && newlevel < ml->level) || ml->level == newlevel) {
    
    					ast_cli(a->fd, "Core debug is still %u for '%s'.\n", ml->level, mod);
    
    					AST_RWLIST_UNLOCK(&debug_modules);
    					return CLI_SUCCESS;
    				}
    				oldval = ml->level;
    				ml->level = newlevel;
    			} else {
    				ml = ast_calloc(1, sizeof(*ml) + strlen(mod) + 1);
    				if (!ml) {
    					AST_RWLIST_UNLOCK(&debug_modules);
    					return CLI_FAILURE;
    				}
    				oldval = ml->level;
    				ml->level = newlevel;
    				strcpy(ml->module, mod);
    				AST_RWLIST_INSERT_TAIL(&debug_modules, ml, entry);
    			}
    			ast_set_flag(&ast_options, AST_OPT_FLAG_DEBUG_MODULE);
    
    
    			ast_cli(a->fd, "Core debug was %d and has been set to %u for '%s'.\n",
    
    				oldval, ml->level, ml->module);
    
    			AST_RWLIST_UNLOCK(&debug_modules);
    
    			return CLI_SUCCESS;
    		}
    
    	/* Update global debug level */
    	if (!newlevel) {
    		/* Specified level was 0 or off. */
    		AST_RWLIST_WRLOCK(&debug_modules);
    		while ((ml = AST_RWLIST_REMOVE_HEAD(&debug_modules, entry))) {
    			ast_free(ml);
    
    		ast_clear_flag(&ast_options, AST_OPT_FLAG_DEBUG_MODULE);
    		AST_RWLIST_UNLOCK(&debug_modules);
    	}
    	oldval = option_debug;
    	if (!atleast || newlevel > option_debug) {
    		option_debug = newlevel;
    	}
    
    	/* Report debug level status */
    	status_debug_verbose(a, "Core debug", oldval, option_debug);
    
    static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    	int oldval;
    	int newlevel;
    	int atleast = 0;
    	int silent = 0;
    	const char *argv3 = a->argv ? S_OR(a->argv[3], "") : "";
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "core set verbose";
    		e->usage =
    			"Usage: core set verbose [atleast] <level> [silent]\n"
    			"       core set verbose off\n"
    			"\n"
    			"       Sets level of verbose messages to be displayed.\n"
    			"       0 or off means no verbose messages should be displayed.\n"
    			"       The silent option means the command does not report what\n"
    			"       happened to the verbose level.\n"
    			"       Equivalent to -v[v[...]] on startup\n";
    		return NULL;
    
    	case CLI_GENERATE:
    		if (!strcasecmp(argv3, "atleast")) {
    			atleast = 1;
    
    Russell Bryant's avatar
    Russell Bryant committed
    		}
    
    		if (a->pos == 3 || (a->pos == 4 && atleast)) {
    			const char *pos = a->pos == 3 ? argv3 : S_OR(a->argv[4], "");
    			int numbermatch = (ast_strlen_zero(pos) || strchr("123456789", pos[0])) ? 0 : 21;
    
    			if (a->n < 21 && numbermatch == 0) {
    				return complete_number(pos, 0, 0x7fffffff, a->n);
    			} else if (pos[0] == '0') {
    				if (a->n == 0) {
    					return ast_strdup("0");
    				}
    			} else if (a->n == (21 - numbermatch)) {
    				if (a->pos == 3 && !strncasecmp(argv3, "off", strlen(argv3))) {
    					return ast_strdup("off");
    				} else if (a->pos == 3 && !strncasecmp(argv3, "atleast", strlen(argv3))) {
    					return ast_strdup("atleast");
    				}
    			} else if (a->n == (22 - numbermatch) && a->pos == 3 && ast_strlen_zero(argv3)) {
    				return ast_strdup("atleast");
    
    Russell Bryant's avatar
    Russell Bryant committed
    			}
    
    		} else if ((a->pos == 4 && !atleast && strcasecmp(argv3, "off"))
    			|| (a->pos == 5 && atleast)) {
    			const char *pos = S_OR(a->argv[a->pos], "");
    
    			if (a->n == 0 && !strncasecmp(pos, "silent", strlen(pos))) {
    				return ast_strdup("silent");
    
    Russell Bryant's avatar
    Russell Bryant committed
    		}
    
    		return NULL;
    	}
    	/* all the above return, so we proceed with the handler.
    	 * we are guaranteed to be called with argc >= e->args;
    	 */
    
    	if (a->argc <= e->args) {
    		return CLI_SHOWUSAGE;
    	}
    
    	if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args], "off")) {
    		newlevel = 0;
    	} else {
    		if (!strcasecmp(a->argv[e->args], "atleast")) {
    			atleast = 1;
    		}
    		if (a->argc == e->args + atleast + 2
    			&& !strcasecmp(a->argv[e->args + atleast + 1], "silent")) {
    			silent = 1;
    		}
    		if (a->argc != e->args + atleast + silent + 1) {
    			return CLI_SHOWUSAGE;
    		}
    		if (sscanf(a->argv[e->args + atleast], "%30d", &newlevel) != 1) {
    			return CLI_SHOWUSAGE;
    		}
    	}
    
    	/* Update verbose level */
    	oldval = ast_verb_console_get();
    	if (!atleast || newlevel > oldval) {
    		ast_verb_console_set(newlevel);
    	} else {
    		newlevel = oldval;
    	}
    
    	if (silent) {
    		/* Be silent after setting the level. */
    
    Russell Bryant's avatar
    Russell Bryant committed
    		return CLI_SUCCESS;
    
    	/* Report verbose level status */
    	status_debug_verbose(a, "Console verbose", oldval, newlevel);
    
    
    static char *handle_logger_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "logger mute";
    
    		e->usage =
    
    			"Usage: logger mute\n"
    			"       Disables logging output to the current console, making it possible to\n"
    			"       gather information without being disturbed by scrolling lines.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    
    	if (a->argc < 2 || a->argc > 3)
    
    	if (a->argc == 3 && !strcasecmp(a->argv[2], "silent"))
    
    		ast_console_toggle_mute(a->fd, 1);
    	else
    		ast_console_toggle_mute(a->fd, 0);
    
    
    static char *handle_unload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	/* "module unload mod_1 [mod_2 .. mod_N]" */
    
    	int x;
    	int force = AST_FORCE_SOFT;
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "module unload";
    		e->usage =
    			"Usage: module unload [-f|-h] <module_1> [<module_2> ... ]\n"
    			"       Unloads the specified module from Asterisk. The -f\n"
    			"       option causes the module to be unloaded even if it is\n"
    			"       in use (may cause a crash) and the -h module causes the\n"
    			"       module to be unloaded even if the module says it cannot, \n"
    			"       which almost always will cause a crash.\n";
    		return NULL;
    
    	case CLI_GENERATE:
    		return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
    	}
    	if (a->argc < e->args + 1)
    		return CLI_SHOWUSAGE;
    	x = e->args;	/* first argument */
    	s = a->argv[x];
    	if (s[0] == '-') {
    		if (s[1] == 'f')
    			force = AST_FORCE_FIRM;
    		else if (s[1] == 'h')
    			force = AST_FORCE_HARD;
    		else
    			return CLI_SHOWUSAGE;
    		if (a->argc < e->args + 2)	/* need at least one module name */
    			return CLI_SHOWUSAGE;
    		x++;	/* skip this argument */
    	}
    
    	for (; x < a->argc; x++) {
    		if (ast_unload_resource(a->argv[x], force)) {
    			ast_cli(a->fd, "Unable to unload resource %s\n", a->argv[x]);
    			return CLI_FAILURE;
    
    		ast_cli(a->fd, "Unloaded %s\n", a->argv[x]);
    
    	return CLI_SUCCESS;
    
    #define MODLIST_FORMAT  "%-30s %-40.40s %-10d %-11s %13s\n"
    #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s %-11s %13s\n"
    
    AST_MUTEX_DEFINE_STATIC(climodentrylock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int climodentryfd = -1;
    
    
    static int modlist_modentry(const char *module, const char *description,
    		int usecnt, const char *status, const char *like,
    		enum ast_module_support_level support_level)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	/* Comparing the like with the module */
    
    	if (strcasestr(module, like) ) {
    
    		ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt,
    				status, ast_module_support_level_to_string(support_level));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    static void print_uptimestr(int fd, struct timeval timeval, const char *prefix, int printsec)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	int x; /* the main part - years, weeks, etc. */
    
    	struct ast_str *out;
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define SECOND (1)
    
    #define MINUTE (SECOND*60)
    #define HOUR (MINUTE*60)
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define DAY (HOUR*24)
    #define WEEK (DAY*7)
    #define YEAR (DAY*365)
    
    #define NEEDCOMMA(x) ((x) ? ", " : "")	/* define if we need a comma */
    
    	if (timeval.tv_sec < 0)	/* invalid, nothing to show */
    
    	if (printsec)  {	/* plain seconds output */
    
    		ast_cli(fd, "%s%lu\n", prefix, (u_long)timeval.tv_sec);
    
    	out = ast_str_alloca(256);
    
    	if (timeval.tv_sec > YEAR) {
    		x = (timeval.tv_sec / YEAR);
    		timeval.tv_sec -= (x * YEAR);
    
    		ast_str_append(&out, 0, "%d year%s%s", x, ESS(x), NEEDCOMMA(timeval.tv_sec));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (timeval.tv_sec > WEEK) {
    		x = (timeval.tv_sec / WEEK);
    		timeval.tv_sec -= (x * WEEK);
    
    		ast_str_append(&out, 0, "%d week%s%s", x, ESS(x), NEEDCOMMA(timeval.tv_sec));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (timeval.tv_sec > DAY) {
    		x = (timeval.tv_sec / DAY);
    		timeval.tv_sec -= (x * DAY);
    
    		ast_str_append(&out, 0, "%d day%s%s", x, ESS(x), NEEDCOMMA(timeval.tv_sec));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (timeval.tv_sec > HOUR) {
    		x = (timeval.tv_sec / HOUR);
    		timeval.tv_sec -= (x * HOUR);
    
    		ast_str_append(&out, 0, "%d hour%s%s", x, ESS(x), NEEDCOMMA(timeval.tv_sec));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (timeval.tv_sec > MINUTE) {
    		x = (timeval.tv_sec / MINUTE);
    		timeval.tv_sec -= (x * MINUTE);
    
    		ast_str_append(&out, 0, "%d minute%s%s", x, ESS(x), NEEDCOMMA(timeval.tv_sec));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (x > 0 || ast_str_strlen(out) == 0) {
    		/* if there is nothing, print 0 seconds */
    		ast_str_append(&out, 0, "%d second%s", x, ESS(x));
    	}
    
    	ast_cli(fd, "%s%s\n", prefix, ast_str_buffer(out));
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    
    static struct ast_cli_entry *cli_next(struct ast_cli_entry *e)
    {
    	if (e) {
    		return AST_LIST_NEXT(e, list);
    	} else {
    		return AST_LIST_FIRST(&helpers);
    	}
    }
    
    
    static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	case CLI_INIT:
    
    		e->command = "core show uptime [seconds]";
    
    			"Usage: core show uptime [seconds]\n"
    			"       Shows Asterisk uptime information.\n"
    			"       The seconds word returns the uptime in seconds only.\n";
    
    	if (a->argc == e->args && !strcasecmp(a->argv[e->args-1],"seconds"))
    
    	if (ast_startuptime.tv_sec) {
    		print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime: ", printsec);
    	}
    	if (ast_lastreloadtime.tv_sec) {
    		print_uptimestr(a->fd, ast_tvsub(curtime, ast_lastreloadtime), "Last reload: ", printsec);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    
    static char *handle_modlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	switch (cmd) {
    	case CLI_INIT:
    
    		e->command = "module show [like]";
    
    			"Usage: module show [like keyword]\n"
    			"       Shows Asterisk modules currently in use, and usage statistics.\n";
    
    
    	case CLI_GENERATE:
    		if (a->pos == e->args)
    
    			return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
    
    	/* all the above return, so we proceed with the handler.
    	 * we are guaranteed to have argc >= e->args
    	 */
    
    	if (a->argc == e->args - 1)
    
    	else if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args-1], "like") )
    		like = a->argv[e->args];
    
    	climodentryfd = a->fd; /* global, protected by climodentrylock */
    
    	ast_cli(a->fd, MODLIST_FORMAT2, "Module", "Description", "Use Count", "Status", "Support Level");
    
    	ast_cli(a->fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	climodentryfd = -1;
    
    	ast_mutex_unlock(&climodentrylock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static char *handle_showcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    
    	static const char * const completions[] = { "seconds", NULL };
    
    	struct timeval curtime = ast_tvnow();
    	int showuptime, printsec;
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "core show calls [uptime]";
    		e->usage =
    
    			"Usage: core show calls [uptime [seconds]]\n"
    
    			"       Lists number of currently active calls and total number of calls\n"
    			"       processed through PBX since last restart. If 'uptime' is specified\n"
    			"       the system uptime is also displayed. If 'seconds' is specified in\n"
    			"       addition to 'uptime', the system uptime is displayed in seconds.\n";
    		return NULL;
    
    	case CLI_GENERATE:
    		if (a->pos != e->args)
    			return NULL;
    
    		return ast_cli_complete(a->word, completions, a->n);
    
    	}
    
    	/* regular handler */
    	if (a->argc >= e->args && !strcasecmp(a->argv[e->args-1],"uptime")) {
    		showuptime = 1;
    
    		if (a->argc == e->args+1 && !strcasecmp(a->argv[e->args],"seconds"))
    			printsec = 1;
    		else if (a->argc == e->args)
    			printsec = 0;
    		else
    			return CLI_SHOWUSAGE;
    	} else if (a->argc == e->args-1) {
    		showuptime = 0;
    		printsec = 0;
    	} else
    		return CLI_SHOWUSAGE;
    
    
    	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);
    
    	} 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()));
    
    	if (ast_startuptime.tv_sec && showuptime) {
    
    		print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime: ", printsec);
    
    static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    #define FORMAT_STRING  "%-20.20s %-20.20s %-7.7s %-30.30s\n"
    #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
    
    #define CONCISE_FORMAT_STRING  "%s!%s!%s!%d!%s!%s!%s!%s!%s!%s!%d!%s!%s!%s\n"
    #define VERBOSE_FORMAT_STRING  "%-20.20s %-20.20s %-16.16s %4d %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-11.11s %-20.20s\n"
    #define VERBOSE_FORMAT_STRING2 "%-20.20s %-20.20s %-16.16s %-4.4s %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-11.11s %-20.20s\n"
    
    	RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
    	struct ao2_iterator it_chans;
    	struct stasis_message *msg;
    
    	int numchans = 0, concise = 0, verbose = 0, count = 0;
    
    	switch (cmd) {
    	case CLI_INIT:
    
    		e->command = "core show channels [concise|verbose|count]";
    
    		e->usage =
    
    			"Usage: core show channels [concise|verbose|count]\n"
    
    			"       Lists currently defined channels and some information about them. If\n"
    			"       'concise' is specified, the format is abridged and in a more easily\n"
    			"       machine parsable format. If 'verbose' is specified, the output includes\n"
    
    			"       more and longer fields. If 'count' is specified only the channel and call\n"
    
    			"       count is output.\n"
    			"	The 'concise' option is deprecated and will be removed from future versions\n"
    			"	of Asterisk.\n";
    
    		return NULL;