1999-12-19 22:38:55 +00:00
|
|
|
/*
|
2005-09-14 20:46:50 +00:00
|
|
|
* Asterisk -- An open source telephony toolkit.
|
1999-12-19 22:38:55 +00:00
|
|
|
*
|
2006-02-14 22:44:20 +00:00
|
|
|
* Copyright (C) 1999 - 2006, Digium, Inc.
|
1999-12-19 22:38:55 +00:00
|
|
|
*
|
2004-09-03 03:44:35 +00:00
|
|
|
* Mark Spencer <markster@digium.com>
|
1999-12-19 22:38:55 +00:00
|
|
|
*
|
2005-09-14 20:46:50 +00:00
|
|
|
* 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.
|
|
|
|
*
|
1999-12-19 22:38:55 +00:00
|
|
|
* This program is free software, distributed under the terms of
|
2005-09-14 20:46:50 +00:00
|
|
|
* the GNU General Public License Version 2. See the LICENSE file
|
|
|
|
* at the top of the source tree.
|
|
|
|
*/
|
|
|
|
|
2005-10-24 20:12:06 +00:00
|
|
|
/*! \file
|
2005-09-14 20:46:50 +00:00
|
|
|
*
|
2005-10-24 20:12:06 +00:00
|
|
|
* \brief Standard Command Line Interface
|
2005-12-30 21:18:06 +00:00
|
|
|
*
|
|
|
|
* \author Mark Spencer <markster@digium.com>
|
1999-12-19 22:38:55 +00:00
|
|
|
*/
|
|
|
|
|
2006-06-07 18:54:56 +00:00
|
|
|
#include "asterisk.h"
|
|
|
|
|
|
|
|
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
2005-04-22 13:11:34 +00:00
|
|
|
#include <sys/signal.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
This is the first round of removing applications that were marked as deprecated
in the 1.2 release. They are being removed from the trunk and will not be in
the next major release. The following is a list of the applications that are
being removed in this commit:
Curl, Cut, Sort, DBPut, DBGet, ENUMLookup, Eval
GetGroupCount, SetGroup, CheckGroup, GetGroupMatchCount
MD5, MD5Check, Math, SetCIDName, SetCIDNum, SetRDNIS, SetCallerID
TXTCIDName, AbsoluteTimeout, DigitTimeout, ResponseTimeout, SetAccount
SetLanguage, SetVar (renamed to Set)
These changes also include moving the "group show channels" cli command from
app_groupcount.c to cli.c.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@7379 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2005-12-07 15:36:55 +00:00
|
|
|
#include <regex.h>
|
2005-04-22 13:11:34 +00:00
|
|
|
|
2005-04-21 06:02:45 +00:00
|
|
|
#include "asterisk/logger.h"
|
|
|
|
#include "asterisk/options.h"
|
|
|
|
#include "asterisk/cli.h"
|
2006-03-28 22:25:08 +00:00
|
|
|
#include "asterisk/linkedlists.h"
|
2006-04-14 14:08:19 +00:00
|
|
|
#define MOD_LOADER
|
2005-04-21 06:02:45 +00:00
|
|
|
#include "asterisk/module.h"
|
|
|
|
#include "asterisk/pbx.h"
|
|
|
|
#include "asterisk/channel.h"
|
|
|
|
#include "asterisk/utils.h"
|
This is the first round of removing applications that were marked as deprecated
in the 1.2 release. They are being removed from the trunk and will not be in
the next major release. The following is a list of the applications that are
being removed in this commit:
Curl, Cut, Sort, DBPut, DBGet, ENUMLookup, Eval
GetGroupCount, SetGroup, CheckGroup, GetGroupMatchCount
MD5, MD5Check, Math, SetCIDName, SetCIDNum, SetRDNIS, SetCallerID
TXTCIDName, AbsoluteTimeout, DigitTimeout, ResponseTimeout, SetAccount
SetLanguage, SetVar (renamed to Set)
These changes also include moving the "group show channels" cli command from
app_groupcount.c to cli.c.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@7379 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2005-12-07 15:36:55 +00:00
|
|
|
#include "asterisk/app.h"
|
2005-04-21 06:02:45 +00:00
|
|
|
#include "asterisk/lock.h"
|
2003-01-30 15:03:20 +00:00
|
|
|
#include "editline/readline/readline.h"
|
2005-11-05 18:58:27 +00:00
|
|
|
|
2004-11-06 21:33:01 +00:00
|
|
|
extern unsigned long global_fin, global_fout;
|
2006-07-23 15:19:16 +00:00
|
|
|
|
|
|
|
static pthread_key_t ast_cli_buf_key;
|
|
|
|
static pthread_once_t ast_cli_buf_once = PTHREAD_ONCE_INIT;
|
|
|
|
|
2006-07-26 07:48:48 +00:00
|
|
|
/*! \brief Initial buffer size for resulting strings in ast_cli() */
|
|
|
|
#define AST_CLI_MAXSTRLEN 256
|
2006-07-23 15:19:16 +00:00
|
|
|
|
2006-07-27 01:49:57 +00:00
|
|
|
#ifdef __AST_DEBUG_MALLOC
|
|
|
|
static void FREE(void *ptr)
|
|
|
|
{
|
|
|
|
free(ptr);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define FREE free
|
|
|
|
#endif
|
|
|
|
|
2006-07-23 15:19:16 +00:00
|
|
|
static void ast_cli_buf_key_create(void)
|
|
|
|
{
|
2006-07-27 01:49:57 +00:00
|
|
|
pthread_key_create(&ast_cli_buf_key, FREE);
|
2006-07-23 15:19:16 +00:00
|
|
|
}
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
void ast_cli(int fd, char *fmt, ...)
|
|
|
|
{
|
2006-07-26 07:48:48 +00:00
|
|
|
struct {
|
|
|
|
size_t len;
|
|
|
|
char str[0];
|
|
|
|
} *buf;
|
2006-02-09 16:59:50 +00:00
|
|
|
int res;
|
1999-12-19 22:38:55 +00:00
|
|
|
va_list ap;
|
2005-07-15 22:06:15 +00:00
|
|
|
|
2006-07-23 15:19:16 +00:00
|
|
|
pthread_once(&ast_cli_buf_once, ast_cli_buf_key_create);
|
|
|
|
if (!(buf = pthread_getspecific(ast_cli_buf_key))) {
|
2006-07-26 07:48:48 +00:00
|
|
|
if (!(buf = ast_malloc(AST_CLI_MAXSTRLEN + sizeof(*buf))))
|
2006-07-23 15:19:16 +00:00
|
|
|
return;
|
2006-07-26 07:48:48 +00:00
|
|
|
buf->len = AST_CLI_MAXSTRLEN;
|
2006-07-23 15:19:16 +00:00
|
|
|
pthread_setspecific(ast_cli_buf_key, buf);
|
|
|
|
}
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
va_start(ap, fmt);
|
2006-07-26 07:48:48 +00:00
|
|
|
res = vsnprintf(buf->str, buf->len, fmt, ap);
|
|
|
|
while (res >= buf->len) {
|
|
|
|
if (!(buf = ast_realloc(buf, (buf->len * 2) + sizeof(*buf)))) {
|
|
|
|
va_end(ap);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
buf->len *= 2;
|
|
|
|
pthread_setspecific(ast_cli_buf_key, buf);
|
2006-07-27 01:29:57 +00:00
|
|
|
va_end(ap);
|
|
|
|
va_start(ap, fmt);
|
2006-07-26 07:48:48 +00:00
|
|
|
res = vsnprintf(buf->str, buf->len, fmt, ap);
|
|
|
|
}
|
1999-12-19 22:38:55 +00:00
|
|
|
va_end(ap);
|
2006-07-23 15:19:16 +00:00
|
|
|
|
|
|
|
if (res > 0)
|
2006-07-26 07:48:48 +00:00
|
|
|
ast_carefulwrite(fd, buf->str, strlen(buf->str), 100);
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
|
|
|
|
2006-02-14 22:44:20 +00:00
|
|
|
static AST_LIST_HEAD_STATIC(helpers, ast_cli_entry);
|
1999-12-19 22:38:55 +00:00
|
|
|
|
|
|
|
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"
|
2005-08-08 01:45:29 +00:00
|
|
|
" Unloads the specified module from Asterisk. The -f\n"
|
1999-12-19 22:38:55 +00:00
|
|
|
" 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"
|
2005-08-08 01:45:29 +00:00
|
|
|
" information on the given command. If called without a\n"
|
1999-12-19 22:38:55 +00:00
|
|
|
" topic, it provides a list of commands.\n";
|
|
|
|
|
|
|
|
static char chanlist_help[] =
|
2005-08-08 01:45:29 +00:00
|
|
|
"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";
|
1999-12-19 22:38:55 +00:00
|
|
|
|
2001-05-09 03:11:22 +00:00
|
|
|
static char reload_help[] =
|
2004-09-03 14:02:12 +00:00
|
|
|
"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";
|
2001-05-09 03:11:22 +00:00
|
|
|
|
|
|
|
static char set_verbose_help[] =
|
|
|
|
"Usage: set verbose <level>\n"
|
|
|
|
" Sets level of verbose messages to be displayed. 0 means\n"
|
2004-09-13 18:19:15 +00:00
|
|
|
" 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";
|
2001-05-09 03:11:22 +00:00
|
|
|
|
2006-05-26 19:48:17 +00:00
|
|
|
static char logger_mute_help[] =
|
|
|
|
"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";
|
|
|
|
|
2001-05-09 03:11:22 +00:00
|
|
|
static char softhangup_help[] =
|
|
|
|
"Usage: soft hangup <channel>\n"
|
2005-08-08 01:45:29 +00:00
|
|
|
" Request that a channel be hung up. The hangup takes effect\n"
|
2001-05-09 03:11:22 +00:00
|
|
|
" the next time the driver reads or writes from the channel\n";
|
|
|
|
|
This is the first round of removing applications that were marked as deprecated
in the 1.2 release. They are being removed from the trunk and will not be in
the next major release. The following is a list of the applications that are
being removed in this commit:
Curl, Cut, Sort, DBPut, DBGet, ENUMLookup, Eval
GetGroupCount, SetGroup, CheckGroup, GetGroupMatchCount
MD5, MD5Check, Math, SetCIDName, SetCIDNum, SetRDNIS, SetCallerID
TXTCIDName, AbsoluteTimeout, DigitTimeout, ResponseTimeout, SetAccount
SetLanguage, SetVar (renamed to Set)
These changes also include moving the "group show channels" cli command from
app_groupcount.c to cli.c.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@7379 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2005-12-07 15:36:55 +00:00
|
|
|
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";
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2001-05-09 03:11:22 +00:00
|
|
|
static int handle_reload(int fd, int argc, char *argv[])
|
|
|
|
{
|
2004-09-02 20:45:24 +00:00
|
|
|
int x;
|
2004-10-16 21:14:05 +00:00
|
|
|
int res;
|
2004-09-02 20:45:24 +00:00
|
|
|
if (argc < 1)
|
2001-05-09 03:11:22 +00:00
|
|
|
return RESULT_SHOWUSAGE;
|
2004-09-02 20:45:24 +00:00
|
|
|
if (argc > 1) {
|
2004-10-16 21:14:05 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2004-09-02 20:45:24 +00:00
|
|
|
} else
|
|
|
|
ast_module_reload(NULL);
|
2001-05-09 03:11:22 +00:00
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int handle_set_verbose(int fd, int argc, char *argv[])
|
|
|
|
{
|
2004-09-13 18:19:15 +00:00
|
|
|
int val = 0;
|
2006-03-28 22:25:08 +00:00
|
|
|
int oldval = option_verbose;
|
2004-12-29 07:46:10 +00:00
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
/* "set verbose [atleast] N" */
|
2001-05-09 03:11:22 +00:00
|
|
|
if (argc == 3)
|
|
|
|
option_verbose = atoi(argv[2]);
|
2006-03-28 22:25:08 +00:00
|
|
|
else if (argc == 4) {
|
|
|
|
if (strcasecmp(argv[2], "atleast"))
|
|
|
|
return RESULT_SHOWUSAGE;
|
2001-05-09 03:11:22 +00:00
|
|
|
val = atoi(argv[3]);
|
|
|
|
if (val > option_verbose)
|
|
|
|
option_verbose = val;
|
2006-03-28 22:25:08 +00:00
|
|
|
} else
|
|
|
|
return RESULT_SHOWUSAGE;
|
2004-09-13 18:19:15 +00:00
|
|
|
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)
|
2004-12-29 07:46:10 +00:00
|
|
|
ast_cli(fd, "Verbosity is at least %d\n", option_verbose);
|
2004-10-08 04:23:22 +00:00
|
|
|
else if (oldval > 0 && option_verbose == 0)
|
2004-09-13 18:19:15 +00:00
|
|
|
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;
|
2006-03-28 22:25:08 +00:00
|
|
|
int oldval = option_debug;
|
|
|
|
|
|
|
|
/* "set debug [atleast] N" */
|
2004-09-13 18:19:15 +00:00
|
|
|
if (argc == 3)
|
|
|
|
option_debug = atoi(argv[2]);
|
2006-03-28 22:25:08 +00:00
|
|
|
else if (argc == 4) {
|
|
|
|
if (strcasecmp(argv[2], "atleast"))
|
|
|
|
return RESULT_SHOWUSAGE;
|
2004-09-13 18:19:15 +00:00
|
|
|
val = atoi(argv[3]);
|
|
|
|
if (val > option_debug)
|
|
|
|
option_debug = val;
|
2006-03-28 22:25:08 +00:00
|
|
|
} else
|
|
|
|
return RESULT_SHOWUSAGE;
|
2004-09-13 18:19:15 +00:00
|
|
|
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)
|
2004-12-29 07:46:10 +00:00
|
|
|
ast_cli(fd, "Core debug is at least %d\n", option_debug);
|
2004-09-13 18:19:15 +00:00
|
|
|
else if (oldval > 0 && option_debug == 0)
|
|
|
|
ast_cli(fd, "Core debug is now OFF\n");
|
2001-05-09 03:11:22 +00:00
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2006-05-26 19:48:17 +00:00
|
|
|
static int handle_logger_mute(int fd, int argc, char *argv[])
|
|
|
|
{
|
|
|
|
if (argc != 2)
|
|
|
|
return RESULT_SHOWUSAGE;
|
2006-05-26 21:47:52 +00:00
|
|
|
ast_console_toggle_mute(fd);
|
2006-05-26 19:48:17 +00:00
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2005-06-06 20:27:51 +00:00
|
|
|
#define MODLIST_FORMAT "%-30s %-40.40s %-10d\n"
|
|
|
|
#define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
|
1999-12-19 22:38:55 +00:00
|
|
|
|
2004-06-09 01:45:08 +00:00
|
|
|
AST_MUTEX_DEFINE_STATIC(climodentrylock);
|
1999-12-19 22:38:55 +00:00
|
|
|
static int climodentryfd = -1;
|
|
|
|
|
2005-06-06 20:27:51 +00:00
|
|
|
static int modlist_modentry(const char *module, const char *description, int usecnt, const char *like)
|
1999-12-19 22:38:55 +00:00
|
|
|
{
|
2004-10-14 04:38:29 +00:00
|
|
|
/* Comparing the like with the module */
|
2005-08-22 18:43:32 +00:00
|
|
|
if (strcasestr(module, like) ) {
|
2005-06-06 20:27:51 +00:00
|
|
|
ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
|
2004-10-14 04:38:29 +00:00
|
|
|
return 1;
|
|
|
|
}
|
1999-12-19 22:38:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char modlist_help[] =
|
2004-10-14 04:38:29 +00:00
|
|
|
"Usage: show modules [like keyword]\n"
|
|
|
|
" Shows Asterisk modules currently in use, and usage statistics.\n";
|
1999-12-19 22:38:55 +00:00
|
|
|
|
2005-02-05 16:49:14 +00:00
|
|
|
static char uptime_help[] =
|
|
|
|
"Usage: show uptime [seconds]\n"
|
|
|
|
" Shows Asterisk uptime information.\n"
|
|
|
|
" The seconds word returns the uptime in seconds only.\n";
|
2001-05-09 03:11:22 +00:00
|
|
|
|
2006-01-18 00:05:09 +00:00
|
|
|
static void print_uptimestr(int fd, time_t timeval, const char *prefix, int printsec)
|
2003-05-02 15:37:34 +00:00
|
|
|
{
|
2006-01-18 00:05:09 +00:00
|
|
|
int x; /* the main part - years, weeks, etc. */
|
|
|
|
char timestr[256]="", *s = timestr;
|
|
|
|
size_t maxbytes = sizeof(timestr);
|
|
|
|
|
2003-05-02 15:37:34 +00:00
|
|
|
#define SECOND (1)
|
2004-06-22 17:42:14 +00:00
|
|
|
#define MINUTE (SECOND*60)
|
|
|
|
#define HOUR (MINUTE*60)
|
2003-05-02 15:37:34 +00:00
|
|
|
#define DAY (HOUR*24)
|
|
|
|
#define WEEK (DAY*7)
|
|
|
|
#define YEAR (DAY*365)
|
2006-01-18 00:05:09 +00:00
|
|
|
#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 */
|
|
|
|
}
|
2003-05-02 15:37:34 +00:00
|
|
|
if (timeval > YEAR) {
|
2006-01-18 00:05:09 +00:00
|
|
|
x = (timeval / YEAR);
|
|
|
|
timeval -= (x * YEAR);
|
|
|
|
ast_build_string(&s, &maxbytes, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval));
|
2003-05-02 15:37:34 +00:00
|
|
|
}
|
|
|
|
if (timeval > WEEK) {
|
2006-01-18 00:05:09 +00:00
|
|
|
x = (timeval / WEEK);
|
|
|
|
timeval -= (x * WEEK);
|
|
|
|
ast_build_string(&s, &maxbytes, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval));
|
2003-05-02 15:37:34 +00:00
|
|
|
}
|
|
|
|
if (timeval > DAY) {
|
2006-01-18 00:05:09 +00:00
|
|
|
x = (timeval / DAY);
|
|
|
|
timeval -= (x * DAY);
|
|
|
|
ast_build_string(&s, &maxbytes, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval));
|
2003-05-02 15:37:34 +00:00
|
|
|
}
|
|
|
|
if (timeval > HOUR) {
|
2006-01-18 00:05:09 +00:00
|
|
|
x = (timeval / HOUR);
|
|
|
|
timeval -= (x * HOUR);
|
|
|
|
ast_build_string(&s, &maxbytes, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval));
|
2003-05-02 15:37:34 +00:00
|
|
|
}
|
2004-06-22 17:42:14 +00:00
|
|
|
if (timeval > MINUTE) {
|
2006-01-18 00:05:09 +00:00
|
|
|
x = (timeval / MINUTE);
|
|
|
|
timeval -= (x * MINUTE);
|
|
|
|
ast_build_string(&s, &maxbytes, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval));
|
2003-05-02 15:37:34 +00:00
|
|
|
}
|
2006-01-18 00:05:09 +00:00
|
|
|
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);
|
2003-05-02 15:37:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int handle_showuptime(int fd, int argc, char *argv[])
|
|
|
|
{
|
2006-01-18 00:05:09 +00:00
|
|
|
/* 'show uptime [seconds]' */
|
|
|
|
time_t curtime = time(NULL);
|
|
|
|
int printsec = (argc == 3 && !strcasecmp(argv[2],"seconds"));
|
2005-02-05 16:49:14 +00:00
|
|
|
|
2006-01-18 00:05:09 +00:00
|
|
|
if (argc != 2 && !printsec)
|
2005-02-05 16:49:14 +00:00
|
|
|
return RESULT_SHOWUSAGE;
|
2006-01-18 00:05:09 +00:00
|
|
|
if (ast_startuptime)
|
|
|
|
print_uptimestr(fd, curtime - ast_startuptime, "System uptime", printsec);
|
|
|
|
if (ast_lastreloadtime)
|
|
|
|
print_uptimestr(fd, curtime - ast_lastreloadtime, "Last reload", printsec);
|
2003-05-02 15:37:34 +00:00
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
static int handle_modlist(int fd, int argc, char *argv[])
|
|
|
|
{
|
2004-10-14 04:38:29 +00:00
|
|
|
char *like = "";
|
|
|
|
if (argc == 3)
|
1999-12-19 22:38:55 +00:00
|
|
|
return RESULT_SHOWUSAGE;
|
2004-10-14 04:38:29 +00:00
|
|
|
else if (argc >= 4) {
|
2005-03-23 05:56:32 +00:00
|
|
|
if (strcmp(argv[2],"like"))
|
2004-10-14 04:38:29 +00:00
|
|
|
return RESULT_SHOWUSAGE;
|
|
|
|
like = argv[3];
|
|
|
|
}
|
|
|
|
|
2003-08-13 15:25:16 +00:00
|
|
|
ast_mutex_lock(&climodentrylock);
|
2006-03-28 22:25:08 +00:00
|
|
|
climodentryfd = fd; /* global, protected by climodentrylock */
|
2005-06-06 20:27:51 +00:00
|
|
|
ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
|
2005-06-06 18:31:29 +00:00
|
|
|
ast_cli(fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
|
1999-12-19 22:38:55 +00:00
|
|
|
climodentryfd = -1;
|
2003-08-13 15:25:16 +00:00
|
|
|
ast_mutex_unlock(&climodentrylock);
|
1999-12-19 22:38:55 +00:00
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
2005-08-08 01:45:29 +00:00
|
|
|
#undef MODLIST_FORMAT
|
|
|
|
#undef MODLIST_FORMAT2
|
1999-12-19 22:38:55 +00:00
|
|
|
|
|
|
|
static int handle_chanlist(int fd, int argc, char *argv[])
|
|
|
|
{
|
2005-08-08 01:45:29 +00:00
|
|
|
#define FORMAT_STRING "%-20.20s %-20.20s %-7.7s %-30.30s\n"
|
|
|
|
#define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
|
2006-02-14 23:22:52 +00:00
|
|
|
#define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s\n"
|
2005-08-08 01:45:29 +00:00
|
|
|
#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"
|
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
struct ast_channel *c = NULL;
|
2005-08-08 01:45:29 +00:00
|
|
|
char durbuf[10] = "-";
|
|
|
|
char locbuf[40];
|
|
|
|
char appdata[40];
|
|
|
|
int duration;
|
|
|
|
int durh, durm, durs;
|
|
|
|
int numchans = 0, concise = 0, verbose = 0;
|
2004-06-11 00:18:30 +00:00
|
|
|
|
|
|
|
concise = (argc == 3 && (!strcasecmp(argv[2],"concise")));
|
2005-08-08 01:45:29 +00:00
|
|
|
verbose = (argc == 3 && (!strcasecmp(argv[2],"verbose")));
|
2004-06-11 00:18:30 +00:00
|
|
|
|
2005-08-08 01:45:29 +00:00
|
|
|
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");
|
2006-03-28 22:25:08 +00:00
|
|
|
|
2005-08-08 01:45:29 +00:00
|
|
|
while ((c = ast_channel_walk_locked(c)) != NULL) {
|
2006-03-28 22:25:08 +00:00
|
|
|
struct ast_channel *bc = ast_bridged_channel(c);
|
2005-08-08 01:45:29 +00:00
|
|
|
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),
|
2006-05-09 06:14:30 +00:00
|
|
|
c->appl ? c->appl : "(None)",
|
|
|
|
S_OR(c->data, ""), /* XXX different from verbose ? */
|
2006-04-21 10:00:58 +00:00
|
|
|
S_OR(c->cid.cid_num, ""),
|
2006-05-09 06:14:30 +00:00
|
|
|
S_OR(c->accountcode, ""),
|
|
|
|
c->amaflags,
|
|
|
|
durbuf,
|
|
|
|
bc ? bc->name : "(None)");
|
2005-08-08 01:45:29 +00:00
|
|
|
} else if (verbose) {
|
|
|
|
ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
|
2006-05-09 06:14:30 +00:00
|
|
|
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)");
|
2005-08-08 01:45:29 +00:00
|
|
|
} 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)");
|
2006-03-28 22:25:08 +00:00
|
|
|
if (c->appl)
|
2005-08-08 01:45:29 +00:00
|
|
|
snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, c->data ? c->data : "");
|
2006-03-28 22:25:08 +00:00
|
|
|
else
|
2005-08-08 01:45:29 +00:00
|
|
|
strcpy(appdata, "(None)");
|
|
|
|
ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
|
|
|
|
}
|
2003-02-12 13:59:15 +00:00
|
|
|
numchans++;
|
2006-04-29 14:50:18 +00:00
|
|
|
ast_channel_unlock(c);
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
2005-08-08 01:45:29 +00:00
|
|
|
if (!concise) {
|
2006-03-28 22:25:08 +00:00
|
|
|
ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
|
2005-05-18 01:49:13 +00:00
|
|
|
if (option_maxcalls)
|
2006-03-28 22:25:08 +00:00
|
|
|
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);
|
2005-05-18 01:49:13 +00:00
|
|
|
else
|
2006-03-28 22:25:08 +00:00
|
|
|
ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
|
2005-05-18 01:49:13 +00:00
|
|
|
}
|
1999-12-19 22:38:55 +00:00
|
|
|
return RESULT_SUCCESS;
|
2005-08-08 01:45:29 +00:00
|
|
|
|
|
|
|
#undef FORMAT_STRING
|
|
|
|
#undef FORMAT_STRING2
|
|
|
|
#undef CONCISE_FORMAT_STRING
|
|
|
|
#undef VERBOSE_FORMAT_STRING
|
|
|
|
#undef VERBOSE_FORMAT_STRING2
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char showchan_help[] =
|
|
|
|
"Usage: show channel <channel>\n"
|
|
|
|
" Shows lots of information about the specified channel.\n";
|
|
|
|
|
2003-03-12 06:00:18 +00:00
|
|
|
static char debugchan_help[] =
|
|
|
|
"Usage: debug channel <channel>\n"
|
|
|
|
" Enables debugging on a specific channel.\n";
|
|
|
|
|
2005-03-05 04:04:55 +00:00
|
|
|
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";
|
|
|
|
|
2003-03-12 06:00:18 +00:00
|
|
|
static char nodebugchan_help[] =
|
|
|
|
"Usage: no debug channel <channel>\n"
|
|
|
|
" Disables debugging on a specific channel.\n";
|
|
|
|
|
2001-05-09 03:11:22 +00:00
|
|
|
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";
|
|
|
|
|
2003-01-30 15:03:20 +00:00
|
|
|
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";
|
|
|
|
|
2001-05-09 03:11:22 +00:00
|
|
|
static int handle_softhangup(int fd, int argc, char *argv[])
|
|
|
|
{
|
|
|
|
struct ast_channel *c=NULL;
|
|
|
|
if (argc != 3)
|
|
|
|
return RESULT_SHOWUSAGE;
|
2005-06-06 02:29:18 +00:00
|
|
|
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);
|
2006-04-29 14:50:18 +00:00
|
|
|
ast_channel_unlock(c);
|
2005-06-06 02:29:18 +00:00
|
|
|
} else
|
2001-05-09 03:11:22 +00:00
|
|
|
ast_cli(fd, "%s is not a known channel\n", argv[2]);
|
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2006-01-18 22:17:31 +00:00
|
|
|
static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
|
2001-05-09 03:11:22 +00:00
|
|
|
|
2003-01-30 15:03:20 +00:00
|
|
|
static int handle_commandmatchesarray(int fd, int argc, char *argv[])
|
|
|
|
{
|
2004-10-14 04:12:05 +00:00
|
|
|
char *buf, *obuf;
|
2004-04-06 07:42:01 +00:00
|
|
|
int buflen = 2048;
|
2003-01-30 15:03:20 +00:00
|
|
|
int len = 0;
|
|
|
|
char **matches;
|
2004-10-14 04:12:05 +00:00
|
|
|
int x, matchlen;
|
2003-01-30 15:03:20 +00:00
|
|
|
|
|
|
|
if (argc != 4)
|
|
|
|
return RESULT_SHOWUSAGE;
|
2006-02-09 16:59:50 +00:00
|
|
|
if (!(buf = ast_malloc(buflen)))
|
2004-04-06 07:42:01 +00:00
|
|
|
return RESULT_FAILURE;
|
2003-01-30 15:03:20 +00:00
|
|
|
buf[len] = '\0';
|
|
|
|
matches = ast_cli_completion_matches(argv[2], argv[3]);
|
|
|
|
if (matches) {
|
|
|
|
for (x=0; matches[x]; x++) {
|
2004-10-14 04:12:05 +00:00
|
|
|
matchlen = strlen(matches[x]) + 1;
|
|
|
|
if (len + matchlen >= buflen) {
|
|
|
|
buflen += matchlen * 3;
|
|
|
|
obuf = buf;
|
2006-02-09 16:59:50 +00:00
|
|
|
if (!(buf = ast_realloc(obuf, buflen)))
|
|
|
|
/* Memory allocation failure... Just free old buffer and be done */
|
2004-10-14 04:12:05 +00:00
|
|
|
free(obuf);
|
2004-04-06 07:42:01 +00:00
|
|
|
}
|
2004-10-14 04:12:05 +00:00
|
|
|
if (buf)
|
|
|
|
len += sprintf( buf + len, "%s ", matches[x]);
|
2003-04-08 13:45:36 +00:00
|
|
|
free(matches[x]);
|
|
|
|
matches[x] = NULL;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
2003-04-08 13:45:36 +00:00
|
|
|
free(matches);
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
2006-03-28 22:25:08 +00:00
|
|
|
|
2003-01-30 15:03:20 +00:00
|
|
|
if (buf) {
|
2004-04-06 07:42:01 +00:00
|
|
|
ast_cli(fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
|
|
|
|
free(buf);
|
2003-01-30 15:03:20 +00:00
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
2001-05-09 03:11:22 +00:00
|
|
|
static int handle_commandcomplete(int fd, int argc, char *argv[])
|
|
|
|
{
|
|
|
|
char *buf;
|
2006-03-28 22:25:08 +00:00
|
|
|
|
2001-05-09 03:11:22 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2005-03-05 04:04:55 +00:00
|
|
|
static int handle_debuglevel(int fd, int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int newlevel;
|
|
|
|
char *filename = "<any>";
|
|
|
|
if ((argc < 3) || (argc > 4))
|
|
|
|
return RESULT_SHOWUSAGE;
|
2005-04-29 17:00:33 +00:00
|
|
|
if (sscanf(argv[2], "%d", &newlevel) != 1)
|
2005-03-05 04:04:55 +00:00
|
|
|
return RESULT_SHOWUSAGE;
|
|
|
|
option_debug = newlevel;
|
|
|
|
if (argc == 4) {
|
|
|
|
filename = argv[3];
|
2005-07-10 22:56:21 +00:00
|
|
|
ast_copy_string(debug_filename, filename, sizeof(debug_filename));
|
2005-03-05 04:04:55 +00:00
|
|
|
} else {
|
|
|
|
debug_filename[0] = '\0';
|
|
|
|
}
|
|
|
|
ast_cli(fd, "Debugging level set to %d, file '%s'\n", newlevel, filename);
|
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2005-06-06 02:29:18 +00:00
|
|
|
/* XXX todo: merge next two functions!!! */
|
2003-03-12 06:00:18 +00:00
|
|
|
static int handle_debugchan(int fd, int argc, char *argv[])
|
|
|
|
{
|
|
|
|
struct ast_channel *c=NULL;
|
2004-11-06 21:33:01 +00:00
|
|
|
int is_all;
|
2006-03-28 22:25:08 +00:00
|
|
|
|
|
|
|
/* 'debug channel {all|chan_id}' */
|
2003-03-12 06:00:18 +00:00
|
|
|
if (argc != 3)
|
|
|
|
return RESULT_SHOWUSAGE;
|
2004-11-06 21:33:01 +00:00
|
|
|
|
|
|
|
is_all = !strcasecmp("all", argv[2]);
|
|
|
|
if (is_all) {
|
2005-06-06 02:29:18 +00:00
|
|
|
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]);
|
2004-11-06 21:33:01 +00:00
|
|
|
}
|
2005-11-29 04:25:28 +00:00
|
|
|
while (c) {
|
2005-06-06 02:29:18 +00:00
|
|
|
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);
|
2003-03-12 06:00:18 +00:00
|
|
|
}
|
2006-04-29 14:50:18 +00:00
|
|
|
ast_channel_unlock(c);
|
2005-06-06 02:29:18 +00:00
|
|
|
if (!is_all)
|
|
|
|
break;
|
2004-05-20 16:30:10 +00:00
|
|
|
c = ast_channel_walk_locked(c);
|
2003-03-12 06:00:18 +00:00
|
|
|
}
|
2005-06-06 02:29:18 +00:00
|
|
|
ast_cli(fd, "Debugging on new channels is enabled\n");
|
2003-03-12 06:00:18 +00:00
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int handle_nodebugchan(int fd, int argc, char *argv[])
|
|
|
|
{
|
|
|
|
struct ast_channel *c=NULL;
|
2004-11-06 21:33:01 +00:00
|
|
|
int is_all;
|
2006-03-28 22:25:08 +00:00
|
|
|
/* 'no debug channel {all|chan_id}' */
|
2003-03-12 06:00:18 +00:00
|
|
|
if (argc != 4)
|
|
|
|
return RESULT_SHOWUSAGE;
|
2004-11-06 21:33:01 +00:00
|
|
|
is_all = !strcasecmp("all", argv[3]);
|
|
|
|
if (is_all) {
|
2005-06-06 02:29:18 +00:00
|
|
|
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]);
|
2006-03-28 22:25:08 +00:00
|
|
|
}
|
2003-03-12 06:00:18 +00:00
|
|
|
while(c) {
|
2005-06-06 02:29:18 +00:00
|
|
|
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);
|
2003-03-12 06:00:18 +00:00
|
|
|
}
|
2006-04-29 14:50:18 +00:00
|
|
|
ast_channel_unlock(c);
|
2005-06-06 02:29:18 +00:00
|
|
|
if (!is_all)
|
|
|
|
break;
|
2004-05-20 16:30:10 +00:00
|
|
|
c = ast_channel_walk_locked(c);
|
2003-03-12 06:00:18 +00:00
|
|
|
}
|
2005-06-06 02:29:18 +00:00
|
|
|
ast_cli(fd, "Debugging on new channels is disabled\n");
|
2003-03-12 06:00:18 +00:00
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
static int handle_showchan(int fd, int argc, char *argv[])
|
|
|
|
{
|
|
|
|
struct ast_channel *c=NULL;
|
2004-08-03 17:48:18 +00:00
|
|
|
struct timeval now;
|
2005-02-23 22:48:47 +00:00
|
|
|
char buf[2048];
|
2004-12-26 11:08:34 +00:00
|
|
|
char cdrtime[256];
|
2006-01-07 17:54:22 +00:00
|
|
|
char nf[256], wf[256], rf[256];
|
2004-08-03 17:48:18 +00:00
|
|
|
long elapsed_seconds=0;
|
2004-09-03 03:44:35 +00:00
|
|
|
int hour=0, min=0, sec=0;
|
2005-02-23 22:48:47 +00:00
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
if (argc != 3)
|
|
|
|
return RESULT_SHOWUSAGE;
|
2005-07-15 23:00:47 +00:00
|
|
|
now = ast_tvnow();
|
2005-06-06 02:29:18 +00:00
|
|
|
c = ast_get_channel_by_name_locked(argv[2]);
|
|
|
|
if (!c) {
|
1999-12-19 22:38:55 +00:00
|
|
|
ast_cli(fd, "%s is not a known channel\n", argv[2]);
|
2005-06-06 02:29:18 +00:00
|
|
|
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
|
2005-07-10 22:56:21 +00:00
|
|
|
strcpy(cdrtime, "N/A");
|
2005-06-06 02:29:18 +00:00
|
|
|
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"
|
2006-01-07 17:54:22 +00:00
|
|
|
" NativeFormats: %s\n"
|
|
|
|
" WriteFormat: %s\n"
|
|
|
|
" ReadFormat: %s\n"
|
2006-06-04 19:38:05 +00:00
|
|
|
" WriteTranscode: %s\n"
|
|
|
|
" ReadTranscode: %s\n"
|
2005-06-06 02:29:18 +00:00
|
|
|
"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",
|
2006-02-01 23:05:28 +00:00
|
|
|
c->name, c->tech->type, c->uniqueid,
|
2006-04-21 10:00:58 +00:00
|
|
|
S_OR(c->cid.cid_num, "(N/A)"),
|
|
|
|
S_OR(c->cid.cid_name, "(N/A)"),
|
|
|
|
S_OR(c->cid.cid_dnid, "(N/A)"), ast_state2str(c->_state), c->_state, c->rings,
|
2006-01-07 17:54:22 +00:00
|
|
|
ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats),
|
|
|
|
ast_getformatname_multiple(wf, sizeof(wf), c->writeformat),
|
|
|
|
ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
|
2006-06-04 19:38:05 +00:00
|
|
|
c->writetrans ? "Yes" : "No",
|
|
|
|
c->readtrans ? "Yes" : "No",
|
2006-04-21 14:49:21 +00:00
|
|
|
c->fds[0],
|
|
|
|
c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
|
|
|
|
c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
|
|
|
|
(long)c->whentohangup,
|
2005-06-06 02:29:18 +00:00
|
|
|
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)" ),
|
2006-03-27 19:31:54 +00:00
|
|
|
( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
|
2005-06-06 02:29:18 +00:00
|
|
|
(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);
|
|
|
|
|
2006-04-29 14:50:18 +00:00
|
|
|
ast_channel_unlock(c);
|
1999-12-19 22:38:55 +00:00
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
/*
|
|
|
|
* 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)
|
2005-08-08 01:45:29 +00:00
|
|
|
{
|
2006-03-28 22:25:08 +00:00
|
|
|
int i, which = 0, len;
|
|
|
|
len = ast_strlen_zero(word) ? 0 : strlen(word);
|
2005-11-29 04:25:28 +00:00
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
for (i = 0; choices[i]; i++) {
|
|
|
|
if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
|
|
|
|
return ast_strdup(choices[i]);
|
2005-08-08 01:45:29 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2006-02-16 17:37:03 +00:00
|
|
|
char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
|
1999-12-19 22:38:55 +00:00
|
|
|
{
|
2005-06-06 02:29:18 +00:00
|
|
|
struct ast_channel *c = NULL;
|
2005-11-29 04:25:28 +00:00
|
|
|
int which = 0;
|
|
|
|
int wordlen;
|
2006-03-28 22:25:08 +00:00
|
|
|
char notfound = '\0';
|
|
|
|
char *ret = ¬found; /* so NULL can break the loop */
|
2005-06-06 02:29:18 +00:00
|
|
|
|
2004-08-07 19:27:54 +00:00
|
|
|
if (pos != rpos)
|
|
|
|
return NULL;
|
2005-11-29 04:25:28 +00:00
|
|
|
|
|
|
|
wordlen = strlen(word);
|
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
while (ret == ¬found && (c = ast_channel_walk_locked(c))) {
|
|
|
|
if (!strncasecmp(word, c->name, wordlen) && ++which > state)
|
|
|
|
ret = ast_strdup(c->name);
|
2006-04-29 14:50:18 +00:00
|
|
|
ast_channel_unlock(c);
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
2006-03-28 22:25:08 +00:00
|
|
|
return ret == ¬found ? NULL : ret;
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
|
|
|
|
2006-01-18 22:17:31 +00:00
|
|
|
static char *complete_ch_3(const char *line, const char *word, int pos, int state)
|
2004-08-07 19:27:54 +00:00
|
|
|
{
|
2006-02-16 17:37:03 +00:00
|
|
|
return ast_complete_channels(line, word, pos, state, 2);
|
2004-08-07 19:27:54 +00:00
|
|
|
}
|
|
|
|
|
2006-01-18 22:17:31 +00:00
|
|
|
static char *complete_ch_4(const char *line, const char *word, int pos, int state)
|
2004-08-07 19:27:54 +00:00
|
|
|
{
|
2006-02-16 17:37:03 +00:00
|
|
|
return ast_complete_channels(line, word, pos, state, 3);
|
2004-08-07 19:27:54 +00:00
|
|
|
}
|
|
|
|
|
2006-01-18 22:17:31 +00:00
|
|
|
static char *complete_mod_2(const char *line, const char *word, int pos, int state)
|
2004-10-16 21:14:05 +00:00
|
|
|
{
|
|
|
|
return ast_module_helper(line, word, pos, state, 1, 1);
|
|
|
|
}
|
|
|
|
|
2006-01-18 22:17:31 +00:00
|
|
|
static char *complete_mod_4(const char *line, const char *word, int pos, int state)
|
2004-10-16 21:14:05 +00:00
|
|
|
{
|
|
|
|
return ast_module_helper(line, word, pos, state, 3, 0);
|
|
|
|
}
|
|
|
|
|
2006-01-18 22:17:31 +00:00
|
|
|
static char *complete_fn(const char *line, const char *word, int pos, int state)
|
1999-12-19 22:38:55 +00:00
|
|
|
{
|
|
|
|
char *c;
|
|
|
|
char filename[256];
|
2005-11-29 04:25:28 +00:00
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
if (pos != 1)
|
|
|
|
return NULL;
|
2005-11-29 04:25:28 +00:00
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
if (word[0] == '/')
|
2005-07-10 22:56:21 +00:00
|
|
|
ast_copy_string(filename, word, sizeof(filename));
|
1999-12-19 22:38:55 +00:00
|
|
|
else
|
2005-11-29 04:25:28 +00:00
|
|
|
snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
|
|
|
|
|
|
|
|
c = filename_completion_function(filename, state);
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
if (c && word[0] != '/')
|
2005-11-29 04:25:28 +00:00
|
|
|
c += (strlen(ast_config_AST_MODULE_DIR) + 1);
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
return c ? strdup(c) : c;
|
|
|
|
}
|
|
|
|
|
This is the first round of removing applications that were marked as deprecated
in the 1.2 release. They are being removed from the trunk and will not be in
the next major release. The following is a list of the applications that are
being removed in this commit:
Curl, Cut, Sort, DBPut, DBGet, ENUMLookup, Eval
GetGroupCount, SetGroup, CheckGroup, GetGroupMatchCount
MD5, MD5Check, Math, SetCIDName, SetCIDNum, SetRDNIS, SetCallerID
TXTCIDName, AbsoluteTimeout, DigitTimeout, ResponseTimeout, SetAccount
SetLanguage, SetVar (renamed to Set)
These changes also include moving the "group show channels" cli command from
app_groupcount.c to cli.c.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@7379 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2005-12-07 15:36:55 +00:00
|
|
|
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++;
|
2006-04-29 14:50:18 +00:00
|
|
|
ast_channel_unlock(c);
|
This is the first round of removing applications that were marked as deprecated
in the 1.2 release. They are being removed from the trunk and will not be in
the next major release. The following is a list of the applications that are
being removed in this commit:
Curl, Cut, Sort, DBPut, DBGet, ENUMLookup, Eval
GetGroupCount, SetGroup, CheckGroup, GetGroupMatchCount
MD5, MD5Check, Math, SetCIDName, SetCIDNum, SetRDNIS, SetCallerID
TXTCIDName, AbsoluteTimeout, DigitTimeout, ResponseTimeout, SetAccount
SetLanguage, SetVar (renamed to Set)
These changes also include moving the "group show channels" cli command from
app_groupcount.c to cli.c.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@7379 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2005-12-07 15:36:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (havepattern)
|
|
|
|
regfree(®exbuf);
|
|
|
|
|
|
|
|
ast_cli(fd, "%d active channel%s\n", numchans, (numchans != 1) ? "s" : "");
|
|
|
|
return RESULT_SUCCESS;
|
|
|
|
#undef FORMAT_STRING
|
|
|
|
}
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
static int handle_help(int fd, int argc, char *argv[]);
|
|
|
|
|
2006-01-18 22:17:31 +00:00
|
|
|
static char * complete_help(const char *text, const char *word, int pos, int state)
|
2006-01-17 17:25:53 +00:00
|
|
|
{
|
|
|
|
/* 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 */
|
2006-03-28 22:25:08 +00:00
|
|
|
return __ast_cli_generator(text, word, state, 0);
|
2006-01-17 17:25:53 +00:00
|
|
|
}
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
static struct ast_cli_entry builtins[] = {
|
2004-03-06 09:01:08 +00:00
|
|
|
/* Keep alphabetized, with longer matches first (example: abcd before abc) */
|
2001-05-09 03:11:22 +00:00
|
|
|
{ { "_command", "complete", NULL }, handle_commandcomplete, "Command complete", commandcomplete_help },
|
2003-01-30 15:03:20 +00:00
|
|
|
{ { "_command", "nummatches", NULL }, handle_commandnummatches, "Returns number of command matches", commandnummatches_help },
|
|
|
|
{ { "_command", "matchesarray", NULL }, handle_commandmatchesarray, "Returns command matches array", commandmatchesarray_help },
|
2004-08-07 19:27:54 +00:00
|
|
|
{ { "debug", "channel", NULL }, handle_debugchan, "Enable debugging on a channel", debugchan_help, complete_ch_3 },
|
2005-03-05 04:04:55 +00:00
|
|
|
{ { "debug", "level", NULL }, handle_debuglevel, "Set global debug level", debuglevel_help },
|
2005-12-28 17:31:12 +00:00
|
|
|
{ { "group", "show", "channels", NULL }, group_show_channels, "Show active channels with group(s)", group_show_channels_help},
|
2006-01-17 17:25:53 +00:00
|
|
|
{ { "help", NULL }, handle_help, "Display help list, or specific help on a command", help_help, complete_help },
|
1999-12-19 22:38:55 +00:00
|
|
|
{ { "load", NULL }, handle_load, "Load a dynamic module by name", load_help, complete_fn },
|
2006-05-26 21:47:52 +00:00
|
|
|
{ { "logger", "mute", NULL }, handle_logger_mute, "Toggle logging output to a console", logger_mute_help },
|
2004-08-07 19:27:54 +00:00
|
|
|
{ { "no", "debug", "channel", NULL }, handle_nodebugchan, "Disable debugging on a channel", nodebugchan_help, complete_ch_4 },
|
2004-10-16 21:14:05 +00:00
|
|
|
{ { "reload", NULL }, handle_reload, "Reload configuration", reload_help, complete_mod_2 },
|
2004-09-13 18:19:15 +00:00
|
|
|
{ { "set", "debug", NULL }, handle_set_debug, "Set level of debug chattiness", set_debug_help },
|
2001-05-09 03:11:22 +00:00
|
|
|
{ { "set", "verbose", NULL }, handle_set_verbose, "Set level of verboseness", set_verbose_help },
|
2004-08-07 19:27:54 +00:00
|
|
|
{ { "show", "channel", NULL }, handle_showchan, "Display information on a specific channel", showchan_help, complete_ch_3 },
|
2005-08-08 01:45:29 +00:00
|
|
|
{ { "show", "channels", NULL }, handle_chanlist, "Display information on channels", chanlist_help, complete_show_channels },
|
2001-10-11 18:51:39 +00:00
|
|
|
{ { "show", "modules", NULL }, handle_modlist, "List modules and info", modlist_help },
|
2004-10-16 21:14:05 +00:00
|
|
|
{ { "show", "modules", "like", NULL }, handle_modlist, "List modules and info", modlist_help, complete_mod_4 },
|
2005-02-05 16:49:14 +00:00
|
|
|
{ { "show", "uptime", NULL }, handle_showuptime, "Show uptime information", uptime_help },
|
2004-08-07 19:27:54 +00:00
|
|
|
{ { "soft", "hangup", NULL }, handle_softhangup, "Request a hangup on a given channel", softhangup_help, complete_ch_3 },
|
1999-12-19 22:38:55 +00:00
|
|
|
{ { "unload", NULL }, handle_unload, "Unload a dynamic module by name", unload_help, complete_fn },
|
|
|
|
{ { NULL }, NULL, NULL, NULL }
|
|
|
|
};
|
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
/*! \brief initialize the _full_cmd string in * each of the builtins. */
|
|
|
|
void ast_builtins_init(void)
|
1999-12-19 22:38:55 +00:00
|
|
|
{
|
2006-03-28 22:25:08 +00:00
|
|
|
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);
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
/*
|
|
|
|
* 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)
|
1999-12-19 22:38:55 +00:00
|
|
|
{
|
2006-03-28 22:25:08 +00:00
|
|
|
struct ast_cli_entry *e;
|
2005-12-20 20:20:04 +00:00
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
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;
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
/*!
|
|
|
|
* \brief locate a cli command in the 'helpers' list (which must be locked).
|
|
|
|
* exact has 3 values:
|
|
|
|
* 0 returns if the search key is equal or longer than the entry.
|
|
|
|
* -1 true if the mismatch is on the last word XXX not true!
|
|
|
|
* 1 true only on complete, exact match.
|
|
|
|
*/
|
|
|
|
static struct ast_cli_entry *find_cli(char *const cmds[], int match_type)
|
1999-12-27 19:20:20 +00:00
|
|
|
{
|
2006-03-28 22:25:08 +00:00
|
|
|
int matchlen = -1; /* length of longest match so far */
|
|
|
|
struct ast_cli_entry *cand = NULL, *e=NULL;
|
|
|
|
struct cli_iterator i = { NULL, NULL};
|
|
|
|
|
|
|
|
while( (e = cli_next(&i)) ) {
|
|
|
|
int y;
|
|
|
|
for (y = 0 ; cmds[y] && e->cmda[y]; y++) {
|
|
|
|
if (strcasecmp(e->cmda[y], cmds[y]))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (e->cmda[y] == NULL) { /* no more words in candidate */
|
|
|
|
if (cmds[y] == NULL) /* this is an exact match, cannot do better */
|
|
|
|
break;
|
|
|
|
/* here the search key is longer than the candidate */
|
|
|
|
if (match_type != 0) /* but we look for almost exact match... */
|
|
|
|
continue; /* so we skip this one. */
|
|
|
|
/* otherwise we like it (case 0) */
|
|
|
|
} else { /* still words in candidate */
|
|
|
|
if (cmds[y] == NULL) /* search key is shorter, not good */
|
|
|
|
continue;
|
|
|
|
/* if we get here, both words exist but there is a mismatch */
|
|
|
|
if (match_type == 0) /* not the one we look for */
|
|
|
|
continue;
|
|
|
|
if (match_type == 1) /* not the one we look for */
|
|
|
|
continue;
|
|
|
|
if (cmds[y+1] != NULL || e->cmda[y+1] != NULL) /* not the one we look for */
|
|
|
|
continue;
|
|
|
|
/* we are in case match_type == -1 and mismatch on last word */
|
|
|
|
}
|
|
|
|
if (cand == NULL || y > matchlen) /* remember the candidate */
|
|
|
|
cand = e;
|
1999-12-27 19:20:20 +00:00
|
|
|
}
|
2006-03-28 22:25:08 +00:00
|
|
|
return e ? e : cand;
|
1999-12-27 19:20:20 +00:00
|
|
|
}
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
static char *find_best(char *argv[])
|
|
|
|
{
|
|
|
|
static char cmdline[80];
|
|
|
|
int x;
|
2006-03-28 22:25:08 +00:00
|
|
|
/* See how close we get, then print the candidate */
|
1999-12-19 22:38:55 +00:00
|
|
|
char *myargv[AST_MAX_CMD_LEN];
|
|
|
|
for (x=0;x<AST_MAX_CMD_LEN;x++)
|
|
|
|
myargv[x]=NULL;
|
2006-03-28 22:25:08 +00:00
|
|
|
AST_LIST_LOCK(&helpers);
|
1999-12-19 22:38:55 +00:00
|
|
|
for (x=0;argv[x];x++) {
|
|
|
|
myargv[x] = argv[x];
|
|
|
|
if (!find_cli(myargv, -1))
|
|
|
|
break;
|
|
|
|
}
|
2006-03-28 22:25:08 +00:00
|
|
|
AST_LIST_UNLOCK(&helpers);
|
|
|
|
ast_join(cmdline, sizeof(cmdline), myargv);
|
1999-12-19 22:38:55 +00:00
|
|
|
return cmdline;
|
|
|
|
}
|
|
|
|
|
1999-12-27 19:20:20 +00:00
|
|
|
int ast_cli_unregister(struct ast_cli_entry *e)
|
|
|
|
{
|
2006-02-14 22:44:20 +00:00
|
|
|
if (e->inuse) {
|
|
|
|
ast_log(LOG_WARNING, "Can't remove command that is in use\n");
|
|
|
|
} else {
|
|
|
|
AST_LIST_LOCK(&helpers);
|
|
|
|
AST_LIST_REMOVE(&helpers, e, list);
|
|
|
|
AST_LIST_UNLOCK(&helpers);
|
1999-12-27 19:20:20 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
int ast_cli_register(struct ast_cli_entry *e)
|
|
|
|
{
|
2006-02-14 22:44:20 +00:00
|
|
|
struct ast_cli_entry *cur;
|
2006-03-28 22:25:08 +00:00
|
|
|
char fulle[80] ="";
|
|
|
|
int lf, ret = -1;
|
2005-11-29 04:25:28 +00:00
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
ast_join(fulle, sizeof(fulle), e->cmda);
|
2006-02-14 22:44:20 +00:00
|
|
|
AST_LIST_LOCK(&helpers);
|
2005-11-29 04:25:28 +00:00
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
if (find_cli(e->cmda, 1)) {
|
2006-02-14 22:44:20 +00:00
|
|
|
AST_LIST_UNLOCK(&helpers);
|
2001-10-11 18:51:39 +00:00
|
|
|
ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", fulle);
|
2006-03-28 22:25:08 +00:00
|
|
|
goto done;
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
2006-03-28 22:25:08 +00:00
|
|
|
e->_full_cmd = ast_strdup(fulle);
|
|
|
|
if (!e->_full_cmd)
|
|
|
|
goto done;
|
|
|
|
lf = strlen(fulle);
|
2006-02-14 22:44:20 +00:00
|
|
|
AST_LIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
|
2006-03-28 22:25:08 +00:00
|
|
|
int len = strlen(cur->_full_cmd);
|
|
|
|
if (lf < len)
|
|
|
|
len = lf;
|
|
|
|
if (strncasecmp(fulle, cur->_full_cmd, len) < 0) {
|
2006-02-14 22:44:20 +00:00
|
|
|
AST_LIST_INSERT_BEFORE_CURRENT(&helpers, e, list);
|
1999-12-19 22:38:55 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-02-14 22:44:20 +00:00
|
|
|
AST_LIST_TRAVERSE_SAFE_END;
|
2005-11-29 04:25:28 +00:00
|
|
|
|
2006-02-14 22:44:20 +00:00
|
|
|
if (!cur)
|
|
|
|
AST_LIST_INSERT_TAIL(&helpers, e, list);
|
2006-03-28 22:25:08 +00:00
|
|
|
ret = 0; /* success */
|
2005-11-29 04:25:28 +00:00
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
done:
|
2006-02-14 22:44:20 +00:00
|
|
|
AST_LIST_UNLOCK(&helpers);
|
2005-11-29 04:25:28 +00:00
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
return ret;
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
|
|
|
|
2005-05-15 03:03:48 +00:00
|
|
|
/*
|
|
|
|
* register/unregister an array of entries.
|
|
|
|
*/
|
|
|
|
void ast_cli_register_multiple(struct ast_cli_entry *e, int len)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2005-11-29 04:25:28 +00:00
|
|
|
for (i = 0; i < len; i++)
|
2005-05-15 03:03:48 +00:00
|
|
|
ast_cli_register(e + i);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2005-11-29 04:25:28 +00:00
|
|
|
for (i = 0; i < len; i++)
|
2005-05-15 03:03:48 +00:00
|
|
|
ast_cli_unregister(e + i);
|
|
|
|
}
|
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
|
|
|
|
/*! \brief helper for help_workhorse and final part of
|
|
|
|
* handle_help. if locked = 0 it's just help_workhorse,
|
|
|
|
* otherwise assume the list is already locked and print
|
|
|
|
* an error message if not found.
|
|
|
|
*/
|
|
|
|
static int help1(int fd, char *match[], int locked)
|
1999-12-19 22:38:55 +00:00
|
|
|
{
|
2006-03-28 22:25:08 +00:00
|
|
|
char matchstr[80] = "";
|
|
|
|
struct ast_cli_entry *e;
|
|
|
|
int len = 0;
|
|
|
|
int found = 0;
|
|
|
|
struct cli_iterator i = { NULL, NULL};
|
|
|
|
|
|
|
|
if (match) {
|
|
|
|
ast_join(matchstr, sizeof(matchstr), match);
|
|
|
|
len = strlen(matchstr);
|
|
|
|
}
|
|
|
|
if (!locked)
|
|
|
|
AST_LIST_LOCK(&helpers);
|
|
|
|
while ( (e = cli_next(&i)) ) {
|
2001-05-09 03:11:22 +00:00
|
|
|
/* Hide commands that start with '_' */
|
2006-03-28 22:25:08 +00:00
|
|
|
if (e->_full_cmd[0] == '_')
|
2001-05-09 03:11:22 +00:00
|
|
|
continue;
|
2006-03-28 22:25:08 +00:00
|
|
|
if (match && strncasecmp(matchstr, e->_full_cmd, len))
|
|
|
|
continue;
|
|
|
|
ast_cli(fd, "%25.25s %s\n", e->_full_cmd, e->summary);
|
|
|
|
found++;
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
2006-03-28 22:25:08 +00:00
|
|
|
AST_LIST_UNLOCK(&helpers);
|
|
|
|
if (!locked && !found && matchstr[0])
|
|
|
|
ast_cli(fd, "No such command '%s'.\n", matchstr);
|
1999-12-19 22:38:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
static int help_workhorse(int fd, char *match[])
|
|
|
|
{
|
|
|
|
return help1(fd, match, 0 /* do not print errors */);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int handle_help(int fd, int argc, char *argv[])
|
|
|
|
{
|
1999-12-19 22:38:55 +00:00
|
|
|
char fullcmd[80];
|
2006-03-28 22:25:08 +00:00
|
|
|
struct ast_cli_entry *e;
|
|
|
|
|
|
|
|
if (argc < 1)
|
1999-12-19 22:38:55 +00:00
|
|
|
return RESULT_SHOWUSAGE;
|
2006-03-28 22:25:08 +00:00
|
|
|
if (argc == 1)
|
1999-12-19 22:38:55 +00:00
|
|
|
return help_workhorse(fd, NULL);
|
2006-03-28 22:25:08 +00:00
|
|
|
|
|
|
|
AST_LIST_LOCK(&helpers);
|
|
|
|
e = find_cli(argv + 1, 1); /* try exact match first */
|
|
|
|
if (!e)
|
|
|
|
return help1(fd, argv + 1, 1 /* locked */);
|
|
|
|
if (e->usage)
|
|
|
|
ast_cli(fd, "%s", e->usage);
|
|
|
|
else {
|
|
|
|
ast_join(fullcmd, sizeof(fullcmd), argv+1);
|
|
|
|
ast_cli(fd, "No help text available for '%s'.\n", fullcmd);
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
2006-03-28 22:25:08 +00:00
|
|
|
AST_LIST_UNLOCK(&helpers);
|
1999-12-19 22:38:55 +00:00
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2006-01-18 22:17:31 +00:00
|
|
|
static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
|
1999-12-19 22:38:55 +00:00
|
|
|
{
|
|
|
|
char *dup, *cur;
|
2005-06-10 20:25:23 +00:00
|
|
|
int x = 0;
|
|
|
|
int quoted = 0;
|
|
|
|
int escaped = 0;
|
|
|
|
int whitespace = 1;
|
|
|
|
|
2005-09-25 03:13:00 +00:00
|
|
|
*trailingwhitespace = 0;
|
2006-03-28 22:25:08 +00:00
|
|
|
if (s == NULL) /* invalid, though! */
|
|
|
|
return NULL;
|
|
|
|
/* make a copy to store the parsed string */
|
2006-07-23 04:58:56 +00:00
|
|
|
if (!(dup = ast_strdup(s)))
|
2005-06-10 20:25:23 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
cur = dup;
|
2006-03-28 22:25:08 +00:00
|
|
|
/* scan the original string copying into cur when needed */
|
|
|
|
for (; *s ; s++) {
|
|
|
|
if (x >= max - 1) {
|
|
|
|
ast_log(LOG_WARNING, "Too many arguments, truncating at %s\n", s);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (*s == '"' && !escaped) {
|
2005-06-10 20:25:23 +00:00
|
|
|
quoted = !quoted;
|
2006-03-28 22:25:08 +00:00
|
|
|
if (quoted && whitespace) {
|
|
|
|
/* start a quoted string from previous whitespace: new argument */
|
2005-06-10 20:25:23 +00:00
|
|
|
argv[x++] = cur;
|
|
|
|
whitespace = 0;
|
|
|
|
}
|
2006-03-28 22:25:08 +00:00
|
|
|
} else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) {
|
2005-06-10 20:25:23 +00:00
|
|
|
/* If we are not already in whitespace, and not in a quoted string or
|
|
|
|
processing an escape sequence, and just entered whitespace, then
|
|
|
|
finalize the previous argument and remember that we are in whitespace
|
|
|
|
*/
|
|
|
|
if (!whitespace) {
|
2006-03-28 22:25:08 +00:00
|
|
|
*cur++ = '\0';
|
2005-06-10 20:25:23 +00:00
|
|
|
whitespace = 1;
|
|
|
|
}
|
2006-03-28 22:25:08 +00:00
|
|
|
} else if (*s == '\\' && !escaped) {
|
2005-06-10 20:25:23 +00:00
|
|
|
escaped = 1;
|
|
|
|
} else {
|
|
|
|
if (whitespace) {
|
2006-03-28 22:25:08 +00:00
|
|
|
/* we leave whitespace, and are not quoted. So it's a new argument */
|
2005-06-10 20:25:23 +00:00
|
|
|
argv[x++] = cur;
|
|
|
|
whitespace = 0;
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
2006-03-28 22:25:08 +00:00
|
|
|
*cur++ = *s;
|
2005-06-10 20:25:23 +00:00
|
|
|
escaped = 0;
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
|
|
|
}
|
2005-06-10 20:25:23 +00:00
|
|
|
/* Null terminate */
|
2006-03-28 22:25:08 +00:00
|
|
|
*cur++ = '\0';
|
|
|
|
/* XXX put a NULL in the last argument, because some functions that take
|
|
|
|
* the array may want a null-terminated array.
|
|
|
|
* argc still reflects the number of non-NULL entries.
|
|
|
|
*/
|
2005-06-10 20:25:23 +00:00
|
|
|
argv[x] = NULL;
|
|
|
|
*argc = x;
|
2005-09-25 03:13:00 +00:00
|
|
|
*trailingwhitespace = whitespace;
|
1999-12-19 22:38:55 +00:00
|
|
|
return dup;
|
|
|
|
}
|
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
/*! \brief Return the number of unique matches for the generator */
|
2006-01-18 22:17:31 +00:00
|
|
|
int ast_cli_generatornummatches(const char *text, const char *word)
|
2003-01-30 15:03:20 +00:00
|
|
|
{
|
|
|
|
int matches = 0, i = 0;
|
2004-11-17 19:28:08 +00:00
|
|
|
char *buf = NULL, *oldbuf = NULL;
|
2003-01-30 15:03:20 +00:00
|
|
|
|
2005-11-29 04:25:28 +00:00
|
|
|
while ((buf = ast_cli_generator(text, word, i++))) {
|
2005-09-24 15:25:52 +00:00
|
|
|
if (!oldbuf || strcmp(buf,oldbuf))
|
|
|
|
matches++;
|
|
|
|
if (oldbuf)
|
|
|
|
free(oldbuf);
|
2003-01-30 15:03:20 +00:00
|
|
|
oldbuf = buf;
|
|
|
|
}
|
2005-09-24 15:25:52 +00:00
|
|
|
if (oldbuf)
|
|
|
|
free(oldbuf);
|
2003-01-30 15:03:20 +00:00
|
|
|
return matches;
|
|
|
|
}
|
|
|
|
|
2006-01-18 22:17:31 +00:00
|
|
|
char **ast_cli_completion_matches(const char *text, const char *word)
|
2003-01-30 15:03:20 +00:00
|
|
|
{
|
|
|
|
char **match_list = NULL, *retstr, *prevstr;
|
|
|
|
size_t match_list_len, max_equal, which, i;
|
|
|
|
int matches = 0;
|
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
/* leave entry 0 free for the longest common substring */
|
2003-01-30 15:03:20 +00:00
|
|
|
match_list_len = 1;
|
|
|
|
while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
|
|
|
|
if (matches + 1 >= match_list_len) {
|
|
|
|
match_list_len <<= 1;
|
2006-02-09 16:59:50 +00:00
|
|
|
if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
|
|
|
|
return NULL;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
|
|
|
match_list[++matches] = retstr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!match_list)
|
2006-03-28 22:25:08 +00:00
|
|
|
return match_list; /* NULL */
|
2003-01-30 15:03:20 +00:00
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
/* Find the longest substring that is common to all results
|
|
|
|
* (it is a candidate for completion), and store a copy in entry 0.
|
|
|
|
*/
|
2003-01-30 15:03:20 +00:00
|
|
|
prevstr = match_list[1];
|
|
|
|
max_equal = strlen(prevstr);
|
2006-03-28 22:25:08 +00:00
|
|
|
for (which = 2; which <= matches; which++) {
|
2004-11-18 04:11:51 +00:00
|
|
|
for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
|
2003-01-30 15:03:20 +00:00
|
|
|
continue;
|
|
|
|
max_equal = i;
|
|
|
|
}
|
|
|
|
|
2006-02-09 16:59:50 +00:00
|
|
|
if (!(retstr = ast_malloc(max_equal + 1)))
|
|
|
|
return NULL;
|
|
|
|
|
2005-11-29 04:25:28 +00:00
|
|
|
strncpy(retstr, match_list[1], max_equal);
|
2003-01-30 15:03:20 +00:00
|
|
|
retstr[max_equal] = '\0';
|
|
|
|
match_list[0] = retstr;
|
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
/* ensure that the array is NULL terminated */
|
2006-02-09 16:59:50 +00:00
|
|
|
if (matches + 1 >= match_list_len) {
|
|
|
|
if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list))))
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
match_list[matches + 1] = NULL;
|
2003-01-30 15:03:20 +00:00
|
|
|
|
2005-11-29 04:25:28 +00:00
|
|
|
return match_list;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
|
|
|
|
2006-01-18 22:17:31 +00:00
|
|
|
static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
|
1999-12-19 22:38:55 +00:00
|
|
|
{
|
|
|
|
char *argv[AST_MAX_ARGS];
|
2006-03-28 22:25:08 +00:00
|
|
|
struct ast_cli_entry *e;
|
|
|
|
struct cli_iterator i = { NULL, NULL };
|
|
|
|
int x = 0, argindex, matchlen;
|
1999-12-19 22:38:55 +00:00
|
|
|
int matchnum=0;
|
2006-03-28 22:25:08 +00:00
|
|
|
char *ret = NULL;
|
2005-08-14 01:48:14 +00:00
|
|
|
char matchstr[80] = "";
|
2006-05-19 19:35:16 +00:00
|
|
|
int tws = 0;
|
2006-03-28 22:25:08 +00:00
|
|
|
char *dup = parse_args(text, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws);
|
1999-12-19 22:38:55 +00:00
|
|
|
|
2006-03-28 22:25:08 +00:00
|
|
|
if (!dup) /* error */
|
|
|
|
return NULL;
|
|
|
|
argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
|
|
|
|
/* rebuild the command, ignore tws */
|
|
|
|
ast_join(matchstr, sizeof(matchstr)-1, argv);
|
|
|
|
matchlen = strlen(matchstr);
|
2006-05-19 19:35:16 +00:00
|
|
|
if (tws) {
|
|
|
|
strcat(matchstr, " "); /* XXX */
|
|
|
|
if (matchlen)
|
|
|
|
matchlen++;
|
|
|
|
}
|
2006-03-28 22:25:08 +00:00
|
|
|
if (lock)
|
|
|
|
AST_LIST_LOCK(&helpers);
|
|
|
|
while( !ret && (e = cli_next(&i)) ) {
|
|
|
|
int lc = strlen(e->_full_cmd);
|
|
|
|
if (e->_full_cmd[0] != '_' && lc > 0 && matchlen <= lc &&
|
|
|
|
!strncasecmp(matchstr, e->_full_cmd, matchlen)) {
|
|
|
|
/* Found initial part, return a copy of the next word... */
|
|
|
|
if (e->cmda[argindex] && ++matchnum > state)
|
|
|
|
ret = strdup(e->cmda[argindex]); /* we need a malloced string */
|
|
|
|
} else if (e->generator && !strncasecmp(matchstr, e->_full_cmd, lc) && matchstr[lc] < 33) {
|
|
|
|
/* We have a command in its entirity within us -- theoretically only one
|
|
|
|
command can have this occur */
|
|
|
|
ret = e->generator(matchstr, word, argindex, state);
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
|
|
|
}
|
2006-03-28 22:25:08 +00:00
|
|
|
if (lock)
|
|
|
|
AST_LIST_UNLOCK(&helpers);
|
|
|
|
free(dup);
|
|
|
|
return ret;
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
|
|
|
|
2006-01-18 22:17:31 +00:00
|
|
|
char *ast_cli_generator(const char *text, const char *word, int state)
|
2001-05-09 03:11:22 +00:00
|
|
|
{
|
|
|
|
return __ast_cli_generator(text, word, state, 1);
|
|
|
|
}
|
|
|
|
|
2006-01-18 22:17:31 +00:00
|
|
|
int ast_cli_command(int fd, const char *s)
|
1999-12-19 22:38:55 +00:00
|
|
|
{
|
|
|
|
char *argv[AST_MAX_ARGS];
|
|
|
|
struct ast_cli_entry *e;
|
|
|
|
int x;
|
|
|
|
char *dup;
|
2005-09-25 03:13:00 +00:00
|
|
|
int tws;
|
2006-02-09 16:59:50 +00:00
|
|
|
|
2006-07-23 04:58:56 +00:00
|
|
|
if (!(dup = parse_args(s, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws)))
|
2005-11-29 04:25:28 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* We need at least one entry, or ignore */
|
|
|
|
if (x > 0) {
|
2006-02-14 22:44:20 +00:00
|
|
|
AST_LIST_LOCK(&helpers);
|
2005-11-29 04:25:28 +00:00
|
|
|
e = find_cli(argv, 0);
|
|
|
|
if (e)
|
|
|
|
e->inuse++;
|
2006-02-14 22:44:20 +00:00
|
|
|
AST_LIST_UNLOCK(&helpers);
|
2005-11-29 04:25:28 +00:00
|
|
|
if (e) {
|
|
|
|
switch(e->handler(fd, x, argv)) {
|
|
|
|
case RESULT_SHOWUSAGE:
|
2006-02-27 15:23:44 +00:00
|
|
|
if (e->usage)
|
|
|
|
ast_cli(fd, "%s", e->usage);
|
|
|
|
else
|
|
|
|
ast_cli(fd, "Invalid usage, but no usage information available.\n");
|
2005-11-29 04:25:28 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(argv));
|
2006-07-23 04:58:56 +00:00
|
|
|
if (e)
|
|
|
|
ast_atomic_fetchadd_int(&e->inuse, -1);
|
1999-12-19 22:38:55 +00:00
|
|
|
}
|
2005-11-29 04:25:28 +00:00
|
|
|
free(dup);
|
|
|
|
|
1999-12-19 22:38:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|