Skip to content
Snippets Groups Projects
cli.c 72.3 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.h"
    
    ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
    
    
    #include "asterisk/_private.h"
    
    #include "asterisk/paths.h"	/* use ast_config_AST_MODULE_DIR */
    
    #include <sys/signal.h>
    #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"
    
    /*!
     * \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;
    
    /*! list of module names and their verbose levels */
    
    static struct module_level_list verbose_modules = AST_RWLIST_HEAD_INIT_VALUE;
    
    /*! \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
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	unsigned int res = 0;
    
    
    	AST_RWLIST_RDLOCK(&verbose_modules);
    	AST_LIST_TRAVERSE(&verbose_modules, ml, entry) {
    		if (!strcasecmp(ml->module, module)) {
    			res = ml->level;
    
    Russell Bryant's avatar
    Russell Bryant committed
    			break;
    		}
    	}
    
    	AST_RWLIST_UNLOCK(&verbose_modules);
    
    /*! \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++) {
    		int res = ast_module_reload(a->argv[x]);
    
    		/* XXX reload has multiple error returns, including -1 on error and 2 on success */
    
    		case 0:
    			ast_cli(a->fd, "No such module '%s'\n", a->argv[x]);
    			break;
    		case 1:
    			ast_cli(a->fd, "Module '%s' does not support reload\n", a->argv[x]);
    			break;
    
    	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 debug or verbose file setting
    
    Russell Bryant's avatar
    Russell Bryant committed
     * \arg debug 1 for debug, 0 for verbose
     */
    
    static struct module_level *find_module_level(const char *module, unsigned int debug)
    
    Russell Bryant's avatar
    Russell Bryant committed
    {
    
    	struct module_level *ml;
    	struct module_level_list *mll = debug ? &debug_modules : &verbose_modules;
    
    	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 char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	unsigned int is_debug;
    
    	int atleast = 0;
    
    	int fd = a->fd;
    	int argc = a->argc;
    
    	const char * const *argv = a->argv;
    	const char *argv3 = a->argv ? S_OR(a->argv[3], "") : "";
    
    	struct module_level_list *mll;
    	struct module_level *ml;
    
    		e->command = "core set {debug|verbose}";
    
    #if !defined(LOW_MEMORY)
    
    			"Usage: core set {debug|verbose} [atleast] <level> [module]\n"
    
    #else
    			"Usage: core set {debug|verbose} [atleast] <level>\n"
    #endif
    
    			"       core set {debug|verbose} off\n"
    
    #if !defined(LOW_MEMORY)
    
    			"       Sets level of debug or verbose messages to be displayed or\n"
    			"       sets a module name to display debug messages from.\n"
    
    #else
    			"       Sets level of debug or verbose messages to be displayed.\n"
    #endif
    
    			"	0 or off means no messages should be displayed.\n"
    			"	Equivalent to -d[d[...]] or -v[v[v...]] on startup\n";
    
    		if (a->pos == 3 || (a->pos == 4 && !strcasecmp(a->argv[3], "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 {
    					return NULL;
    				}
    			} 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");
    			}
    
    #if !defined(LOW_MEMORY)
    
    		} else if (a->pos == 4 || (a->pos == 5 && !strcasecmp(argv3, "atleast"))) {
    			return ast_complete_source_filename(a->pos == 4 ? S_OR(a->argv[4], "") : S_OR(a->argv[5], ""), a->n);
    
    #endif
    
    	}
    	/* all the above return, so we proceed with the handler.
    	 * we are guaranteed to be called with argc >= e->args;
    	 */
    
    	if (!strcasecmp(argv[e->args - 1], "debug")) {
    
    	if (argc == e->args + 1 && !strcasecmp(argv[e->args], "off")) {
    
    		mll = is_debug ? &debug_modules : &verbose_modules;
    
    		AST_RWLIST_WRLOCK(mll);
    		while ((ml = AST_RWLIST_REMOVE_HEAD(mll, entry))) {
    			ast_free(ml);
    		}
    
    		ast_clear_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE);
    
    	if (!strcasecmp(argv[e->args], "atleast"))
    
    	if (argc != e->args + atleast + 1 && argc != e->args + atleast + 2)
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	if (sscanf(argv[e->args + atleast], "%30d", &newlevel) != 1)
    
    	if (argc == e->args + atleast + 2) {
    
    		/* We have specified a module name. */
    
    		char *mod = ast_strdupa(argv[e->args + atleast + 1]);
    
    		if ((strlen(mod) > 3) && !strcasecmp(mod + strlen(mod) - 3, ".so")) {
    			mod[strlen(mod) - 3] = '\0';
    		}
    
    		mll = is_debug ? &debug_modules : &verbose_modules;
    
    
    		ml = find_module_level(mod, is_debug);
    		if (!newlevel) {
    			if (!ml) {
    				/* Specified off for a nonexistent entry. */
    				AST_RWLIST_UNLOCK(mll);
    				return CLI_SUCCESS;
    			}
    
    			AST_RWLIST_REMOVE(mll, ml, entry);
    			if (AST_RWLIST_EMPTY(mll))
    
    				ast_clear_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE);
    
    			AST_RWLIST_UNLOCK(mll);
    			ast_cli(fd, "%s was %d and has been set to 0 for '%s'\n", what, ml->level, mod);
    			ast_free(ml);
    
    Russell Bryant's avatar
    Russell Bryant committed
    			return CLI_SUCCESS;
    		}
    
    
    		if (ml) {
    			if ((atleast && newlevel < ml->level) || ml->level == newlevel) {
    				ast_cli(fd, "%s is %d for '%s'\n", what, ml->level, mod);
    				AST_RWLIST_UNLOCK(mll);
    
    Russell Bryant's avatar
    Russell Bryant committed
    				return CLI_SUCCESS;
    			}
    
    			oldval = ml->level;
    			ml->level = newlevel;
    		} else {
    			ml = ast_calloc(1, sizeof(*ml) + strlen(mod) + 1);
    			if (!ml) {
    				AST_RWLIST_UNLOCK(mll);
    				return CLI_FAILURE;
    			}
    			oldval = ml->level;
    			ml->level = newlevel;
    			strcpy(ml->module, mod);
    			AST_RWLIST_INSERT_TAIL(mll, ml, entry);
    
    		ast_set_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE);
    
    		ast_cli(fd, "%s was %d and has been set to %d for '%s'\n", what, oldval, ml->level, ml->module);
    
    Russell Bryant's avatar
    Russell Bryant committed
    
    		return CLI_SUCCESS;
    
    	} else if (!newlevel) {
    		/* Specified level as 0 instead of off. */
    		mll = is_debug ? &debug_modules : &verbose_modules;
    
    		AST_RWLIST_WRLOCK(mll);
    		while ((ml = AST_RWLIST_REMOVE_HEAD(mll, entry))) {
    			ast_free(ml);
    		}
    		ast_clear_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE);
    		AST_RWLIST_UNLOCK(mll);
    
    Russell Bryant's avatar
    Russell Bryant committed
    	}
    
    	if (!atleast || newlevel > *dst)
    		*dst = newlevel;
    	if (oldval > 0 && *dst == 0)
    		ast_cli(fd, "%s is now OFF\n", what);
    	else if (*dst > 0) {
    		if (oldval == *dst)
    			ast_cli(fd, "%s is at least %d\n", what, *dst);
    
    			ast_cli(fd, "%s was %d and is now %d\n", what, oldval, *dst);
    
    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 %s\n"
    #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s %s\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)
    
    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);
    
    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");
    
    	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)
    {
    	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 a->n == 0  ? ast_strdup("seconds") : NULL;
    	}
    
    	/* 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 (option_maxcalls) {
    		ast_cli(a->fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
    		   ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
    		   ((double)ast_active_calls() / (double)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);
    	}
    
    	return RESULT_SUCCESS;
    }
    
    
    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"
    
    	struct ast_channel *c = NULL;
    
    	int numchans = 0, concise = 0, verbose = 0, count = 0;
    
    	struct ast_channel_iterator *iter = NULL;
    
    	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;
    
    	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;
    
    			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");
    
    	if (!count && !(iter = ast_channel_iterator_all_new())) {
    
    		return CLI_FAILURE;
    	}
    
    	for (; iter && (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
    
    			if ((concise || verbose)  && ast_channel_cdr(c) && !ast_tvzero(ast_channel_cdr(c)->start)) {
    				int duration = (int)(ast_tvdiff_ms(ast_tvnow(), ast_channel_cdr(c)->start) / 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, ast_channel_name(c), ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_state2str(ast_channel_state(c)),
    
    					ast_channel_appl(c) ? ast_channel_appl(c) : "(None)",
    					S_OR(ast_channel_data(c), ""),	/* XXX different from verbose ? */
    
    					S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, ""),
    
    					S_OR(ast_channel_accountcode(c), ""),
    					S_OR(ast_channel_peeraccount(c), ""),
    
    					ast_channel_amaflags(c),
    
    					bridge ? bridge->uniqueid : "(Not bridged)",
    
    					ast_channel_uniqueid(c));
    
    				ast_cli(a->fd, VERBOSE_FORMAT_STRING, ast_channel_name(c), ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_state2str(ast_channel_state(c)),
    
    					ast_channel_appl(c) ? ast_channel_appl(c) : "(None)",
    					ast_channel_data(c) ? S_OR(ast_channel_data(c), "(Empty)" ): "(None)",
    
    					S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, ""),
    
    					S_OR(ast_channel_accountcode(c), ""),
    					S_OR(ast_channel_peeraccount(c), ""),
    
    					bridge ? bridge->uniqueid : "(Not bridged)");
    
    				char locbuf[40] = "(None)";
    				char appdata[40] = "(None)";
    
    
    				if (!ast_strlen_zero(ast_channel_context(c)) && !ast_strlen_zero(ast_channel_exten(c)))
    
    					snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", ast_channel_exten(c), ast_channel_context(c), ast_channel_priority(c));
    
    				if (ast_channel_appl(c))
    					snprintf(appdata, sizeof(appdata), "%s(%s)", ast_channel_appl(c), S_OR(ast_channel_data(c), ""));
    
    				ast_cli(a->fd, FORMAT_STRING, ast_channel_name(c), locbuf, ast_state2str(ast_channel_state(c)), appdata);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    		numchans = ast_active_channels();
    
    		ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (option_maxcalls)
    
    			ast_cli(a->fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
    
    				ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
    				((double)ast_active_calls() / (double)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()));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	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;
    
    		e->command = "channel request hangup";
    
    			"Usage: channel request hangup <channel>|<all>\n"
    
    			"       Request that a channel be hung up. The hangup takes effect\n"