Skip to content
Snippets Groups Projects
cli.c 51.4 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 <sys/signal.h>
    #include <signal.h>
    #include <ctype.h>
    
    #include "asterisk/options.h"
    #include "asterisk/cli.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"
    
    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;
    }
    
    
    static AST_RWLIST_HEAD_STATIC(helpers, ast_cli_entry);
    
    static char *complete_fn(const char *word, int state)
    {
    	char *c;
    	char filename[256];
    
    	if (word[0] == '/')
    		ast_copy_string(filename, word, sizeof(filename));
    	else
    		snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
    
    	/* XXX the following function is not reentrant, so we better not use it */
    	c = filename_completion_function(filename, state);
    	
    	if (c && word[0] != '/')
    		c += (strlen(ast_config_AST_MODULE_DIR) + 1);
    	
    
    	return c ? ast_strdup(c) : 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_load_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	char *res = handle_load(e, cmd, a);
    	if (cmd == CLI_INIT)
    		e->command = "load";
    	return res;
    
    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_reload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	char *s = handle_reload(e, cmd, a);
    	if (cmd == CLI_INIT)		/* override command name */
    		e->command = "reload";
    	return s;
    
    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;
    
    static char *handle_unload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	char *res = handle_unload(e, cmd, a);
    	if (cmd == CLI_INIT)
    		e->command = "unload";	/* XXX override */
    	return res;
    
    #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 char * handle_showuptime(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 = "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
    	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";
    
    		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()));
    
    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;
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "soft hangup";
    		e->usage =
    			"Usage: soft 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, 2);
    	}
    	if (a->argc != 3)
    		return CLI_SHOWUSAGE;
    	c = ast_get_channel_by_name_locked(a->argv[2]);
    
    		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[2]);
    	return CLI_SUCCESS;
    
    static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
    
    static char *handle_commandmatchesarray(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	char *buf, *obuf;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int len = 0;
    	char **matches;
    
    	int x, matchlen;
    
    	
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "_command matchesarray";
    		e->usage = 
    			"Usage: _command matchesarray \"<line>\" text \n"
    			"       This function is used internally to help with command completion and should.\n"
    			"       never be called by the user directly.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	buf[len] = '\0';
    
    	matches = ast_cli_completion_matches(a->argv[2], a->argv[3]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (matches) {
    		for (x=0; matches[x]; x++) {
    
    			matchlen = strlen(matches[x]) + 1;
    			if (len + matchlen >= buflen) {
    				buflen += matchlen * 3;
    				obuf = buf;
    
    				if (!(buf = ast_realloc(obuf, buflen))) 
    					/* Memory allocation failure...  Just free old buffer and be done */
    
    			if (buf)
    				len += sprintf( buf + len, "%s ", matches[x]);
    
    			matches[x] = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (buf) {
    
    		ast_cli(a->fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else
    
    static char *handle_commandnummatches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int matches = 0;
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "_command nummatches";
    		e->usage = 
    			"Usage: _command nummatches \"<line>\" text \n"
    			"       This function is used internally to help with command completion and should.\n"
    			"       never be called by the user directly.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	if (a->argc != 4)
    		return CLI_SHOWUSAGE;
    
    	matches = ast_cli_generatornummatches(a->argv[2], a->argv[3]);
    
    static char *handle_commandcomplete(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	char *buf;
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "_command complete";
    		e->usage = 
    			"Usage: _command complete \"<line>\" text state\n"
    			"       This function is used internally to help with command completion and should.\n"
    			"       never be called by the user directly.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    	if (a->argc != 5)
    		return CLI_SHOWUSAGE;
    	buf = __ast_cli_generator(a->argv[2], a->argv[3], atoi(a->argv[4]), 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (buf) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else
    
    		ast_cli(a->fd, "NULL\n");
    	return CLI_SUCCESS;
    
    static char *handle_core_set_debug_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    {
    	struct ast_channel *c = NULL;
    	int is_all, is_off = 0;
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "core set debug channel";
    		e->usage =
    			"Usage: core set debug channel <all|channel> [off]\n"
    			"       Enables/disables debugging on all or on a specific channel.\n";
    		return NULL;
    
    	case CLI_GENERATE:
    		/* XXX remember to handle the optional "off" */
    		if (a->pos != e->args)
    			return NULL;
    
    		return a->n == 0 ? ast_strdup("all") : ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
    
    	/* 'core set debug channel {all|chan_id}' */
    
    	if (a->argc == e->args + 2) {
    		if (!strcasecmp(a->argv[e->args + 1], "off"))
    			is_off = 1;
    		else
    			return CLI_SHOWUSAGE;
    	} else if (a->argc != e->args + 1)
    		return CLI_SHOWUSAGE;
    
    	is_all = !strcasecmp("all", a->argv[e->args]);
    
    	if (is_all) {
    		if (is_off) {
    			global_fin &= ~DEBUGCHAN_FLAG;
    			global_fout &= ~DEBUGCHAN_FLAG;
    		} else {
    			global_fin |= DEBUGCHAN_FLAG;
    			global_fout |= DEBUGCHAN_FLAG;
    		}
    		c = ast_channel_walk_locked(NULL);
    	} else {
    
    		c = ast_get_channel_by_name_locked(a->argv[e->args]);
    
    			ast_cli(a->fd, "No such channel %s\n", a->argv[e->args]);
    
    	}
    	while (c) {
    		if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
    			if (is_off) {
    				c->fin &= ~DEBUGCHAN_FLAG;
    				c->fout &= ~DEBUGCHAN_FLAG;
    			} else {
    				c->fin |= DEBUGCHAN_FLAG;
    				c->fout |= DEBUGCHAN_FLAG;
    			}
    
    			ast_cli(a->fd, "Debugging %s on channel %s\n", is_off ? "disabled" : "enabled", c->name);
    
    		}
    		ast_channel_unlock(c);
    		if (!is_all)
    			break;
    		c = ast_channel_walk_locked(c);
    	}
    
    	ast_cli(a->fd, "Debugging on new channels is %s\n", is_off ? "disabled" : "enabled");
    
    static char *handle_debugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    	char *res;
    
    	if (cmd == CLI_HANDLER && a->argc != e->args + 1)
    		return CLI_SHOWUSAGE;
    	res = handle_core_set_debug_channel(e, cmd, a);
    	if (cmd == CLI_INIT)
    		e->command = "debug channel";
    	return res;
    }
    
    
    static char *handle_nodebugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	char *res;
    	if (cmd == CLI_HANDLER) {
    		if (a->argc != e->args + 1)
    			return CLI_SHOWUSAGE;
    		/* pretend we have an extra "off" at the end. We can do this as the array
    		 * is NULL terminated so we overwrite that entry.
    		 */
    		a->argv[e->args+1] = "off";
    		a->argc++;
    
    	res = handle_core_set_debug_channel(e, cmd, a);
    	if (cmd == CLI_INIT)
    		e->command = "no debug channel";
    	return res;
    
    static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_channel *c=NULL;
    
    	struct timeval now;
    
    	struct ast_str *out = ast_str_alloca(2048);
    
    	char nf[256], wf[256], rf[256];
    
    	long elapsed_seconds=0;
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "core show channel";
    		e->usage = 
    			"Usage: core show channel <channel>\n"
    			"       Shows lots of information about the specified channel.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
    	}
    
    	c = ast_get_channel_by_name_locked(a->argv[3]);
    
    		ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
    		return CLI_SUCCESS;
    
    		elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
    		hour = elapsed_seconds / 3600;
    		min = (elapsed_seconds % 3600) / 60;
    		sec = elapsed_seconds % 60;
    		snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
    	} else
    
    		strcpy(cdrtime, "N/A");
    
    		" -- General --\n"
    		"           Name: %s\n"
    		"           Type: %s\n"
    		"       UniqueID: %s\n"
    		"      Caller ID: %s\n"
    		" Caller ID Name: %s\n"
    		"    DNID Digits: %s\n"
    
    		"  NativeFormats: %s\n"
    		"    WriteFormat: %s\n"
    		"     ReadFormat: %s\n"
    
    		" WriteTranscode: %s\n"
    		"  ReadTranscode: %s\n"
    
    		"1st File Descriptor: %d\n"
    		"      Frames in: %d%s\n"
    		"     Frames out: %d%s\n"
    		" Time to Hangup: %ld\n"
    		"   Elapsed Time: %s\n"
    		"  Direct Bridge: %s\n"
    		"Indirect Bridge: %s\n"
    		" --   PBX   --\n"
    		"        Context: %s\n"
    		"      Extension: %s\n"
    		"       Priority: %d\n"
    
    		"     Call Group: %llu\n"
    		"   Pickup Group: %llu\n"
    
    		"    Application: %s\n"
    		"           Data: %s\n"
    		"    Blocking in: %s\n",
    
    		c->name, c->tech->type, c->uniqueid,
    
    		S_OR(c->cid.cid_num, "(N/A)"),
    		S_OR(c->cid.cid_name, "(N/A)"),
    
    		S_OR(c->cid.cid_dnid, "(N/A)"), 
    		c->language,	
    		ast_state2str(c->_state), c->_state, c->rings, 
    
    		ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats), 
    		ast_getformatname_multiple(wf, sizeof(wf), c->writeformat), 
    		ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
    
    		c->writetrans ? "Yes" : "No",
    		c->readtrans ? "Yes" : "No",
    
    		c->fds[0],
    		c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
    		c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",