Newer
Older
* Asterisk -- An open source telephony toolkit.
Kevin P. Fleming
committed
* Copyright (C) 1999 - 2006, Digium, Inc.
* Mark Spencer <markster@digium.com>
* 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.
*
* 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>
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/signal.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>
Russell Bryant
committed
#include <regex.h>
Kevin P. Fleming
committed
#include "asterisk/logger.h"
#include "asterisk/options.h"
#include "asterisk/cli.h"
#include "asterisk/linkedlists.h"
Kevin P. Fleming
committed
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/channel.h"
#include "asterisk/utils.h"
Russell Bryant
committed
#include "asterisk/app.h"
Kevin P. Fleming
committed
#include "asterisk/lock.h"
#include "asterisk/threadstorage.h"
Russell Bryant
committed
AST_THREADSTORAGE(ast_cli_buf);
/*! \brief Initial buffer size for resulting strings in ast_cli() */
#define AST_CLI_INITLEN 256
void ast_cli(int fd, const char *fmt, ...)
Russell Bryant
committed
int res;
if (!(buf = ast_str_thread_get(&ast_cli_buf, AST_CLI_INITLEN)))
res = ast_str_set_va(&buf, 0, fmt, ap);
if (res != AST_DYNSTR_BUILD_FAILED)
ast_carefulwrite(fd, buf->str, strlen(buf->str), 100);
Kevin P. Fleming
committed
static AST_LIST_HEAD_STATIC(helpers, ast_cli_entry);
static const char logger_mute_help[] =
Joshua Colp
committed
"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";
static const char softhangup_help[] =
Mark Spencer
committed
" Request that a channel be hung up. The hangup takes effect\n"
" the next time the driver reads or writes from the channel\n";
static const char group_show_channels_help[] =
"Usage: group show channels [pattern]\n"
Russell Bryant
committed
" Lists all currently active channels with channel group(s) specified.\n"
" Optional regular expression pattern is matched to group names for each\n"
" channel.\n";
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);
}
static char *handle_load(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
/* "module load <mod>" */
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:
if (a->pos != e->args)
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) {
ast_module_reload(NULL);
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 */
Joshua Colp
committed
switch (res) {
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;
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;
static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Mark Michelson
committed
int oldval;
int newlevel;
int atleast = 0;
int fd = a->fd;
int argc = a->argc;
char **argv = a->argv;
int *dst;
char *what;
switch (cmd) {
case CLI_INIT:
e->command = "core set {debug|verbose} [off|atleast]";
"Usage: core set {debug|verbose} [atleast] <level>\n"
" core set {debug|verbose} off\n"
" Sets level of debug or verbose messages to be displayed.\n"
" 0 or off means no messages should be displayed.\n"
" Equivalent to -d[d[...]] or -v[v[v...]] on startup\n";
case CLI_GENERATE:
}
/* all the above return, so we proceed with the handler.
* we are guaranteed to be called with argc >= e->args;
*/
if (argc < e->args)
return CLI_SHOWUSAGE;
if (!strcasecmp(argv[e->args - 2], "debug")) {
dst = &option_debug;
Mark Michelson
committed
oldval = option_debug;
what = "Core debug";
} else {
dst = &option_verbose;
Mark Michelson
committed
oldval = option_verbose;
what = "Verbosity";
}
if (argc == e->args && !strcasecmp(argv[e->args - 1], "off")) {
newlevel = 0;
goto done;
}
if (!strcasecmp(argv[e->args-1], "atleast"))
atleast = 1;
if (argc != e->args + atleast)
return CLI_SHOWUSAGE;
if (sscanf(argv[e->args + atleast - 1], "%d", &newlevel) != 1)
return CLI_SHOWUSAGE;
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);
return CLI_SUCCESS;
Joshua Colp
committed
static int handle_logger_mute(int fd, int argc, char *argv[])
{
if (argc != 2)
return RESULT_SHOWUSAGE;
ast_console_toggle_mute(fd);
Joshua Colp
committed
return RESULT_SUCCESS;
}
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;
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
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;
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);
static int modlist_modentry(const char *module, const char *description, int usecnt, const char *like)
/* Comparing the like with the module */
if (strcasestr(module, like) ) {
ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
static void print_uptimestr(int fd, time_t timeval, const char *prefix, int printsec)
int x; /* the main part - years, weeks, etc. */
Mark Spencer
committed
#define MINUTE (SECOND*60)
#define HOUR (MINUTE*60)
#define DAY (HOUR*24)
#define WEEK (DAY*7)
#define YEAR (DAY*365)
#define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */
if (timeval < 0) /* invalid, nothing to show */
return;
if (printsec) { /* plain seconds output */
ast_cli(fd, "%s: %lu\n", prefix, (u_long)timeval);
return;
out = ast_str_alloca(256);
x = (timeval / YEAR);
timeval -= (x * YEAR);
ast_str_append(&out, 0, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval));
x = (timeval / WEEK);
timeval -= (x * WEEK);
ast_str_append(&out, 0, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval));
x = (timeval / DAY);
timeval -= (x * DAY);
ast_str_append(&out, 0, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval));
x = (timeval / HOUR);
timeval -= (x * HOUR);
ast_str_append(&out, 0, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval));
Mark Spencer
committed
if (timeval > MINUTE) {
x = (timeval / MINUTE);
timeval -= (x * MINUTE);
ast_str_append(&out, 0, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval));
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);
static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Luigi Rizzo
committed
time_t curtime;
int printsec;
switch (cmd) {
case CLI_INIT:
e->command = "core show uptime [seconds]";
Luigi Rizzo
committed
"Usage: core show uptime [seconds]\n"
" Shows Asterisk uptime information.\n"
" The seconds word returns the uptime in seconds only.\n";
Luigi Rizzo
committed
case CLI_GENERATE:
return NULL;
Luigi Rizzo
committed
}
/* regular handler */
if (a->argc == e->args && !strcasecmp(a->argv[e->args-1],"seconds"))
Luigi Rizzo
committed
printsec = 1;
else if (a->argc == e->args-1)
Luigi Rizzo
committed
printsec = 0;
else
return CLI_SHOWUSAGE;
Luigi Rizzo
committed
curtime = time(NULL);
print_uptimestr(a->fd, curtime - ast_startuptime, "System uptime", printsec);
print_uptimestr(a->fd, curtime - ast_lastreloadtime, "Last reload", printsec);
return CLI_SUCCESS;
static char *handle_modlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
switch (cmd) {
case CLI_INIT:
"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
*/
else if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args-1], "like") )
like = a->argv[e->args];
return CLI_SHOWUSAGE;
ast_mutex_lock(&climodentrylock);
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));
ast_mutex_unlock(&climodentrylock);
Mark Spencer
committed
#undef MODLIST_FORMAT
#undef MODLIST_FORMAT2
static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
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"
Kevin P. Fleming
committed
#define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s\n"
Mark Spencer
committed
#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;
Joshua Colp
committed
int numchans = 0, concise = 0, verbose = 0, count = 0;
Joshua Colp
committed
e->command = "core show channels [concise|verbose|count]";
Joshua Colp
committed
"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"
Joshua Colp
committed
" more and longer fields. If 'count' is specified only the channel and call\n"
" count is output.\n";
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"))
else if (!strcasecmp(argv[e->args-1],"verbose"))
Joshua Colp
committed
else if (!strcasecmp(argv[e->args-1],"count"))
count = 1;
} else if (a->argc != e->args - 1)
Mark Spencer
committed
Joshua Colp
committed
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");
}
Mark Spencer
committed
while ((c = ast_channel_walk_locked(c)) != NULL) {
struct ast_channel *bc = ast_bridged_channel(c);
char durbuf[10] = "-";
Joshua Colp
committed
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
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)");
} 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)");
Mark Spencer
committed
} else {
Joshua Colp
committed
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);
}
Mark Spencer
committed
}
ast_channel_unlock(c);
Mark Spencer
committed
if (!concise) {
ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
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);
ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
Mark Spencer
committed
#undef FORMAT_STRING
#undef FORMAT_STRING2
#undef CONCISE_FORMAT_STRING
#undef VERBOSE_FORMAT_STRING
#undef VERBOSE_FORMAT_STRING2
static const char showchan_help[] =
"Usage: core show channel <channel>\n"
" Shows lots of information about the specified channel.\n";
static const char commandcomplete_help[] =
"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";
static const char commandnummatches_help[] =
"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";
static const char commandmatchesarray_help[] =
"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";
static int handle_softhangup(int fd, int argc, char *argv[])
{
struct ast_channel *c=NULL;
if (argc != 3)
return RESULT_SHOWUSAGE;
Kevin P. Fleming
committed
c = ast_get_channel_by_name_locked(argv[2]);
if (c) {
ast_cli(fd, "Requested Hangup on channel '%s'\n", c->name);
ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
ast_channel_unlock(c);
Kevin P. Fleming
committed
} else
ast_cli(fd, "%s is not a known channel\n", argv[2]);
return RESULT_SUCCESS;
}
Russell Bryant
committed
static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
static int handle_commandmatchesarray(int fd, int argc, char *argv[])
{
int buflen = 2048;
Russell Bryant
committed
if (!(buf = ast_malloc(buflen)))
return RESULT_FAILURE;
buf[len] = '\0';
matches = ast_cli_completion_matches(argv[2], argv[3]);
if (matches) {
for (x=0; matches[x]; x++) {
matchlen = strlen(matches[x]) + 1;
if (len + matchlen >= buflen) {
buflen += matchlen * 3;
obuf = buf;
Russell Bryant
committed
if (!(buf = ast_realloc(obuf, buflen)))
/* Memory allocation failure... Just free old buffer and be done */
Tilghman Lesher
committed
ast_free(obuf);
}
if (buf)
len += sprintf( buf + len, "%s ", matches[x]);
Tilghman Lesher
committed
ast_free(matches[x]);
Tilghman Lesher
committed
ast_free(matches);
ast_cli(fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
Tilghman Lesher
committed
ast_free(buf);
} else
ast_cli(fd, "NULL\n");
return RESULT_SUCCESS;
}
static int handle_commandnummatches(int fd, int argc, char *argv[])
{
int matches = 0;
if (argc != 4)
return RESULT_SHOWUSAGE;
matches = ast_cli_generatornummatches(argv[2], argv[3]);
ast_cli(fd, "%d", matches);
return RESULT_SUCCESS;
}
static int handle_commandcomplete(int fd, int argc, char *argv[])
{
char *buf;
if (argc != 5)
return RESULT_SHOWUSAGE;
buf = __ast_cli_generator(argv[2], argv[3], atoi(argv[4]), 0);
if (buf) {
ast_cli(fd, buf);
Tilghman Lesher
committed
ast_free(buf);
} else
ast_cli(fd, "NULL\n");
return RESULT_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");
return RESULT_SUCCESS;
}
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 int handle_showchan(int fd, int argc, char *argv[])
{
struct ast_channel *c=NULL;
struct ast_str *out = ast_str_alloca(2048);
Mark Spencer
committed
char cdrtime[256];
char nf[256], wf[256], rf[256];
int hour=0, min=0, sec=0;
Kevin P. Fleming
committed
now = ast_tvnow();
c = ast_get_channel_by_name_locked(argv[3]);
Kevin P. Fleming
committed
if (!c) {
ast_cli(fd, "%s is not a known channel\n", argv[3]);
Kevin P. Fleming
committed
return RESULT_SUCCESS;
}
Joshua Colp
committed
if (c->cdr) {
Kevin P. Fleming
committed
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
Kevin P. Fleming
committed
ast_cli(fd,
" -- General --\n"
" Name: %s\n"
" Type: %s\n"
" UniqueID: %s\n"
" Caller ID: %s\n"
" Caller ID Name: %s\n"
" DNID Digits: %s\n"
Russell Bryant
committed
" Language: %s\n"
Kevin P. Fleming
committed
" State: %s (%d)\n"
" Rings: %d\n"
" NativeFormats: %s\n"
" WriteFormat: %s\n"
" ReadFormat: %s\n"
" WriteTranscode: %s\n"
" ReadTranscode: %s\n"
Kevin P. Fleming
committed
"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"
Kevin P. Fleming
committed
" 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)"),
Russell Bryant
committed
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)" : "",
(long)c->whentohangup,
Kevin P. Fleming
committed
cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>",
c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
Kevin P. Fleming
committed
(ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
Joshua Colp
committed
if (pbx_builtin_serialize_variables(c, &out))
ast_cli(fd," Variables:\n%s\n", out->str);
Joshua Colp
committed
if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1))
ast_cli(fd," CDR Variables:\n%s\n", out->str);
Kevin P. Fleming
committed
ast_channel_unlock(c);
/*
* helper function to generate CLI matches from a fixed set of values.
* A NULL word is acceptable.
*/
char *ast_cli_complete(const char *word, char *const choices[], int state)
Mark Spencer
committed
{
int i, which = 0, len;
len = ast_strlen_zero(word) ? 0 : strlen(word);
for (i = 0; choices[i]; i++) {
if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
return ast_strdup(choices[i]);
Mark Spencer
committed
}
return NULL;
}
char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
Kevin P. Fleming
committed
struct ast_channel *c = NULL;
int which = 0;
int wordlen;
char notfound = '\0';
char *ret = ¬found; /* so NULL can break the loop */
Kevin P. Fleming
committed
if (pos != rpos)
return NULL;
wordlen = strlen(word);
while (ret == ¬found && (c = ast_channel_walk_locked(c))) {
if (!strncasecmp(word, c->name, wordlen) && ++which > state)
ret = ast_strdup(c->name);
ast_channel_unlock(c);
return ret == ¬found ? NULL : ret;
Russell Bryant
committed
static char *complete_ch_3(const char *line, const char *word, int pos, int state)
return ast_complete_channels(line, word, pos, state, 2);
static char *complete_ch_4(const char *line, const char *word, int pos, int state)
{
return ast_complete_channels(line, word, pos, state, 3);
}
Russell Bryant
committed
static int group_show_channels(int fd, int argc, char *argv[])
{
#define FORMAT_STRING "%-25s %-20s %-20s\n"
struct ast_group_info *gi = NULL;
Russell Bryant
committed
int numchans = 0;
regex_t regexbuf;
int havepattern = 0;
if (argc < 3 || argc > 4)
return RESULT_SHOWUSAGE;
if (argc == 4) {
if (regcomp(®exbuf, argv[3], REG_EXTENDED | REG_NOSUB))
return RESULT_SHOWUSAGE;
havepattern = 1;
}
ast_cli(fd, FORMAT_STRING, "Channel", "Group", "Category");
ast_app_group_list_rdlock();
gi = ast_app_group_list_head();
while (gi) {
if (!havepattern || !regexec(®exbuf, gi->group, 0, NULL, 0)) {
ast_cli(fd, FORMAT_STRING, gi->chan->name, gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
numchans++;
Russell Bryant
committed
}
Russell Bryant
committed
}
ast_app_group_list_unlock();
Russell Bryant
committed
if (havepattern)
regfree(®exbuf);
ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
Russell Bryant
committed
return RESULT_SUCCESS;
#undef FORMAT_STRING
}
/* XXX Nothing in this array can currently be deprecated...
You have to change the way find_cli works in order to remove this array
I recommend doing this eventually...
*/
/* Keep alphabetized, with longer matches first (example: abcd before abc) */
{ { "_command", "complete", NULL },
handle_commandcomplete, "Command complete",
commandcomplete_help },
{ { "_command", "nummatches", NULL },
handle_commandnummatches, "Returns number of command matches",
commandnummatches_help },
{ { "_command", "matchesarray", NULL },
handle_commandmatchesarray, "Returns command matches array",
commandmatchesarray_help },
static struct ast_cli_entry cli_debug_channel_deprecated = NEW_CLI(handle_debugchan_deprecated, "Enable debugging on channel");
static struct ast_cli_entry cli_module_load_deprecated = NEW_CLI(handle_load_deprecated, "Load a module");
static struct ast_cli_entry cli_module_reload_deprecated = NEW_CLI(handle_reload_deprecated, "reload modules by name");
static struct ast_cli_entry cli_module_unload_deprecated = NEW_CLI(handle_unload_deprecated, "unload modules by name");
static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static struct ast_cli_entry cli_cli[] = {
/* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
NEW_CLI(handle_nodebugchan_deprecated, "Disable debugging on channel(s)"),
NEW_CLI(handle_chanlist, "Display information on channels"),
{ { "core", "show", "channel", NULL },
handle_showchan, "Display information on a specific channel",
showchan_help, complete_ch_4 },
NEW_CLI(handle_core_set_debug_channel, "Enable/disable debugging on a channel",
.deprecate_cmd = &cli_debug_channel_deprecated),
NEW_CLI(handle_verbose, "Set level of debug/verbose chattiness"),
{ { "group", "show", "channels", NULL },
group_show_channels, "Display active channels with group(s)",
NEW_CLI(handle_help, "Display help list, or specific help on a command"),
{ { "logger", "mute", NULL },
handle_logger_mute, "Toggle logging output to a console",
logger_mute_help },
NEW_CLI(handle_modlist, "List modules and info"),