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>
/*! \li \ref cli.c uses the configuration file \ref cli_permissions.conf
* \addtogroup configuration_file Configuration Files
*/
/*!
* \page cli_permissions.conf cli_permissions.conf
* \verbinclude cli_permissions.conf.sample
*/
/*** MODULEINFO
<support_level>core</support_level>
***/
Kevin P. Fleming
committed
#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>
Russell Bryant
committed
#include <regex.h>
#include <editline/readline.h>
Kevin P. Fleming
committed
#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"
Richard Mudgett
committed
#include "asterisk/bridging.h"
/*!
* \brief List of restrictions per user.
*/
struct cli_perm {
unsigned int permit:1; /*!< 1=Permit 0=Deny */
char *command; /*!< Command name (to apply restrictions) */
AST_LIST_ENTRY(cli_perm) list;
};
AST_LIST_HEAD_NOLOCK(cli_perm_head, cli_perm);
/*! \brief list of users to apply restrictions. */
struct usergroup_cli_perm {
int uid; /*!< User ID (-1 disabled) */
int gid; /*!< Group ID (-1 disabled) */
struct cli_perm_head *perms; /*!< List of permissions. */
AST_LIST_ENTRY(usergroup_cli_perm) list;/*!< List mechanics */
};
/*! \brief CLI permissions config file. */
static const char perms_config[] = "cli_permissions.conf";
/*! \brief Default permissions value 1=Permit 0=Deny */
static int cli_default_perm = 1;
/*! \brief mutex used to prevent a user from running the 'cli reload permissions' command while
* it is already running. */
AST_MUTEX_DEFINE_STATIC(permsconfiglock);
/*! \brief List of users and permissions. */
static AST_RWLIST_HEAD_STATIC(cli_perms, usergroup_cli_perm);
* \brief map a debug or verbose level to a module name
struct module_level {
AST_RWLIST_ENTRY(module_level) entry;
char module[0];
AST_RWLIST_HEAD(module_level_list, module_level);
/*! list of module names and their debug levels */
static struct module_level_list debug_modules = AST_RWLIST_HEAD_INIT_VALUE;
/*! list of module names and their verbose levels */
static struct module_level_list verbose_modules = AST_RWLIST_HEAD_INIT_VALUE;
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, ast_str_buffer(buf), ast_str_strlen(buf), 100);
}
unsigned int ast_debug_get_by_module(const char *module)
struct module_level *ml;
AST_RWLIST_RDLOCK(&debug_modules);
AST_LIST_TRAVERSE(&debug_modules, ml, entry) {
if (!strcasecmp(ml->module, module)) {
res = ml->level;
AST_RWLIST_UNLOCK(&debug_modules);
unsigned int ast_verbose_get_by_module(const char *module)
struct module_level *ml;
AST_RWLIST_RDLOCK(&verbose_modules);
AST_LIST_TRAVERSE(&verbose_modules, ml, entry) {
if (!strcasecmp(ml->module, module)) {
res = ml->level;
AST_RWLIST_UNLOCK(&verbose_modules);
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/*! \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(®exbuf, perm->command, REG_EXTENDED | REG_NOSUB | REG_ICASE);
if (ispattern && regexec(®exbuf, command, 0, NULL, 0)) {
regfree(®exbuf);
continue;
}
if (!ispattern) {
continue;
}
regfree(®exbuf);
}
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)
{
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)
/* "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;
}
ast_cli(a->fd, "Loaded %s\n", a->argv[e->args]);
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) {
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_core_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "core reload";
e->usage =
"Usage: core reload\n"
" Execute a global reload.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != e->args) {
return CLI_SHOWUSAGE;
}
ast_module_reload(NULL);
return CLI_SUCCESS;
}
/*!
* \brief Find the debug or verbose file setting
static struct module_level *find_module_level(const char *module, unsigned int debug)
struct module_level *ml;
struct module_level_list *mll = debug ? &debug_modules : &verbose_modules;
AST_LIST_TRAVERSE(mll, ml, entry) {
if (!strcasecmp(ml->module, module))
return ml;
return NULL;
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
static char *complete_number(const char *partial, unsigned int min, unsigned int max, int n)
{
int i, count = 0;
unsigned int prospective[2];
unsigned int part = strtoul(partial, NULL, 10);
char next[12];
if (part < min || part > max) {
return NULL;
}
for (i = 0; i < 21; i++) {
if (i == 0) {
prospective[0] = prospective[1] = part;
} else if (part == 0 && !ast_strlen_zero(partial)) {
break;
} else if (i < 11) {
prospective[0] = prospective[1] = part * 10 + (i - 1);
} else {
prospective[0] = (part * 10 + (i - 11)) * 10;
prospective[1] = prospective[0] + 9;
}
if (i < 11 && (prospective[0] < min || prospective[0] > max)) {
continue;
} else if (prospective[1] < min || prospective[0] > max) {
continue;
}
if (++count > n) {
if (i < 11) {
snprintf(next, sizeof(next), "%u", prospective[0]);
} else {
snprintf(next, sizeof(next), "%u...", prospective[0] / 10);
}
return ast_strdup(next);
}
}
return NULL;
}
static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Mark Michelson
committed
int oldval;
int fd = a->fd;
int argc = a->argc;
const char * const *argv = a->argv;
const char *argv3 = a->argv ? S_OR(a->argv[3], "") : "";
int *dst;
char *what;
struct module_level_list *mll;
struct module_level *ml;
switch (cmd) {
case CLI_INIT:
e->command = "core set {debug|verbose}";
"Usage: core set {debug|verbose} [atleast] <level> [module]\n"
#else
"Usage: core set {debug|verbose} [atleast] <level>\n"
#endif
" core set {debug|verbose} off\n"
" Sets level of debug or verbose messages to be displayed or\n"
" sets a module name to display debug messages from.\n"
#else
" Sets level of debug or verbose messages to be displayed.\n"
#endif
" 0 or off means no messages should be displayed.\n"
" Equivalent to -d[d[...]] or -v[v[v...]] on startup\n";
case CLI_GENERATE:
if (a->pos == 3 || (a->pos == 4 && !strcasecmp(a->argv[3], "atleast"))) {
const char *pos = a->pos == 3 ? argv3 : S_OR(a->argv[4], "");
int numbermatch = (ast_strlen_zero(pos) || strchr("123456789", pos[0])) ? 0 : 21;
if (a->n < 21 && numbermatch == 0) {
return complete_number(pos, 0, 0x7fffffff, a->n);
} else if (pos[0] == '0') {
if (a->n == 0) {
return ast_strdup("0");
} else {
return NULL;
}
} else if (a->n == (21 - numbermatch)) {
if (a->pos == 3 && !strncasecmp(argv3, "off", strlen(argv3))) {
return ast_strdup("off");
} else if (a->pos == 3 && !strncasecmp(argv3, "atleast", strlen(argv3))) {
return ast_strdup("atleast");
}
} else if (a->n == (22 - numbermatch) && a->pos == 3 && ast_strlen_zero(argv3)) {
return ast_strdup("atleast");
}
} else if (a->pos == 4 || (a->pos == 5 && !strcasecmp(argv3, "atleast"))) {
return ast_complete_source_filename(a->pos == 4 ? S_OR(a->argv[4], "") : S_OR(a->argv[5], ""), a->n);
}
/* 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 - 1], "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 + 1 && !strcasecmp(argv[e->args], "off")) {
newlevel = 0;
mll = is_debug ? &debug_modules : &verbose_modules;
AST_RWLIST_WRLOCK(mll);
while ((ml = AST_RWLIST_REMOVE_HEAD(mll, entry))) {
ast_free(ml);
}
ast_clear_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE);
AST_RWLIST_UNLOCK(mll);
goto done;
}
if (!strcasecmp(argv[e->args], "atleast"))
atleast = 1;
if (argc != e->args + atleast + 1 && argc != e->args + atleast + 2)
return CLI_SHOWUSAGE;
if (sscanf(argv[e->args + atleast], "%30d", &newlevel) != 1)
return CLI_SHOWUSAGE;
if (argc == e->args + atleast + 2) {
/* We have specified a module name. */
char *mod = ast_strdupa(argv[e->args + atleast + 1]);
if ((strlen(mod) > 3) && !strcasecmp(mod + strlen(mod) - 3, ".so")) {
mod[strlen(mod) - 3] = '\0';
}
mll = is_debug ? &debug_modules : &verbose_modules;
AST_RWLIST_WRLOCK(mll);
ml = find_module_level(mod, is_debug);
if (!newlevel) {
if (!ml) {
/* Specified off for a nonexistent entry. */
AST_RWLIST_UNLOCK(mll);
return CLI_SUCCESS;
}
AST_RWLIST_REMOVE(mll, ml, entry);
if (AST_RWLIST_EMPTY(mll))
ast_clear_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE);
AST_RWLIST_UNLOCK(mll);
ast_cli(fd, "%s was %d and has been set to 0 for '%s'\n", what, ml->level, mod);
ast_free(ml);
if (ml) {
if ((atleast && newlevel < ml->level) || ml->level == newlevel) {
ast_cli(fd, "%s is %d for '%s'\n", what, ml->level, mod);
AST_RWLIST_UNLOCK(mll);
oldval = ml->level;
ml->level = newlevel;
} else {
ml = ast_calloc(1, sizeof(*ml) + strlen(mod) + 1);
if (!ml) {
AST_RWLIST_UNLOCK(mll);
return CLI_FAILURE;
}
oldval = ml->level;
ml->level = newlevel;
strcpy(ml->module, mod);
AST_RWLIST_INSERT_TAIL(mll, ml, entry);
ast_set_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE);
AST_RWLIST_UNLOCK(mll);
ast_cli(fd, "%s was %d and has been set to %d for '%s'\n", what, oldval, ml->level, ml->module);
} else if (!newlevel) {
/* Specified level as 0 instead of off. */
mll = is_debug ? &debug_modules : &verbose_modules;
AST_RWLIST_WRLOCK(mll);
while ((ml = AST_RWLIST_REMOVE_HEAD(mll, entry))) {
ast_free(ml);
}
ast_clear_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE);
AST_RWLIST_UNLOCK(mll);
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;
static char *handle_logger_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Joshua Colp
committed
{
switch (cmd) {
case CLI_INIT:
e->command = "logger mute";
"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)
return CLI_SHOWUSAGE;
if (a->argc == 3 && !strcasecmp(a->argv[2], "silent"))
ast_console_toggle_mute(a->fd, 1);
else
ast_console_toggle_mute(a->fd, 0);
return CLI_SUCCESS;
Joshua Colp
committed
}
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;
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
switch (cmd) {
case CLI_INIT:
e->command = "module unload";
e->usage =
"Usage: module unload [-f|-h] <module_1> [<module_2> ... ]\n"
" Unloads the specified module from Asterisk. The -f\n"
" option causes the module to be unloaded even if it is\n"
" in use (may cause a crash) and the -h module causes the\n"
" module to be unloaded even if the module says it cannot, \n"
" which almost always will cause a crash.\n";
return NULL;
case CLI_GENERATE:
return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
}
if (a->argc < e->args + 1)
return CLI_SHOWUSAGE;
x = e->args; /* first argument */
s = a->argv[x];
if (s[0] == '-') {
if (s[1] == 'f')
force = AST_FORCE_FIRM;
else if (s[1] == 'h')
force = AST_FORCE_HARD;
else
return CLI_SHOWUSAGE;
if (a->argc < e->args + 2) /* need at least one module name */
return CLI_SHOWUSAGE;
x++; /* skip this argument */
}
for (; x < a->argc; x++) {
if (ast_unload_resource(a->argv[x], force)) {
ast_cli(a->fd, "Unable to unload resource %s\n", a->argv[x]);
return CLI_FAILURE;
ast_cli(a->fd, "Unloaded %s\n", a->argv[x]);
#define MODLIST_FORMAT "%-30s %-40.40s %-10d %s\n"
#define MODLIST_FORMAT2 "%-30s %-40.40s %-10s %s\n"
AST_MUTEX_DEFINE_STATIC(climodentrylock);
static int modlist_modentry(const char *module, const char *description, int usecnt, const char *status, const char *like)
/* Comparing the like with the module */
if (strcasestr(module, like) ) {
ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt, status);
Tilghman Lesher
committed
static void print_uptimestr(int fd, struct timeval 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 */
Tilghman Lesher
committed
if (timeval.tv_sec < 0) /* invalid, nothing to show */
if (printsec) { /* plain seconds output */
Tilghman Lesher
committed
ast_cli(fd, "%s: %lu\n", prefix, (u_long)timeval.tv_sec);
out = ast_str_alloca(256);
Tilghman Lesher
committed
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));
Tilghman Lesher
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));
Tilghman Lesher
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));
Tilghman Lesher
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));
Tilghman Lesher
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));
Tilghman Lesher
committed
x = timeval.tv_sec;
if (x > 0 || ast_str_strlen(out) == 0) /* if there is nothing, print 0 seconds */
ast_str_append(&out, 0, "%d second%s ", x, ESS(x));
ast_cli(fd, "%s: %s\n", prefix, ast_str_buffer(out));
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)
Tilghman Lesher
committed
struct timeval curtime = ast_tvnow();
Luigi Rizzo
committed
int printsec;
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;
Tilghman Lesher
committed
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);
return CLI_SUCCESS;
static char *handle_modlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
const char *like;
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", "Status");
ast_cli(a->fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
ast_mutex_unlock(&climodentrylock);
return CLI_SUCCESS;
Mark Spencer
committed
#undef MODLIST_FORMAT
#undef MODLIST_FORMAT2
Jason Parker
committed
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
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()));
}
Jason Parker
committed
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
committed
#define FORMAT_STRING "%-20.20s %-20.20s %-7.7s %-30.30s\n"
#define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
#define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%s!%d!%s!%s!%s\n"
#define VERBOSE_FORMAT_STRING "%-20.20s %-20.20s %-16.16s %4d %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-11.11s %-20.20s\n"
#define VERBOSE_FORMAT_STRING2 "%-20.20s %-20.20s %-16.16s %-4.4s %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-11.11s %-20.20s\n"
Mark Spencer
committed
struct ast_channel *c = NULL;
Joshua Colp
committed
int numchans = 0, concise = 0, verbose = 0, count = 0;
struct ast_channel_iterator *iter = NULL;
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"
" The 'concise' option is deprecated and will be removed from future versions\n"
" of Asterisk.\n";
case CLI_GENERATE:
return NULL;
if (a->argc == e->args) {
if (!strcasecmp(a->argv[e->args-1],"concise"))
else if (!strcasecmp(a->argv[e->args-1],"verbose"))
else if (!strcasecmp(a->argv[e->args-1],"count"))
Joshua Colp
committed
count = 1;
} else if (a->argc != e->args - 1)
Mark Spencer
committed
Joshua Colp
committed
if (!count) {
if (!concise && !verbose)
ast_cli(a->fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
Joshua Colp
committed
else if (verbose)
ast_cli(a->fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data",
Richard Mudgett
committed
"CallerID", "Duration", "Accountcode", "PeerAccount", "BridgeID");
Joshua Colp
committed
}
Kevin P. Fleming
committed
if (!count && !(iter = ast_channel_iterator_all_new())) {
return CLI_FAILURE;
}
for (; iter && (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
Richard Mudgett
committed
struct ast_bridge *bridge;
char durbuf[10] = "-";
ast_channel_lock(c);
Richard Mudgett
committed
bridge = ast_channel_get_bridge(c);
Joshua Colp
committed
if (!count) {
if ((concise || verbose) && ast_channel_cdr(c) && !ast_tvzero(ast_channel_cdr(c)->start)) {
int duration = (int)(ast_tvdiff_ms(ast_tvnow(), ast_channel_cdr(c)->start) / 1000);
Joshua Colp
committed
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);
Joshua Colp
committed
}
if (concise) {
ast_cli(a->fd, CONCISE_FORMAT_STRING, ast_channel_name(c), ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_state2str(ast_channel_state(c)),
ast_channel_appl(c) ? ast_channel_appl(c) : "(None)",
S_OR(ast_channel_data(c), ""), /* XXX different from verbose ? */
S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, ""),
S_OR(ast_channel_accountcode(c), ""),
S_OR(ast_channel_peeraccount(c), ""),
Joshua Colp
committed
durbuf,
Richard Mudgett
committed
bridge ? bridge->uniqueid : "(Not bridged)",
Joshua Colp
committed
} else if (verbose) {
ast_cli(a->fd, VERBOSE_FORMAT_STRING, ast_channel_name(c), ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_state2str(ast_channel_state(c)),
ast_channel_appl(c) ? ast_channel_appl(c) : "(None)",
ast_channel_data(c) ? S_OR(ast_channel_data(c), "(Empty)" ): "(None)",
S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, ""),
Joshua Colp
committed
durbuf,
S_OR(ast_channel_accountcode(c), ""),
S_OR(ast_channel_peeraccount(c), ""),
Richard Mudgett
committed
bridge ? bridge->uniqueid : "(Not bridged)");
Mark Spencer
committed
} else {
Joshua Colp
committed
char locbuf[40] = "(None)";
char appdata[40] = "(None)";
if (!ast_strlen_zero(ast_channel_context(c)) && !ast_strlen_zero(ast_channel_exten(c)))
snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", ast_channel_exten(c), ast_channel_context(c), ast_channel_priority(c));
if (ast_channel_appl(c))
snprintf(appdata, sizeof(appdata), "%s(%s)", ast_channel_appl(c), S_OR(ast_channel_data(c), ""));
ast_cli(a->fd, FORMAT_STRING, ast_channel_name(c), locbuf, ast_state2str(ast_channel_state(c)), appdata);
Joshua Colp
committed
}
Mark Spencer
committed
}
ast_channel_unlock(c);
Richard Mudgett
committed
ao2_cleanup(bridge);
if (iter) {
ast_channel_iterator_destroy(iter);
}
Mark Spencer
committed
if (!concise) {
numchans = ast_active_channels();
ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
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);
ast_cli(a->fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
Jason Parker
committed
ast_cli(a->fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
Mark Spencer
committed
#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)
switch (cmd) {
case CLI_INIT:
e->usage =
"Usage: channel request hangup <channel>|<all>\n"
" Request that a channel be hung up. The hangup takes effect\n"