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"
#include "asterisk/_private.h"
#include "asterisk/paths.h" /* use ast_config_AST_MODULE_DIR */
#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"
#include "asterisk/stasis_channels.h"
#include "asterisk/stasis_bridges.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;
Russell Bryant
committed
AST_THREADSTORAGE(ast_cli_buf);
AST_RWLOCK_DEFINE_STATIC(shutdown_commands_lock);
static AST_VECTOR(, struct ast_cli_entry *) shutdown_commands;
/*! \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)
157
158
159
160
161
162
163
164
165
166
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
/*! \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);
ast_std_free(d);
}
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++) {
enum ast_module_reload_result res = ast_module_reload(a->argv[x]);
Joshua Colp
committed
switch (res) {
case AST_MODULE_RELOAD_NOT_FOUND:
ast_cli(a->fd, "No such module '%s'\n", a->argv[x]);
break;
case AST_MODULE_RELOAD_NOT_IMPLEMENTED:
ast_cli(a->fd, "The module '%s' does not support reloads\n", a->argv[x]);
break;
case AST_MODULE_RELOAD_QUEUED:
ast_cli(a->fd, "Asterisk cannot reload a module yet; request queued\n");
break;
case AST_MODULE_RELOAD_ERROR:
ast_cli(a->fd, "The module '%s' reported a reload failure\n", a->argv[x]);
break;
case AST_MODULE_RELOAD_IN_PROGRESS:
ast_cli(a->fd, "A module reload request is already in progress; please be patient\n");
break;
case AST_MODULE_RELOAD_UNINITIALIZED:
ast_cli(a->fd, "The module '%s' was not properly initialized. Before reloading"
" the module, you must run \"module load %s\" and fix whatever is"
" preventing the module from being initialized.\n", a->argv[x], a->argv[x]);
break;
case AST_MODULE_RELOAD_SUCCESS:
ast_cli(a->fd, "Module '%s' reloaded successfully.\n", a->argv[x]);
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 module level setting
*
* \param module Module name to look for.
* \param mll List to search.
*
* \retval level struct found on success.
* \retval NULL not found.
static struct module_level *find_module_level(const char *module, struct module_level_list *mll)
struct module_level *ml;
AST_LIST_TRAVERSE(mll, ml, entry) {
if (!strcasecmp(ml->module, module))
return ml;
return NULL;
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
399
400
401
402
403
404
405
406
407
408
409
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;
}
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
static void status_debug_verbose(struct ast_cli_args *a, const char *what, int old_val, int cur_val)
{
char was_buf[30];
const char *was;
if (old_val) {
snprintf(was_buf, sizeof(was_buf), "%d", old_val);
was = was_buf;
} else {
was = "OFF";
}
if (old_val == cur_val) {
ast_cli(a->fd, "%s is still %s.\n", what, was);
} else {
char now_buf[30];
const char *now;
if (cur_val) {
snprintf(now_buf, sizeof(now_buf), "%d", cur_val);
now = now_buf;
} else {
now = "OFF";
}
ast_cli(a->fd, "%s was %s and is now %s.\n", what, was, now);
}
}
static char *handle_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Mark Michelson
committed
int oldval;
int newlevel;
int atleast = 0;
const char *argv3 = a->argv ? S_OR(a->argv[3], "") : "";
struct module_level *ml;
switch (cmd) {
case CLI_INIT:
e->command = "core set debug";
"Usage: core set debug [atleast] <level> [module]\n"
" core set debug off\n"
"\n"
" Sets level of debug messages to be displayed or\n"
" sets a module name to display debug messages from.\n"
" 0 or off means no messages should be displayed.\n"
" Equivalent to -d[d[...]] on startup\n";
case CLI_GENERATE:
if (!strcasecmp(argv3, "atleast")) {
atleast = 1;
}
if (a->pos == 3 || (a->pos == 4 && 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 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 && !atleast && strcasecmp(argv3, "off") && strcasecmp(argv3, "channel"))
|| (a->pos == 5 && atleast)) {
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 be called with argc >= e->args;
*/
if (a->argc <= e->args) {
return CLI_SHOWUSAGE;
if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args], "off")) {
newlevel = 0;
} else {
if (!strcasecmp(a->argv[e->args], "atleast")) {
atleast = 1;
}
if (a->argc != e->args + atleast + 1 && a->argc != e->args + atleast + 2) {
return CLI_SHOWUSAGE;
}
if (sscanf(a->argv[e->args + atleast], "%30d", &newlevel) != 1) {
return CLI_SHOWUSAGE;
}
if (a->argc == e->args + atleast + 2) {
/* We have specified a module name. */
char *mod = ast_strdupa(a->argv[e->args + atleast + 1]);
int mod_len = strlen(mod);
if (3 < mod_len && !strcasecmp(mod + mod_len - 3, ".so")) {
mod[mod_len - 3] = '\0';
}
AST_RWLIST_WRLOCK(&debug_modules);
ml = find_module_level(mod, &debug_modules);
if (!newlevel) {
if (!ml) {
/* Specified off for a nonexistent entry. */
AST_RWLIST_UNLOCK(&debug_modules);
ast_cli(a->fd, "Core debug is still 0 for '%s'.\n", mod);
return CLI_SUCCESS;
}
AST_RWLIST_REMOVE(&debug_modules, ml, entry);
if (AST_RWLIST_EMPTY(&debug_modules)) {
ast_clear_flag(&ast_options, AST_OPT_FLAG_DEBUG_MODULE);
}
AST_RWLIST_UNLOCK(&debug_modules);
ast_cli(a->fd, "Core debug was %u and has been set to 0 for '%s'.\n",
ml->level, mod);
ast_free(ml);
return CLI_SUCCESS;
}
if (ml) {
if ((atleast && newlevel < ml->level) || ml->level == newlevel) {
ast_cli(a->fd, "Core debug is still %u for '%s'.\n", ml->level, mod);
AST_RWLIST_UNLOCK(&debug_modules);
return CLI_SUCCESS;
}
oldval = ml->level;
ml->level = newlevel;
} else {
ml = ast_calloc(1, sizeof(*ml) + strlen(mod) + 1);
if (!ml) {
AST_RWLIST_UNLOCK(&debug_modules);
return CLI_FAILURE;
}
oldval = ml->level;
ml->level = newlevel;
strcpy(ml->module, mod);
AST_RWLIST_INSERT_TAIL(&debug_modules, ml, entry);
}
ast_set_flag(&ast_options, AST_OPT_FLAG_DEBUG_MODULE);
ast_cli(a->fd, "Core debug was %d and has been set to %u for '%s'.\n",
oldval, ml->level, ml->module);
AST_RWLIST_UNLOCK(&debug_modules);
return CLI_SUCCESS;
}
/* Update global debug level */
if (!newlevel) {
/* Specified level was 0 or off. */
AST_RWLIST_WRLOCK(&debug_modules);
while ((ml = AST_RWLIST_REMOVE_HEAD(&debug_modules, entry))) {
ast_free(ml);
ast_clear_flag(&ast_options, AST_OPT_FLAG_DEBUG_MODULE);
AST_RWLIST_UNLOCK(&debug_modules);
}
oldval = option_debug;
if (!atleast || newlevel > option_debug) {
option_debug = newlevel;
}
/* Report debug level status */
status_debug_verbose(a, "Core debug", oldval, option_debug);
return CLI_SUCCESS;
}
static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
int oldval;
int newlevel;
int atleast = 0;
int silent = 0;
const char *argv3 = a->argv ? S_OR(a->argv[3], "") : "";
switch (cmd) {
case CLI_INIT:
e->command = "core set verbose";
e->usage =
"Usage: core set verbose [atleast] <level> [silent]\n"
" core set verbose off\n"
"\n"
" Sets level of verbose messages to be displayed.\n"
" 0 or off means no verbose messages should be displayed.\n"
" The silent option means the command does not report what\n"
" happened to the verbose level.\n"
" Equivalent to -v[v[...]] on startup\n";
return NULL;
case CLI_GENERATE:
if (!strcasecmp(argv3, "atleast")) {
atleast = 1;
if (a->pos == 3 || (a->pos == 4 && 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 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 && !atleast && strcasecmp(argv3, "off"))
|| (a->pos == 5 && atleast)) {
const char *pos = S_OR(a->argv[a->pos], "");
if (a->n == 0 && !strncasecmp(pos, "silent", strlen(pos))) {
return ast_strdup("silent");
return NULL;
}
/* all the above return, so we proceed with the handler.
* we are guaranteed to be called with argc >= e->args;
*/
if (a->argc <= e->args) {
return CLI_SHOWUSAGE;
}
if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args], "off")) {
newlevel = 0;
} else {
if (!strcasecmp(a->argv[e->args], "atleast")) {
atleast = 1;
}
if (a->argc == e->args + atleast + 2
&& !strcasecmp(a->argv[e->args + atleast + 1], "silent")) {
silent = 1;
}
if (a->argc != e->args + atleast + silent + 1) {
return CLI_SHOWUSAGE;
}
if (sscanf(a->argv[e->args + atleast], "%30d", &newlevel) != 1) {
return CLI_SHOWUSAGE;
}
}
/* Update verbose level */
oldval = ast_verb_console_get();
if (!atleast || newlevel > oldval) {
ast_verb_console_set(newlevel);
} else {
newlevel = oldval;
}
if (silent) {
/* Be silent after setting the level. */
/* Report verbose level status */
status_debug_verbose(a, "Console verbose", oldval, newlevel);
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;
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
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]);
Mark Michelson
committed
#define MODLIST_FORMAT "%-30s %-40.40s %-10d %-11s %13s\n"
#define MODLIST_FORMAT2 "%-30s %-40.40s %-10s %-11s %13s\n"
AST_MUTEX_DEFINE_STATIC(climodentrylock);
Mark Michelson
committed
static int modlist_modentry(const char *module, const char *description,
int usecnt, const char *status, const char *like,
enum ast_module_support_level support_level)
/* Comparing the like with the module */
if (strcasestr(module, like) ) {
Mark Michelson
committed
ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt,
status, ast_module_support_level_to_string(support_level));
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 */
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;
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 */
Mark Michelson
committed
ast_cli(a->fd, MODLIST_FORMAT2, "Module", "Description", "Use Count", "Status", "Support Level");
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
static char *handle_showcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
static const char * const completions[] = { "seconds", NULL };
Jason Parker
committed
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"
Jason Parker
committed
" 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 ast_cli_complete(a->word, completions, a->n);
Jason Parker
committed
}
/* 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;
Jason Parker
committed
ast_cli(a->fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
ast_active_calls(), ast_option_maxcalls, ESS(ast_active_calls()),
((double)ast_active_calls() / (double)ast_option_maxcalls) * 100.0);
Jason Parker
committed
} 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);
Jason Parker
committed
}
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
RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
struct ao2_iterator it_chans;
struct stasis_message *msg;
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"
" The 'concise' option is deprecated and will be removed from future versions\n"
" of Asterisk.\n";