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>
*/
#include <unistd.h>
#include <stdlib.h>
#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/manager.h"
#include "asterisk/utils.h"
Russell Bryant
committed
#include "asterisk/app.h"
Kevin P. Fleming
committed
#include "asterisk/lock.h"
Kevin P. Fleming
committed
#include "asterisk/version.h"
extern const char *ast_build_hostname;
extern const char *ast_build_kernel;
extern const char *ast_build_machine;
extern const char *ast_build_os;
extern const char *ast_build_date;
extern const char *ast_build_user;
Mark Spencer
committed
extern unsigned long global_fin, global_fout;
Russell Bryant
committed
int res;
res = vasprintf(&stuff, fmt, ap);
Russell Bryant
committed
ast_log(LOG_ERROR, "Memory allocation failure\n");
ast_carefulwrite(fd, stuff, strlen(stuff), 100);
free(stuff);
}
Kevin P. Fleming
committed
static AST_LIST_HEAD_STATIC(helpers, ast_cli_entry);
static char load_help[] =
"Usage: load <module name>\n"
" Loads the specified module into Asterisk.\n";
static char unload_help[] =
"Usage: unload [-f|-h] <module name>\n"
Mark Spencer
committed
" 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";
static char help_help[] =
"Usage: help [topic]\n"
" When called with a topic as an argument, displays usage\n"
Mark Spencer
committed
" information on the given command. If called without a\n"
" topic, it provides a list of commands.\n";
static char chanlist_help[] =
Mark Spencer
committed
"Usage: show channels [concise|verbose]\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.\n";
"Usage: reload [module ...]\n"
" Reloads configuration files for all listed modules which support\n"
" reloading, or for all supported modules if none are listed.\n";
static char set_verbose_help[] =
"Usage: set verbose <level>\n"
" Sets level of verbose messages to be displayed. 0 means\n"
" no messages should be displayed. Equivalent to -v[v[v...]]\n"
" on startup\n";
static char set_debug_help[] =
"Usage: set debug <level>\n"
" Sets level of core debug messages to be displayed. 0 means\n"
" no messages should be displayed. Equivalent to -d[d[d...]]\n"
" on startup.\n";
static char softhangup_help[] =
"Usage: soft hangup <channel>\n"
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";
Russell Bryant
committed
static char group_show_channels_help[] =
"Usage: group show channels [pattern]\n"
" 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 frog_help[] =
"Usage: frog [warp_factor]\n"
" Performs frog-in-a-blender calculations (Jacobsen Corollary)\n";
static int handle_load(int fd, int argc, char *argv[])
{
if (argc != 2)
return RESULT_SHOWUSAGE;
if (ast_load_resource(argv[1])) {
ast_cli(fd, "Unable to load module %s\n", argv[1]);
return RESULT_FAILURE;
}
return RESULT_SUCCESS;
}
static int handle_reload(int fd, int argc, char *argv[])
{
Mark Spencer
committed
int x;
Mark Spencer
committed
if (argc < 1)
Mark Spencer
committed
if (argc > 1) {
for (x=1;x<argc;x++) {
res = ast_module_reload(argv[x]);
switch(res) {
case 0:
ast_cli(fd, "No such module '%s'\n", argv[x]);
break;
case 1:
ast_cli(fd, "Module '%s' does not support reload\n", argv[x]);
break;
}
}
Mark Spencer
committed
} else
ast_module_reload(NULL);
return RESULT_SUCCESS;
}
static int handle_set_verbose(int fd, int argc, char *argv[])
{
int val = 0;
int oldval = option_verbose;
/* "set verbose [atleast] N" */
if (argc == 3)
option_verbose = atoi(argv[2]);
else if (argc == 4) {
if (strcasecmp(argv[2], "atleast"))
return RESULT_SHOWUSAGE;
val = atoi(argv[3]);
if (val > option_verbose)
option_verbose = val;
} else
return RESULT_SHOWUSAGE;
if (oldval != option_verbose && option_verbose > 0)
ast_cli(fd, "Verbosity was %d and is now %d\n", oldval, option_verbose);
else if (oldval > 0 && option_verbose > 0)
ast_cli(fd, "Verbosity is at least %d\n", option_verbose);
else if (oldval > 0 && option_verbose == 0)
ast_cli(fd, "Verbosity is now OFF\n");
return RESULT_SUCCESS;
}
static int handle_set_debug(int fd, int argc, char *argv[])
{
int val = 0;
int oldval = option_debug;
/* "set debug [atleast] N" */
if (argc == 3)
option_debug = atoi(argv[2]);
else if (argc == 4) {
if (strcasecmp(argv[2], "atleast"))
return RESULT_SHOWUSAGE;
val = atoi(argv[3]);
if (val > option_debug)
option_debug = val;
} else
return RESULT_SHOWUSAGE;
if (oldval != option_debug && option_debug > 0)
ast_cli(fd, "Core debug was %d and is now %d\n", oldval, option_debug);
else if (oldval > 0 && option_debug > 0)
ast_cli(fd, "Core debug is at least %d\n", option_debug);
else if (oldval > 0 && option_debug == 0)
ast_cli(fd, "Core debug is now OFF\n");
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
static int handle_unload(int fd, int argc, char *argv[])
{
int x;
int force=AST_FORCE_SOFT;
if (argc < 2)
return RESULT_SHOWUSAGE;
for (x=1;x<argc;x++) {
if (argv[x][0] == '-') {
switch(argv[x][1]) {
case 'f':
force = AST_FORCE_FIRM;
break;
case 'h':
force = AST_FORCE_HARD;
break;
default:
return RESULT_SHOWUSAGE;
}
} else if (x != argc - 1)
return RESULT_SHOWUSAGE;
else if (ast_unload_resource(argv[x], force)) {
ast_cli(fd, "Unable to unload resource %s\n", argv[x]);
return RESULT_FAILURE;
}
}
return RESULT_SUCCESS;
}
/*
* Perform frong-in-a-blender calculations (Jacobsen Corollary)
*/
static int handle_frog(int fd, int argc, char *argv[])
{
double warpone = 75139293848.0;
double warpfactor = 1.0;
if (argc > 2)
return RESULT_SHOWUSAGE;
if (argc > 1 && sscanf(argv[1], "%lf", &warpfactor) != 1)
return RESULT_SHOWUSAGE;
ast_cli(fd, "A frog in a blender with a base diameter of 3 inches going\n");
ast_cli(fd, "%.0f RPM will be travelling at warp factor %f,\n",
warpfactor * warpfactor * warpfactor * warpone, warpfactor);
ast_cli(fd, "based upon the Jacobsen Frog Corollary.\n");
return RESULT_SUCCESS;
}
#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);
return 0;
}
static char modlist_help[] =
"Usage: show modules [like keyword]\n"
" Shows Asterisk modules currently in use, and usage statistics.\n";
static char version_help[] =
"Usage: show version\n"
" Shows Asterisk version information.\n";
static char uptime_help[] =
"Usage: show uptime [seconds]\n"
" Shows Asterisk uptime information.\n"
" The seconds word returns the uptime in seconds only.\n";
static void print_uptimestr(int fd, time_t timeval, const char *prefix, int printsec)
int x; /* the main part - years, weeks, etc. */
char timestr[256]="", *s = timestr;
size_t maxbytes = sizeof(timestr);
Mark Spencer
committed
#define MINUTE (SECOND*60)
#define HOUR (MINUTE*60)
#define DAY (HOUR*24)
#define WEEK (DAY*7)
#define YEAR (DAY*365)
#define ESS(x) ((x == 1) ? "" : "s") /* plural suffix */
#define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */
if (timeval < 0) /* invalid, nothing to show */
return;
if (printsec) { /* plain seconds output */
ast_build_string(&s, &maxbytes, "%lu", (u_long)timeval);
timeval = 0; /* bypass the other cases */
}
x = (timeval / YEAR);
timeval -= (x * YEAR);
ast_build_string(&s, &maxbytes, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval));
x = (timeval / WEEK);
timeval -= (x * WEEK);
ast_build_string(&s, &maxbytes, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval));
x = (timeval / DAY);
timeval -= (x * DAY);
ast_build_string(&s, &maxbytes, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval));
x = (timeval / HOUR);
timeval -= (x * HOUR);
ast_build_string(&s, &maxbytes, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval));
Mark Spencer
committed
if (timeval > MINUTE) {
x = (timeval / MINUTE);
timeval -= (x * MINUTE);
ast_build_string(&s, &maxbytes, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval));
x = timeval;
if (x > 0)
ast_build_string(&s, &maxbytes, "%d second%s ", x, ESS(x));
if (timestr[0] != '\0')
ast_cli(fd, "%s: %s\n", prefix, timestr);
}
static int handle_showuptime(int fd, int argc, char *argv[])
{
/* 'show uptime [seconds]' */
time_t curtime = time(NULL);
int printsec = (argc == 3 && !strcasecmp(argv[2],"seconds"));
if (argc != 2 && !printsec)
return RESULT_SHOWUSAGE;
if (ast_startuptime)
print_uptimestr(fd, curtime - ast_startuptime, "System uptime", printsec);
if (ast_lastreloadtime)
print_uptimestr(fd, curtime - ast_lastreloadtime, "Last reload", printsec);
static int handle_modlist(int fd, int argc, char *argv[])
{
char *like = "";
if (argc == 3)
return RESULT_SHOWUSAGE;
like = argv[3];
}
ast_mutex_lock(&climodentrylock);
climodentryfd = fd; /* global, protected by climodentrylock */
ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
ast_cli(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 int handle_version(int fd, int argc, char *argv[])
{
if (argc != 2)
return RESULT_SHOWUSAGE;
ast_cli(fd, "Asterisk %s built by %s @ %s on a %s running %s on %s\n",
ASTERISK_VERSION, ast_build_user, ast_build_hostname,
ast_build_machine, ast_build_os, ast_build_date);
static int handle_chanlist(int fd, int argc, char *argv[])
{
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;
Mark Spencer
committed
char durbuf[10] = "-";
char locbuf[40];
char appdata[40];
int duration;
int durh, durm, durs;
int numchans = 0, concise = 0, verbose = 0;
concise = (argc == 3 && (!strcasecmp(argv[2],"concise")));
Mark Spencer
committed
verbose = (argc == 3 && (!strcasecmp(argv[2],"verbose")));
Mark Spencer
committed
if (argc < 2 || argc > 3 || (argc == 3 && !concise && !verbose))
return RESULT_SHOWUSAGE;
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);
Mark Spencer
committed
if ((concise || verbose) && c->cdr && !ast_tvzero(c->cdr->start)) {
duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
if (verbose) {
durh = duration / 3600;
durm = (duration % 3600) / 60;
durs = duration % 60;
snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
} else {
snprintf(durbuf, sizeof(durbuf), "%d", duration);
}
} else {
durbuf[0] = '\0';
}
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)", c->data ? c->data : "",
c->cid.cid_num ? c->cid.cid_num : "",
c->accountcode ? c->accountcode : "", c->amaflags,
Mark Spencer
committed
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)",
c->cid.cid_num ? c->cid.cid_num : "", durbuf,
c->accountcode ? c->accountcode : "", bc ? bc->name : "(None)");
Mark Spencer
committed
} else {
if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten))
snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
else
strcpy(locbuf, "(None)");
Mark Spencer
committed
snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, c->data ? c->data : "");
Mark Spencer
committed
strcpy(appdata, "(None)");
ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
}
ast_mutex_unlock(&c->lock);
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 char showchan_help[] =
"Usage: show channel <channel>\n"
" Shows lots of information about the specified channel.\n";
static char debugchan_help[] =
"Usage: debug channel <channel>\n"
" Enables debugging on a specific channel.\n";
static char debuglevel_help[] =
"Usage: debug level <level> [filename]\n"
" Set debug to specified level (0 to disable). If filename\n"
"is specified, debugging will be limited to just that file.\n";
static char nodebugchan_help[] =
"Usage: no debug channel <channel>\n"
" Disables debugging on a specific channel.\n";
static 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 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 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_mutex_unlock(&c->lock);
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 */
}
if (buf)
len += sprintf( buf + len, "%s ", matches[x]);
free(matches[x]);
matches[x] = NULL;
ast_cli(fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
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);
free(buf);
} else
ast_cli(fd, "NULL\n");
return RESULT_SUCCESS;
}
static int handle_debuglevel(int fd, int argc, char *argv[])
{
int newlevel;
char *filename = "<any>";
if ((argc < 3) || (argc > 4))
return RESULT_SHOWUSAGE;
Kevin P. Fleming
committed
if (sscanf(argv[2], "%d", &newlevel) != 1)
return RESULT_SHOWUSAGE;
option_debug = newlevel;
if (argc == 4) {
filename = argv[3];
ast_copy_string(debug_filename, filename, sizeof(debug_filename));
} else {
debug_filename[0] = '\0';
}
ast_cli(fd, "Debugging level set to %d, file '%s'\n", newlevel, filename);
return RESULT_SUCCESS;
}
Kevin P. Fleming
committed
#define DEBUGCHAN_FLAG 0x80000000
/* XXX todo: merge next two functions!!! */
static int handle_debugchan(int fd, int argc, char *argv[])
{
struct ast_channel *c=NULL;
Mark Spencer
committed
int is_all;
/* 'debug channel {all|chan_id}' */
if (argc != 3)
return RESULT_SHOWUSAGE;
Mark Spencer
committed
is_all = !strcasecmp("all", argv[2]);
if (is_all) {
Kevin P. Fleming
committed
global_fin |= DEBUGCHAN_FLAG;
global_fout |= DEBUGCHAN_FLAG;
c = ast_channel_walk_locked(NULL);
} else {
c = ast_get_channel_by_name_locked(argv[2]);
if (c == NULL)
ast_cli(fd, "No such channel %s\n", argv[2]);
Mark Spencer
committed
}
while (c) {
Kevin P. Fleming
committed
if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
c->fin |= DEBUGCHAN_FLAG;
c->fout |= DEBUGCHAN_FLAG;
ast_cli(fd, "Debugging enabled on channel %s\n", c->name);
ast_mutex_unlock(&c->lock);
Kevin P. Fleming
committed
if (!is_all)
break;
c = ast_channel_walk_locked(c);
Kevin P. Fleming
committed
ast_cli(fd, "Debugging on new channels is enabled\n");
return RESULT_SUCCESS;
}
static int handle_nodebugchan(int fd, int argc, char *argv[])
{
struct ast_channel *c=NULL;
Mark Spencer
committed
int is_all;
/* 'no debug channel {all|chan_id}' */
if (argc != 4)
return RESULT_SHOWUSAGE;
Mark Spencer
committed
is_all = !strcasecmp("all", argv[3]);
if (is_all) {
Kevin P. Fleming
committed
global_fin &= ~DEBUGCHAN_FLAG;
global_fout &= ~DEBUGCHAN_FLAG;
c = ast_channel_walk_locked(NULL);
} else {
c = ast_get_channel_by_name_locked(argv[3]);
if (c == NULL)
ast_cli(fd, "No such channel %s\n", argv[3]);
Kevin P. Fleming
committed
if ((c->fin & DEBUGCHAN_FLAG) || (c->fout & DEBUGCHAN_FLAG)) {
c->fin &= ~DEBUGCHAN_FLAG;
c->fout &= ~DEBUGCHAN_FLAG;
ast_cli(fd, "Debugging disabled on channel %s\n", c->name);
ast_mutex_unlock(&c->lock);
Kevin P. Fleming
committed
if (!is_all)
break;
c = ast_channel_walk_locked(c);
Kevin P. Fleming
committed
ast_cli(fd, "Debugging on new channels is disabled\n");
static int handle_showchan(int fd, int argc, char *argv[])
{
struct ast_channel *c=NULL;
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();
Kevin P. Fleming
committed
c = ast_get_channel_by_name_locked(argv[2]);
if (!c) {
ast_cli(fd, "%s is not a known channel\n", argv[2]);
Kevin P. Fleming
committed
return RESULT_SUCCESS;
}
if(c->cdr) {
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"
" State: %s (%d)\n"
" Rings: %d\n"
" NativeFormats: %s\n"
" WriteFormat: %s\n"
" ReadFormat: %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: %d\n"
" Pickup Group: %d\n"
" Application: %s\n"
" Data: %s\n"
" Blocking in: %s\n",
c->name, c->tech->type, c->uniqueid,
Kevin P. Fleming
committed
(c->cid.cid_num ? c->cid.cid_num : "(N/A)"),
(c->cid.cid_name ? c->cid.cid_name : "(N/A)"),
(c->cid.cid_dnid ? c->cid.cid_dnid : "(N/A)" ), 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),
Kevin P. Fleming
committed
c->fds[0], c->fin & 0x7fffffff, (c->fin & 0x80000000) ? " (DEBUGGED)" : "",
c->fout & 0x7fffffff, (c->fout & 0x80000000) ? " (DEBUGGED)" : "", (long)c->whentohangup,
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)"));
if(pbx_builtin_serialize_variables(c,buf,sizeof(buf)))
ast_cli(fd," Variables:\n%s\n",buf);
if(c->cdr && ast_cdr_serialize_variables(c->cdr,buf, sizeof(buf), '=', '\n', 1))
ast_cli(fd," CDR Variables:\n%s\n",buf);
ast_mutex_unlock(&c->lock);
/*
* 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;
}
static char *complete_show_channels(const char *line, const char *word, int pos, int state)
{
static char *choices[] = { "concise", "verbose", NULL };
return (pos != 2) ? NULL : ast_cli_complete(word, choices, state);
}
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_mutex_unlock(&c->lock);
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);
Russell Bryant
committed
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 char *complete_mod_2(const char *line, const char *word, int pos, int state)
{
return ast_module_helper(line, word, pos, state, 1, 1);
}
Russell Bryant
committed
static char *complete_mod_4(const char *line, const char *word, int pos, int state)
{
return ast_module_helper(line, word, pos, state, 3, 0);
}
Russell Bryant
committed
static char *complete_fn(const char *line, const char *word, int pos, int state)
ast_copy_string(filename, word, sizeof(filename));
snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
c = filename_completion_function(filename, state);
c += (strlen(ast_config_AST_MODULE_DIR) + 1);
Russell Bryant
committed
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
static int group_show_channels(int fd, int argc, char *argv[])
{
#define FORMAT_STRING "%-25s %-20s %-20s\n"
struct ast_channel *c = NULL;
int numchans = 0;
struct ast_var_t *current;
struct varshead *headp;
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");
while ( (c = ast_channel_walk_locked(c)) != NULL) {
headp=&c->varshead;
AST_LIST_TRAVERSE(headp,current,entries) {
if (!strncmp(ast_var_name(current), GROUP_CATEGORY_PREFIX "_", strlen(GROUP_CATEGORY_PREFIX) + 1)) {
if (!havepattern || !regexec(®exbuf, ast_var_value(current), 0, NULL, 0)) {
ast_cli(fd, FORMAT_STRING, c->name, ast_var_value(current),
(ast_var_name(current) + strlen(GROUP_CATEGORY_PREFIX) + 1));
numchans++;
}
} else if (!strcmp(ast_var_name(current), GROUP_CATEGORY_PREFIX)) {
if (!havepattern || !regexec(®exbuf, ast_var_value(current), 0, NULL, 0)) {
ast_cli(fd, FORMAT_STRING, c->name, ast_var_value(current), "(default)");
numchans++;
}
}
}
numchans++;
ast_mutex_unlock(&c->lock);
}
if (havepattern)
regfree(®exbuf);
ast_cli(fd, "%d active channel%s\n", numchans, (numchans != 1) ? "s" : "");
return RESULT_SUCCESS;
#undef FORMAT_STRING
}
static int handle_help(int fd, int argc, char *argv[]);
Russell Bryant
committed
static char * complete_help(const char *text, const char *word, int pos, int state)
{
/* skip first 4 or 5 chars, "help "*/
int l = strlen(text);
if (l > 5)
l = 5;
text += l;
/* XXX watch out, should stop to the non-generator parts */
return __ast_cli_generator(text, word, state, 0);
/* 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 },
{ { "debug", "channel", NULL }, handle_debugchan, "Enable debugging on a channel", debugchan_help, complete_ch_3 },
{ { "debug", "level", NULL }, handle_debuglevel, "Set global debug level", debuglevel_help },
{ { "frog", NULL }, handle_frog,"Perform frog-in-a-blender calculations", frog_help },
{ { "group", "show", "channels", NULL }, group_show_channels, "Show active channels with group(s)", group_show_channels_help},
{ { "help", NULL }, handle_help, "Display help list, or specific help on a command", help_help, complete_help },
{ { "load", NULL }, handle_load, "Load a dynamic module by name", load_help, complete_fn },
{ { "no", "debug", "channel", NULL }, handle_nodebugchan, "Disable debugging on a channel", nodebugchan_help, complete_ch_4 },
{ { "reload", NULL }, handle_reload, "Reload configuration", reload_help, complete_mod_2 },
{ { "set", "debug", NULL }, handle_set_debug, "Set level of debug chattiness", set_debug_help },
{ { "set", "verbose", NULL }, handle_set_verbose, "Set level of verboseness", set_verbose_help },
{ { "show", "channel", NULL }, handle_showchan, "Display information on a specific channel", showchan_help, complete_ch_3 },
Mark Spencer
committed
{ { "show", "channels", NULL }, handle_chanlist, "Display information on channels", chanlist_help, complete_show_channels },
{ { "show", "modules", NULL }, handle_modlist, "List modules and info", modlist_help },
{ { "show", "modules", "like", NULL }, handle_modlist, "List modules and info", modlist_help, complete_mod_4 },
{ { "show", "uptime", NULL }, handle_showuptime, "Show uptime information", uptime_help },
{ { "show", "version", NULL }, handle_version, "Display version info", version_help },
{ { "soft", "hangup", NULL }, handle_softhangup, "Request a hangup on a given channel", softhangup_help, complete_ch_3 },
{ { "unload", NULL }, handle_unload, "Unload a dynamic module by name", unload_help, complete_fn },
{ { NULL }, NULL, NULL, NULL }
};
/*! \brief initialize the _full_cmd string in * each of the builtins. */
void ast_builtins_init(void)
struct ast_cli_entry *e;
for (e = builtins; e->cmda[0] != NULL; e++) {
char buf[80];
ast_join(buf, sizeof(buf), e->cmda);
e->_full_cmd = strdup(buf);
if (!e->_full_cmd)
ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
/*
* We have two sets of commands: builtins are stored in a
* NULL-terminated array of ast_cli_entry, whereas external
* commands are in a list.
* When navigating, we need to keep two pointers and get
* the next one in lexicographic order. For the purpose,
* we use a structure.
*/
struct cli_iterator {
struct ast_cli_entry *builtins;
struct ast_cli_entry *helpers;
};
static struct ast_cli_entry *cli_next(struct cli_iterator *i)
struct ast_cli_entry *e;
Russell Bryant
committed
if (i->builtins == NULL && i->helpers == NULL) {
/* initialize */
i->builtins = builtins;
i->helpers = AST_LIST_FIRST(&helpers);
}
e = i->builtins; /* temporary */
if (!e->cmda[0] || (i->helpers &&
strcmp(i->helpers->_full_cmd, e->_full_cmd) < 0)) {
/* Use helpers */
e = i->helpers;
if (e)
i->helpers = AST_LIST_NEXT(e, list);
} else { /* use builtin. e is already set */
(i->builtins)++; /* move to next */
}
return e;
/*!
* \brief locate a cli command in the 'helpers' list (which must be locked).
* exact has 3 values: