Newer
Older
(long)c->whentohangup,
Kevin P. Fleming
committed
cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>",
c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
Kevin P. Fleming
committed
(ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
Joshua Colp
committed
if (pbx_builtin_serialize_variables(c, &out))
ast_cli(a->fd," Variables:\n%s\n", out->str);
Joshua Colp
committed
if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1))
ast_cli(a->fd," CDR Variables:\n%s\n", out->str);
Kevin P. Fleming
committed
ast_channel_unlock(c);
return CLI_SUCCESS;
/*
* helper function to generate CLI matches from a fixed set of values.
* A NULL word is acceptable.
*/
char *ast_cli_complete(const char *word, char *const choices[], int state)
Mark Spencer
committed
{
int i, which = 0, len;
len = ast_strlen_zero(word) ? 0 : strlen(word);
for (i = 0; choices[i]; i++) {
if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
return ast_strdup(choices[i]);
Mark Spencer
committed
}
return NULL;
}
char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
Kevin P. Fleming
committed
struct ast_channel *c = NULL;
int which = 0;
int wordlen;
char notfound = '\0';
char *ret = ¬found; /* so NULL can break the loop */
Kevin P. Fleming
committed
if (pos != rpos)
return NULL;
wordlen = strlen(word);
while (ret == ¬found && (c = ast_channel_walk_locked(c))) {
if (!strncasecmp(word, c->name, wordlen) && ++which > state)
ret = ast_strdup(c->name);
ast_channel_unlock(c);
return ret == ¬found ? NULL : ret;
static char *group_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Russell Bryant
committed
{
#define FORMAT_STRING "%-25s %-20s %-20s\n"
struct ast_group_info *gi = NULL;
Russell Bryant
committed
int numchans = 0;
regex_t regexbuf;
int havepattern = 0;
switch (cmd) {
case CLI_INIT:
e->command = "group show channels";
e->usage =
"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";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc < 3 || a->argc > 4)
return CLI_SHOWUSAGE;
Russell Bryant
committed
if (a->argc == 4) {
if (regcomp(®exbuf, a->argv[3], REG_EXTENDED | REG_NOSUB))
return CLI_SHOWUSAGE;
Russell Bryant
committed
havepattern = 1;
}
ast_cli(a->fd, FORMAT_STRING, "Channel", "Group", "Category");
ast_app_group_list_rdlock();
gi = ast_app_group_list_head();
while (gi) {
if (!havepattern || !regexec(®exbuf, gi->group, 0, NULL, 0)) {
ast_cli(a->fd, FORMAT_STRING, gi->chan->name, gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
Russell Bryant
committed
}
Russell Bryant
committed
}
ast_app_group_list_unlock();
Russell Bryant
committed
if (havepattern)
regfree(®exbuf);
ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
return CLI_SUCCESS;
Russell Bryant
committed
#undef FORMAT_STRING
}
Jason Parker
committed
static struct ast_cli_entry cli_debug_channel_deprecated = AST_CLI_DEFINE(handle_debugchan_deprecated, "Enable debugging on channel");
static struct ast_cli_entry cli_module_load_deprecated = AST_CLI_DEFINE(handle_load_deprecated, "Load a module");
static struct ast_cli_entry cli_module_reload_deprecated = AST_CLI_DEFINE(handle_reload_deprecated, "reload modules by name");
static struct ast_cli_entry cli_module_unload_deprecated = AST_CLI_DEFINE(handle_unload_deprecated, "unload modules by name");
static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static struct ast_cli_entry cli_cli[] = {
/* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
Jason Parker
committed
AST_CLI_DEFINE(handle_commandcomplete, "Command complete"),
AST_CLI_DEFINE(handle_commandnummatches, "Returns number of command matches"),
AST_CLI_DEFINE(handle_commandmatchesarray, "Returns command matches array"),
Russell Bryant
committed
Jason Parker
committed
AST_CLI_DEFINE(handle_nodebugchan_deprecated, "Disable debugging on channel(s)"),
Jason Parker
committed
AST_CLI_DEFINE(handle_chanlist, "Display information on channels"),
Jason Parker
committed
AST_CLI_DEFINE(handle_showchan, "Display information on a specific channel"),
Jason Parker
committed
AST_CLI_DEFINE(handle_core_set_debug_channel, "Enable/disable debugging on a channel",
.deprecate_cmd = &cli_debug_channel_deprecated),
Jason Parker
committed
AST_CLI_DEFINE(handle_verbose, "Set level of debug/verbose chattiness"),
Jason Parker
committed
AST_CLI_DEFINE(group_show_channels, "Display active channels with group(s)"),
Jason Parker
committed
AST_CLI_DEFINE(handle_help, "Display help list, or specific help on a command"),
Jason Parker
committed
AST_CLI_DEFINE(handle_logger_mute, "Toggle logging output to a console"),
Jason Parker
committed
AST_CLI_DEFINE(handle_modlist, "List modules and info"),
Jason Parker
committed
AST_CLI_DEFINE(handle_load, "Load a module by name", .deprecate_cmd = &cli_module_load_deprecated),
Jason Parker
committed
AST_CLI_DEFINE(handle_reload, "Reload configuration", .deprecate_cmd = &cli_module_reload_deprecated),
Jason Parker
committed
AST_CLI_DEFINE(handle_unload, "Unload a module by name", .deprecate_cmd = &cli_module_unload_deprecated ),
Jason Parker
committed
AST_CLI_DEFINE(handle_showuptime, "Show uptime information"),
Jason Parker
committed
AST_CLI_DEFINE(handle_softhangup, "Request a hangup on a given channel"),
/*!
* Some regexp characters in cli arguments are reserved and used as separators.
*/
static const char cli_rsvd[] = "[]{}|*%";
/*!
* initialize the _full_cmd string and related parameters,
* return 0 on success, -1 on error.
*/
static int set_full_cmd(struct ast_cli_entry *e)
{
int i;
char buf[80];
ast_join(buf, sizeof(buf), e->cmda);
e->_full_cmd = ast_strdup(buf);
if (!e->_full_cmd) {
ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
return -1;
}
e->cmdlen = strcspn(e->_full_cmd, cli_rsvd);
for (i = 0; e->cmda[i]; i++)
;
e->args = i;
return 0;
}
/*! \brief initialize the _full_cmd string in * each of the builtins. */
void ast_builtins_init(void)
ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
Russell Bryant
committed
static struct ast_cli_entry *cli_next(struct ast_cli_entry *e)
Russell Bryant
committed
if (e == NULL)
e = AST_LIST_FIRST(&helpers);
if (e)
e = AST_LIST_NEXT(e, list);
/*!
* match a word in the CLI entry.
* returns -1 on mismatch, 0 on match of an optional word,
* 1 on match of a full word.
*
* The pattern can be
* any_word match for equal
* [foo|bar|baz] optionally, one of these words
* {foo|bar|baz} exactly, one of these words
* % any word
*/
static int word_match(const char *cmd, const char *cli_word)
{
int l;
char *pos;
if (ast_strlen_zero(cmd) || ast_strlen_zero(cli_word))
return -1;
if (!strchr(cli_rsvd, cli_word[0])) /* normal match */
return (strcasecmp(cmd, cli_word) == 0) ? 1 : -1;
/* regexp match, takes [foo|bar] or {foo|bar} */
l = strlen(cmd);
/* wildcard match - will extend in the future */
if (l > 0 && cli_word[0] == '%') {
return 1; /* wildcard */
}
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
pos = strcasestr(cli_word, cmd);
if (pos == NULL) /* not found, say ok if optional */
return cli_word[0] == '[' ? 0 : -1;
if (pos == cli_word) /* no valid match at the beginning */
return -1;
if (strchr(cli_rsvd, pos[-1]) && strchr(cli_rsvd, pos[l]))
return 1; /* valid match */
return -1; /* not found */
}
/*! \brief if word is a valid prefix for token, returns the pos-th
* match as a malloced string, or NULL otherwise.
* Always tell in *actual how many matches we got.
*/
static char *is_prefix(const char *word, const char *token,
int pos, int *actual)
{
int lw;
char *s, *t1;
*actual = 0;
if (ast_strlen_zero(token))
return NULL;
if (ast_strlen_zero(word))
word = ""; /* dummy */
lw = strlen(word);
if (strcspn(word, cli_rsvd) != lw)
return NULL; /* no match if word has reserved chars */
if (strchr(cli_rsvd, token[0]) == NULL) { /* regular match */
if (strncasecmp(token, word, lw)) /* no match */
return NULL;
*actual = 1;
return (pos != 0) ? NULL : ast_strdup(token);
}
/* now handle regexp match */
/* Wildcard always matches, so we never do is_prefix on them */
t1 = ast_strdupa(token + 1); /* copy, skipping first char */
while (pos >= 0 && (s = strsep(&t1, cli_rsvd)) && *s) {
if (*s == '%') /* wildcard */
continue;
if (strncasecmp(s, word, lw)) /* no match */
continue;
(*actual)++;
if (pos-- == 0)
}
return NULL;
}
/*!
* \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.
* note that trailing optional arguments are skipped.
* -1 true if the mismatch is on the last word XXX not true!
* 1 true only on complete, exact match.
*
* The search compares word by word taking care of regexps in e->cmda
*/
static struct ast_cli_entry *find_cli(char *const cmds[], int match_type)
int matchlen = -1; /* length of longest match so far */
struct ast_cli_entry *cand = NULL, *e=NULL;
Russell Bryant
committed
while ( (e = cli_next(e)) ) {
/* word-by word regexp comparison */
char * const *src = cmds;
char * const *dst = e->cmda;
int n = 0;
for (;; dst++, src += n) {
n = word_match(*src, *dst);
if (n < 0)
if (ast_strlen_zero(*dst) || ((*dst)[0] == '[' && ast_strlen_zero(dst[1]))) {
/* no more words in 'e' */
if (ast_strlen_zero(*src)) /* exact match, cannot do better */
/* Here, cmds has more words than the entry 'e' */
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 'e' */
if (ast_strlen_zero(*src))
continue; /* cmds is shorter than 'e', not good */
/* Here we have leftover words in cmds and 'e',
* but there is a mismatch. We only accept this one if match_type == -1
* and this is the last word for both.
*/
if (match_type != -1 || !ast_strlen_zero(src[1]) ||
!ast_strlen_zero(dst[1])) /* not the one we look for */
/* good, we are in case match_type == -1 and mismatch on last word */
if (src - cmds > matchlen) { /* remember the candidate */
matchlen = src - cmds;
return e ? e : cand;
static char *find_best(char *argv[])
{
static char cmdline[80];
int x;
/* See how close we get, then print the candidate */
char *myargv[AST_MAX_CMD_LEN];
for (x=0;x<AST_MAX_CMD_LEN;x++)
myargv[x]=NULL;
for (x=0;argv[x];x++) {
myargv[x] = argv[x];
if (!find_cli(myargv, -1))
break;
}
ast_join(cmdline, sizeof(cmdline), myargv);
static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
if (e->deprecate_cmd) {
__ast_cli_unregister(e->deprecate_cmd, e);
}
Kevin P. Fleming
committed
if (e->inuse) {
ast_log(LOG_WARNING, "Can't remove command that is in use\n");
} else {
AST_RWLIST_WRLOCK(&helpers);
AST_RWLIST_REMOVE(&helpers, e, list);
AST_RWLIST_UNLOCK(&helpers);
Tilghman Lesher
committed
ast_free(e->_full_cmd);
if (e->new_handler) {
/* this is a new-style entry. Reset fields and free memory. */
bzero((char **)(e->cmda), sizeof(e->cmda));
Tilghman Lesher
committed
ast_free(e->command);
e->command = NULL;
e->usage = NULL;
}
static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
Kevin P. Fleming
committed
struct ast_cli_entry *cur;
int i, lf, ret = -1;
if (e->handler == NULL) { /* new style entry, run the handler to init fields */
struct ast_cli_args a; /* fake argument */
char **dst = (char **)e->cmda; /* need to cast as the entry is readonly */
bzero (&a, sizeof(a));
e->new_handler(e, CLI_INIT, &a);
/* XXX check that usage and command are filled up */
s = ast_skip_blanks(e->command);
s = e->command = ast_strdup(s);
for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) {
*dst++ = s; /* store string */
s = ast_skip_nonblanks(s);
if (*s == '\0') /* we are done */
break;
*s++ = '\0';
s = ast_skip_blanks(s);
}
*dst++ = NULL;
}
if (find_cli(e->cmda, 1)) {
ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", e->_full_cmd);
if (set_full_cmd(e))
goto done;
if (!ed) {
e->deprecated = 0;
} else {
e->deprecated = 1;
e->summary = ed->summary;
e->usage = ed->usage;
/* XXX If command A deprecates command B, and command B deprecates command C...
Do we want to show command A or command B when telling the user to use new syntax?
This currently would show command A.
To show command B, you just need to always use ed->_full_cmd.
*/
e->_deprecated_by = S_OR(ed->_deprecated_by, ed->_full_cmd);
}
lf = e->cmdlen;
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
int len = cur->cmdlen;
if (lf < len)
len = lf;
if (strncasecmp(e->_full_cmd, cur->_full_cmd, len) < 0) {
AST_RWLIST_INSERT_BEFORE_CURRENT(e, list);
Kevin P. Fleming
committed
if (!cur)
AST_RWLIST_INSERT_TAIL(&helpers, e, list);
ret = 0; /* success */
if (e->deprecate_cmd) {
/* This command deprecates another command. Register that one also. */
__ast_cli_register(e->deprecate_cmd, e);
}
/* wrapper function, so we can unregister deprecated commands recursively */
int ast_cli_unregister(struct ast_cli_entry *e)
{
return __ast_cli_unregister(e, NULL);
}
/* wrapper function, so we can register deprecated commands recursively */
int ast_cli_register(struct ast_cli_entry *e)
{
return __ast_cli_register(e, NULL);
}
Kevin P. Fleming
committed
/*
* register/unregister an array of entries.
*/
Russell Bryant
committed
int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
Kevin P. Fleming
committed
{
Russell Bryant
committed
int i, res = 0;
Kevin P. Fleming
committed
for (i = 0; i < len; i++)
Russell Bryant
committed
res |= ast_cli_register(e + i);
return res;
Kevin P. Fleming
committed
}
Russell Bryant
committed
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Kevin P. Fleming
committed
{
Russell Bryant
committed
int i, res = 0;
Kevin P. Fleming
committed
for (i = 0; i < len; i++)
Russell Bryant
committed
res |= ast_cli_unregister(e + i);
return res;
Kevin P. Fleming
committed
}
/*! \brief helper for 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 char *help1(int fd, char *match[], int locked)
char matchstr[80] = "";
Russell Bryant
committed
struct ast_cli_entry *e = NULL;
int len = 0;
int found = 0;
if (match) {
ast_join(matchstr, sizeof(matchstr), match);
len = strlen(matchstr);
}
if (!locked)
Russell Bryant
committed
while ( (e = cli_next(e)) ) {
if (e->_full_cmd[0] == '_')
/* Hide commands that are marked as deprecated. */
if (e->deprecated)
continue;
if (match && strncasecmp(matchstr, e->_full_cmd, len))
continue;
ast_cli(fd, "%30.30s %s\n", e->_full_cmd, S_OR(e->summary, "<no description available>"));
if (!locked && !found && matchstr[0])
ast_cli(fd, "No such command '%s'.\n", matchstr);
static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
if (cmd == CLI_INIT) {
e->command = "help";
e->usage =
"Usage: help [topic]\n"
" When called with a topic as an argument, displays usage\n"
" information on the given command. If called without a\n"
" topic, it provides a list of commands.\n";
return NULL;
} else if (cmd == CLI_GENERATE) {
/* skip first 4 or 5 chars, "help " */
int l = strlen(a->line);
if (l > 5)
l = 5;
/* XXX watch out, should stop to the non-generator parts */
return __ast_cli_generator(a->line + l, a->word, a->n, 0);
}
if (a->argc == 1)
return help1(a->fd, NULL, 0);
my_e = find_cli(a->argv + 1, 1); /* try exact match first */
if (!my_e)
return help1(a->fd, a->argv + 1, 1 /* locked */);
if (my_e->usage)
ast_cli(a->fd, "%s", my_e->usage);
ast_join(fullcmd, sizeof(fullcmd), a->argv+1);
ast_cli(a->fd, "No help text available for '%s'.\n", fullcmd);
Russell Bryant
committed
static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
int x = 0;
int quoted = 0;
int escaped = 0;
int whitespace = 1;
if (trailingwhitespace == NULL)
trailingwhitespace = &dummy;
if (s == NULL) /* invalid, though! */
return NULL;
/* make a copy to store the parsed string */
Russell Bryant
committed
if (!(dup = ast_strdup(s)))
return NULL;
cur = dup;
/* 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) {
if (quoted && whitespace) {
/* start a quoted string from previous whitespace: new argument */
argv[x++] = cur;
whitespace = 0;
}
} else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) {
/* 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) {
} else if (*s == '\\' && !escaped) {
escaped = 1;
} else {
if (whitespace) {
/* we leave whitespace, and are not quoted. So it's a new argument */
argv[x++] = cur;
whitespace = 0;
*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.
*/
argv[x] = NULL;
*argc = x;
/*! \brief Return the number of unique matches for the generator */
Russell Bryant
committed
int ast_cli_generatornummatches(const char *text, const char *word)
while ((buf = ast_cli_generator(text, word, i++))) {
if (!oldbuf || strcmp(buf,oldbuf))
matches++;
if (oldbuf)
Tilghman Lesher
committed
ast_free(oldbuf);
Tilghman Lesher
committed
ast_free(oldbuf);
Russell Bryant
committed
char **ast_cli_completion_matches(const char *text, const char *word)
{
char **match_list = NULL, *retstr, *prevstr;
size_t match_list_len, max_equal, which, i;
int matches = 0;
/* leave entry 0 free for the longest common substring */
match_list_len = 1;
while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
if (matches + 1 >= match_list_len) {
match_list_len <<= 1;
Russell Bryant
committed
if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
return NULL;
}
match_list[++matches] = retstr;
}
if (!match_list)
return match_list; /* NULL */
/* Find the longest substring that is common to all results
* (it is a candidate for completion), and store a copy in entry 0.
*/
prevstr = match_list[1];
max_equal = strlen(prevstr);
for (which = 2; which <= matches; which++) {
for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
Russell Bryant
committed
if (!(retstr = ast_malloc(max_equal + 1)))
return NULL;
ast_copy_string(retstr, match_list[1], max_equal + 1);
/* ensure that the array is NULL terminated */
Russell Bryant
committed
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;
return match_list;
/*! \brief returns true if there are more words to match */
static int more_words (char * const *dst)
{
int i;
for (i = 0; dst[i]; i++) {
if (dst[i][0] != '[')
return -1;
}
return 0;
}
/*
* generate the entry at position 'state'
*/
Russell Bryant
committed
static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
Russell Bryant
committed
struct ast_cli_entry *e = NULL;
int x = 0, argindex, matchlen;
Joshua Colp
committed
int tws = 0;
/* Split the argument into an array of words */
char *dup = parse_args(text, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws);
/* Compute the index of the last argument (could be an empty string) */
argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
/* rebuild the command, ignore terminating white space and flatten space */
ast_join(matchstr, sizeof(matchstr)-1, argv);
matchlen = strlen(matchstr);
Joshua Colp
committed
if (tws) {
strcat(matchstr, " "); /* XXX */
if (matchlen)
matchlen++;
}
Russell Bryant
committed
while ( (e = cli_next(e)) ) {
/* XXX repeated code */
int src = 0, dst = 0, n = 0;
/*
* Try to match words, up to and excluding the last word, which
* is either a blank or something that we want to extend.
*/
for (;src < argindex; dst++, src += n) {
n = word_match(argv[src], e->cmda[dst]);
if (n < 0)
if (src != argindex && more_words(e->cmda + dst)) /* not a match */
continue;
ret = is_prefix(argv[src], e->cmda[dst], state - matchnum, &n);
matchnum += n; /* this many matches here */
if (ret) {
/*
* argv[src] is a valid prefix of the next word in this
* command. If this is also the correct entry, return it.
*/
if (matchnum > state)
break;
Tilghman Lesher
committed
ast_free(ret);
ret = NULL;
} else if (ast_strlen_zero(e->cmda[dst])) {
/*
* This entry is a prefix of the command string entered
* (only one entry in the list should have this property).
* Run the generator if one is available. In any case we are done.
*/
ret = e->generator(matchstr, word, argindex, state - matchnum);
else if (e->new_handler) { /* new style command */
struct ast_cli_args a = {
.line = matchstr, .word = word,
.pos = argindex,
ret = e->new_handler(e, CLI_GENERATE, &a);
}
if (ret)
break;
Tilghman Lesher
committed
ast_free(dup);
Russell Bryant
committed
char *ast_cli_generator(const char *text, const char *word, int state)
{
return __ast_cli_generator(text, word, state, 1);
}
Russell Bryant
committed
int ast_cli_command(int fd, const char *s)
char *args[AST_MAX_ARGS + 1];
int res;
char *dup = parse_args(s, &x, args + 1, AST_MAX_ARGS, NULL);
if (dup == NULL)
return -1;
if (x < 1) /* We need at least one entry, otherwise ignore */
goto done;
e = find_cli(args + 1, 0);
if (e)
ast_atomic_fetchadd_int(&e->inuse, 1);
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
if (e == NULL) {
ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(args + 1));
goto done;
}
/*
* Within the handler, argv[-1] contains a pointer to the ast_cli_entry.
* Remember that the array returned by parse_args is NULL-terminated.
*/
args[0] = (char *)e;
if (!e->new_handler) /* old style */
res = e->handler(fd, x, args + 1);
else {
struct ast_cli_args a = {
.fd = fd, .argc = x, .argv = args+1 };
char *retval = e->new_handler(e, CLI_HANDLER, &a);
if (retval == CLI_SUCCESS)
res = RESULT_SUCCESS;
else if (retval == CLI_SHOWUSAGE)
res = RESULT_SHOWUSAGE;
else
res = RESULT_FAILURE;
}
switch (res) {
case RESULT_SHOWUSAGE:
ast_cli(fd, "%s", S_OR(e->usage, "Invalid usage, but no usage information available.\n"));
if (e->deprecated)
ast_cli(fd, "The '%s' command is deprecated and will be removed in a future release. Please use '%s' instead.\n", e->_full_cmd, e->_deprecated_by);
break;
case RESULT_FAILURE:
ast_cli(fd, "Command '%s' failed.\n", s);
/* FALLTHROUGH */
default:
if (e->deprecated == 1) {
ast_cli(fd, "The '%s' command is deprecated and will be removed in a future release. Please use '%s' instead.\n", e->_full_cmd, e->_deprecated_by);
e->deprecated = 2;
}
ast_atomic_fetchadd_int(&e->inuse, -1);
done:
Tilghman Lesher
committed
ast_free(dup);
int ast_cli_command_multiple(int fd, size_t size, const char *s)
{
char cmd[512];
int x, y = 0, count = 0;
for (x = 0; x < size; x++) {
cmd[y] = s[x];
y++;
if (s[x] == '\0') {
ast_cli_command(fd, cmd);
y = 0;
count++;
}
}
return count;
}