Skip to content
Snippets Groups Projects
cli.c 61.8 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> 
    
    #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"
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include "editline/readline/readline.h"
    
    #include "asterisk/threadstorage.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. */
    AST_RWLIST_HEAD_STATIC(cli_perms, usergroup_cli_perm);
    
    
    Russell Bryant's avatar
    Russell Bryant committed
    /*!
     * \brief map a debug or verbose value to a filename
     */
    struct ast_debug_file {
    	unsigned int level;
    	AST_RWLIST_ENTRY(ast_debug_file) entry;
    	char filename[0];
    };
    
    AST_RWLIST_HEAD(debug_file_list, ast_debug_file);
    
    /*! list of filenames and their debug settings */
    static struct debug_file_list debug_files;
    /*! list of filenames and their verbose settings */
    static struct debug_file_list verbose_files;
    
    
    /*! \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, buf->str, strlen(buf->str), 100);
    
    Russell Bryant's avatar
    Russell Bryant committed
    unsigned int ast_debug_get_by_file(const char *file) 
    {
    	struct ast_debug_file *adf;
    	unsigned int res = 0;
    
    	AST_RWLIST_RDLOCK(&debug_files);
    	AST_LIST_TRAVERSE(&debug_files, adf, entry) {
    		if (!strncasecmp(adf->filename, file, strlen(adf->filename))) {
    			res = adf->level;
    			break;
    		}
    	}
    	AST_RWLIST_UNLOCK(&debug_files);
    
    	return res;
    }
    
    unsigned int ast_verbose_get_by_file(const char *file) 
    {
    	struct ast_debug_file *adf;
    	unsigned int res = 0;
    
    	AST_RWLIST_RDLOCK(&verbose_files);
    	AST_LIST_TRAVERSE(&verbose_files, adf, entry) {
    		if (!strncasecmp(adf->filename, file, strlen(file))) {
    			res = adf->level;
    			break;
    		}
    	}
    	AST_RWLIST_UNLOCK(&verbose_files);
    
    	return res;
    }
    
    
    /*! \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;
    	}
    	return CLI_SUCCESS;
    
    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;
    
    Russell Bryant's avatar
    Russell Bryant committed
    /*! 
     * \brief Find the debug or verbose file setting 
     * \arg debug 1 for debug, 0 for verbose
     */
    static struct ast_debug_file *find_debug_file(const char *fn, unsigned int debug)
    {
    	struct ast_debug_file *df = NULL;
    	struct debug_file_list *dfl = debug ? &debug_files : &verbose_files;
    
    	AST_LIST_TRAVERSE(dfl, df, entry) {
    		if (!strcasecmp(df->filename, fn))
    			break;
    	}
    
    	return df;
    }
    
    
    static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	int newlevel;
    	int atleast = 0;
    
    	int fd = a->fd;
    	int argc = a->argc;
    	char **argv = a->argv;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	struct debug_file_list *dfl;
    	struct ast_debug_file *adf;
    	char *fn;
    
    		e->command = "core set {debug|verbose} [off|atleast]";
    
    Russell Bryant's avatar
    Russell Bryant committed
    			"Usage: core set {debug|verbose} [atleast] <level> [filename]\n"
    
    			"       core set {debug|verbose} off\n"
    
    Russell Bryant's avatar
    Russell Bryant committed
    			"       Sets level of debug or verbose messages to be displayed or \n"
    			"       sets a filename to display debug messages from.\n"
    
    			"	0 or off means no messages should be displayed.\n"
    			"	Equivalent to -d[d[...]] or -v[v[v...]] on startup\n";
    
    	}
    	/* 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 - 2], "debug")) {
    		dst = &option_debug;
    
    		what = "Core debug";
    	} else {
    		dst = &option_verbose;
    
    	if (argc == e->args && !strcasecmp(argv[e->args - 1], "off")) {
    
    Russell Bryant's avatar
    Russell Bryant committed
    		unsigned int debug = (*what == 'C');
    
    Russell Bryant's avatar
    Russell Bryant committed
    
    		dfl = debug ? &debug_files : &verbose_files;
    
    		AST_RWLIST_WRLOCK(dfl);
    		while ((adf = AST_RWLIST_REMOVE_HEAD(dfl, entry)))
    			ast_free(adf);
    		ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
    		AST_RWLIST_UNLOCK(dfl);
    
    
    	if (!strcasecmp(argv[e->args-1], "atleast"))
    
    Russell Bryant's avatar
    Russell Bryant committed
    	if (argc != e->args + atleast && argc != e->args + atleast + 1)
    
    	if (sscanf(argv[e->args + atleast - 1], "%d", &newlevel) != 1)
    
    Russell Bryant's avatar
    Russell Bryant committed
    	if (argc == e->args + atleast + 1) {
    		unsigned int debug = (*what == 'C');
    		dfl = debug ? &debug_files : &verbose_files;
    
    		fn = argv[e->args + atleast];
    
    		AST_RWLIST_WRLOCK(dfl);
    
    		if ((adf = find_debug_file(fn, debug)) && !newlevel) {
    			AST_RWLIST_REMOVE(dfl, adf, entry);
    			if (AST_RWLIST_EMPTY(dfl))
    				ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
    			AST_RWLIST_UNLOCK(dfl);
    			ast_cli(fd, "%s was %d and has been set to 0 for '%s'\n", what, adf->level, fn);
    			ast_free(adf);
    			return CLI_SUCCESS;
    		}
    
    		if (adf) {
    			if ((atleast && newlevel < adf->level) || adf->level == newlevel) {
    				ast_cli(fd, "%s is %d for '%s'\n", what, adf->level, fn);
    				AST_RWLIST_UNLOCK(dfl);
    				return CLI_SUCCESS;
    			}
    		} else if (!(adf = ast_calloc(1, sizeof(*adf) + strlen(fn) + 1))) {
    			AST_RWLIST_UNLOCK(dfl);
    			return CLI_FAILURE;
    		}
    
    		oldval = adf->level;
    		adf->level = newlevel;
    		strcpy(adf->filename, fn);
    
    		ast_set_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
    
    		AST_RWLIST_INSERT_TAIL(dfl, adf, entry);
    		AST_RWLIST_UNLOCK(dfl);
    
    		ast_cli(fd, "%s was %d and has been set to %d for '%s'\n", what, oldval, adf->level, adf->filename);
    
    		return CLI_SUCCESS;
    	}
    
    	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;
    
    	char *s;
    
    	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;
    
    	return CLI_SUCCESS;
    
    #define MODLIST_FORMAT  "%-30s %-40.40s %-10d\n"
    #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\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 *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);
    
    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 || out->used == 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, out->str);
    
    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");
    	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!%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 %-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 %-20.20s\n"
    
    
    	struct ast_channel *c = NULL;
    
    	int numchans = 0, concise = 0, verbose = 0, count = 0;
    
    	int fd, argc;
    	char **argv;
    
    	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;
    
    	}
    	fd = a->fd;
    	argc = a->argc;
    	argv = a->argv;
    
    
    	if (a->argc == e->args) {
    		if (!strcasecmp(argv[e->args-1],"concise"))
    
    			concise = 1;
    
    		else if (!strcasecmp(argv[e->args-1],"verbose"))
    
    			verbose = 1;
    
    		else if (!strcasecmp(argv[e->args-1],"count"))
    			count = 1;
    
    		else
    			return CLI_SHOWUSAGE;
    
    	} else if (a->argc != e->args - 1)
    
    		return CLI_SHOWUSAGE;
    
    	if (!count) {
    		if (!concise && !verbose)
    			ast_cli(fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
    		else if (verbose)
    			ast_cli(fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data", 
    				"CallerID", "Duration", "Accountcode", "BridgedTo");
    	}
    
    	while ((c = ast_channel_walk_locked(c)) != NULL) {
    
    		struct ast_channel *bc = ast_bridged_channel(c);
    
    		if (!count) {
    			if ((concise || verbose)  && c->cdr && !ast_tvzero(c->cdr->start)) {
    				int duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->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);
    				}				
    			}
    			if (concise) {
    				ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
    					c->appl ? c->appl : "(None)",
    					S_OR(c->data, ""),	/* XXX different from verbose ? */
    					S_OR(c->cid.cid_num, ""),
    					S_OR(c->accountcode, ""),
    					c->amaflags, 
    					durbuf,
    
    					bc ? bc->name : "(None)",
    					c->uniqueid);
    
    			} else if (verbose) {
    				ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
    					c->appl ? c->appl : "(None)",
    					c->data ? S_OR(c->data, "(Empty)" ): "(None)",
    					S_OR(c->cid.cid_num, ""),
    					durbuf,
    					S_OR(c->accountcode, ""),
    					bc ? bc->name : "(None)");
    
    				char locbuf[40] = "(None)";
    				char appdata[40] = "(None)";
    				
    				if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten)) 
    					snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
    				if (c->appl)
    
    					snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, S_OR(c->data, ""));
    
    				ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
    			}
    
    		numchans++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    		ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (option_maxcalls)
    
    			ast_cli(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(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
    
    
    		ast_cli(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>\n"
    
    			"       Request that a channel be hung up. The hangup takes effect\n"
    			"       the next time the driver reads or writes from the channel\n";
    		return NULL;
    	case CLI_GENERATE:
    
    		return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
    
    	if (a->argc != 4)
    
    	c = ast_get_channel_by_name_locked(a->argv[3]);
    
    		ast_cli(a->fd, "Requested Hangup on channel '%s'\n", c->name);
    
    		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);
    	}