update the internal cli api following comments from kevin.

This change basically simplifies the interface of the
new-style handler removing almost all the tricks used in
the previous implementation to achieve backward compatibility
(which is still present and guaranteed.)



git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@47652 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Luigi Rizzo
2006-11-15 14:11:28 +00:00
parent 993c6823e6
commit 1781f41b3b
2 changed files with 116 additions and 140 deletions

View File

@@ -38,6 +38,10 @@ void ast_cli(int fd, char *fmt, ...)
#define RESULT_SHOWUSAGE 1 #define RESULT_SHOWUSAGE 1
#define RESULT_FAILURE 2 #define RESULT_FAILURE 2
#define CLI_SUCCESS (char *)RESULT_SUCCESS
#define CLI_SHOWUSAGE (char *)RESULT_SHOWUSAGE
#define CLI_FAILURE (char *)RESULT_FAILURE
#define AST_MAX_CMD_LEN 16 #define AST_MAX_CMD_LEN 16
#define AST_MAX_ARGS 64 #define AST_MAX_ARGS 64
@@ -67,67 +71,44 @@ void ast_cli(int fd, char *fmt, ...)
In the "new-style" format, all the above functionalities are implemented In the "new-style" format, all the above functionalities are implemented
by a single function, and the arguments tell which output is required. by a single function, and the arguments tell which output is required.
The prototype is the following:
\note \b Note: ideally, the new-style handler would have a different prototype, char *new_setdebug(const struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
i.e. something like
int new_setdebug(const struct ast_cli *e, int function,
int fd, int argc, char *argv[], // handler args
int n, int pos, const char *line, const char *word // -complete args)
but at this moment we want to help the transition from old-style to new-style
functions so we keep the same interface and override some of the traditional
arguments.
To help the transition, a new-style entry has the same interface as the old one,
but it is declared as follows:
int new_setdebug(int fd, int argc, char *argv[]);
... ...
// this is how we create the entry to register // this is how we create the entry to register
NEW_CLI(new_setdebug, "short description") NEW_CLI(new_setdebug, "short description")
... ...
Called with the default arguments (argc > 0), the new_handler implements To help the transition, we make the pointer to the struct ast_cli_entry
the command as before. available to old-style handlers via argv[-1].
A negative argc indicates one of the other functions, namely
generate the usage string, the full command, or implement the generator.
As a trick to extend the interface while being backward compatible,
argv[-1] points to a struct ast_cli_args, and, for the generator,
argv[0] is really a pointer to a struct ast_cli_args.
The return string is obtained by casting the result to char *
An example of new-style handler is the following An example of new-style handler is the following
\code \code
static int test_new_cli(int fd, int argc, char *argv[]) static char *test_new_cli(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{ {
struct ast_cli_entry *e = (struct ast_cli_entry *)argv[-1];
struct ast_cli_args *a;
static char *choices = { "one", "two", "three", NULL }; static char *choices = { "one", "two", "three", NULL };
switch(argc) { switch (cmd) {
case CLI_USAGE: case CLI_INIT:
return (int) e->command = "do this well";
e->usage =
"Usage: do this well <arg>\n" "Usage: do this well <arg>\n"
" typically multiline with body indented\n"; " typically multiline with body indented\n";
return NULL;
case CLI_CMD_STRING:
return (int)"do this well";
case CLI_GENERATE: case CLI_GENERATE:
a = (struct ast_cli_args *)argv[0];
if (a->pos > e->args) if (a->pos > e->args)
return NULL; return NULL;
return ast_cli_complete(a->word, choices, a->n); return ast_cli_complete(a->word, choices, a->n);
default: default:
// we are guaranteed to be called with argc >= e->args; // we are guaranteed to be called with argc >= e->args;
if (argc > e->args + 1) // we accept one extra argument if (a->argc > e->args + 1) // we accept one extra argument
return RESULT_SHOWUSAGE; return CLI_SHOWUSAGE;
ast_cli(fd, "done this well for %s\n", e->args[argc-1]); ast_cli(a->fd, "done this well for %s\n", e->args[argc-1]);
return RESULT_SUCCESS; return CLI_SUCCESS;
} }
} }
@@ -139,12 +120,25 @@ static int test_new_cli(int fd, int argc, char *argv[])
See \ref CLI_command_API See \ref CLI_command_API
*/ */
enum ast_cli_fn { enum ast_cli_fn {
CLI_USAGE = -1, /* return the usage string */ CLI_INIT = -2, /* return the usage string */
CLI_CMD_STRING = -2, /* return the command string */
CLI_GENERATE = -3, /* behave as 'generator', remap argv to struct ast_cli_args */ CLI_GENERATE = -3, /* behave as 'generator', remap argv to struct ast_cli_args */
CLI_HANDLER = -4, /* run the normal handler */
}; };
/* argument for new-style CLI handler */
struct ast_cli_args {
int fd;
int argc;
char **argv;
const char *line; /* the current input line */
const char *word; /* the word we want to complete */
int pos; /* position of the word to complete */
int n; /* the iteration count (n-th entry we generate) */
};
struct ast_cli_entry;
typedef int (*old_cli_fn)(int fd, int argc, char *argv[]); typedef int (*old_cli_fn)(int fd, int argc, char *argv[]);
typedef char *(*new_cli_fn)(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
/*! \brief descriptor for a cli entry /*! \brief descriptor for a cli entry
See \ref CLI_command_API See \ref CLI_command_API
@@ -161,10 +155,10 @@ struct ast_cli_entry {
You can overwrite argv or the strings it points to, but remember You can overwrite argv or the strings it points to, but remember
that this memory is deallocated after the handler returns. that this memory is deallocated after the handler returns.
*/ */
int (*handler)(int fd, int argc, char *argv[]); old_cli_fn handler;
const char *summary; /*!< Summary of the command (< 60 characters) */ const char *summary; /*!< Summary of the command (< 60 characters) */
const char *usage; /*!< Detailed usage information */ char *usage; /*!< Detailed usage information */
/*! Generate the n-th (starting from 0) possible completion /*! Generate the n-th (starting from 0) possible completion
for a given 'word' following 'line' in position 'pos'. for a given 'word' following 'line' in position 'pos'.
@@ -188,21 +182,13 @@ struct ast_cli_entry {
int args; /*!< number of non-null entries in cmda */ int args; /*!< number of non-null entries in cmda */
char *command; /*!< command, non-null for new-style entries */ char *command; /*!< command, non-null for new-style entries */
int deprecated; int deprecated;
new_cli_fn new_handler;
char *_deprecated_by; /*!< copied from the "parent" _full_cmd, on deprecated commands */ char *_deprecated_by; /*!< copied from the "parent" _full_cmd, on deprecated commands */
/*! For linking */ /*! For linking */
AST_LIST_ENTRY(ast_cli_entry) list; AST_LIST_ENTRY(ast_cli_entry) list;
}; };
#define NEW_CLI(fn, txt) { .handler = (old_cli_fn)fn, .summary = txt } #define NEW_CLI(fn, txt) { .new_handler = fn, .summary = txt }
/* argument for new-style CLI handler */
struct ast_cli_args {
char fake[4]; /* a fake string, in the first position, for safety */
const char *line; /* the current input line */
const char *word; /* the word we want to complete */
int pos; /* position of the word to complete */
int n; /* the iteration count (n-th entry we generate) */
};
/*! /*!
* Helper function to generate cli entries from a NULL-terminated array. * Helper function to generate cli entries from a NULL-terminated array.

View File

@@ -168,40 +168,38 @@ static int handle_reload_deprecated(int fd, int argc, char *argv[])
return handle_reload(fd, argc+1, argv-1); /* see comment in handle_load_deprecated() */ return handle_reload(fd, argc+1, argv-1); /* see comment in handle_load_deprecated() */
} }
static int handle_verbose(int fd, int argc, char *argv[]) static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{ {
/* "core set verbose [atleast] <n>" */
int oldval = option_verbose; int oldval = option_verbose;
int newlevel; int newlevel;
int atleast = 0; int atleast = 0;
struct ast_cli_entry *e = (struct ast_cli_entry *)argv[-1];
static char *choices[] = { "off", "atleast", NULL }; static char *choices[] = { "off", "atleast", NULL };
struct ast_cli_args *a; int fd = a->fd;
int argc = a->argc;
char **argv = a->argv;
switch (argc) { switch (cmd) {
case CLI_CMD_STRING: case CLI_INIT:
return (int)"core set verbose"; e->command = "core set verbose";
e->usage =
case CLI_USAGE:
return (int)
"Usage: core set verbose [atleast] <level>\n" "Usage: core set verbose [atleast] <level>\n"
" core set verbose off\n" " core set verbose off\n"
" Sets level of verbose messages to be displayed. 0 or off means\n" " Sets level of verbose messages to be displayed. 0 or off means\n"
" no messages should be displayed. Equivalent to -v[v[v...]]\n" " no messages should be displayed. Equivalent to -v[v[v...]]\n"
" on startup\n"; " on startup\n";
return NULL;
case CLI_GENERATE: case CLI_GENERATE:
a = (struct ast_cli_args *)argv[0];
if (a->pos > e->args) if (a->pos > e->args)
return (int)NULL; return NULL;
return (int)ast_cli_complete(a->word, choices, a->n); return ast_cli_complete(a->word, choices, a->n);
} }
/* all the above return, so we proceed with the handler. /* all the above return, so we proceed with the handler.
* we are guaranteed to be called with argc >= e->args; * we are guaranteed to be called with argc >= e->args;
*/ */
if (argc < e->args + 1) if (argc < e->args + 1)
return RESULT_SHOWUSAGE; return CLI_SHOWUSAGE;
if (argc == e->args + 1 && !strcasecmp(argv[e->args], "off")) { if (argc == e->args + 1 && !strcasecmp(argv[e->args], "off")) {
newlevel = 0; newlevel = 0;
@@ -210,9 +208,9 @@ static int handle_verbose(int fd, int argc, char *argv[])
if (!strcasecmp(argv[e->args], "atleast")) if (!strcasecmp(argv[e->args], "atleast"))
atleast = 1; atleast = 1;
if (argc != e->args + atleast + 1) if (argc != e->args + atleast + 1)
return RESULT_SHOWUSAGE; return CLI_SHOWUSAGE;
if (sscanf(argv[e->args + atleast], "%d", &newlevel) != 1) if (sscanf(argv[e->args + atleast], "%d", &newlevel) != 1)
return RESULT_SHOWUSAGE; return CLI_SHOWUSAGE;
done: done:
if (!atleast || newlevel > option_verbose) if (!atleast || newlevel > option_verbose)
@@ -226,44 +224,43 @@ done:
ast_cli(fd, "Verbosity was %d and is now %d\n", oldval, option_verbose); ast_cli(fd, "Verbosity was %d and is now %d\n", oldval, option_verbose);
} }
return RESULT_SUCCESS; return CLI_SUCCESS;
} }
static int handle_set_debug(int fd, int argc, char *argv[]) static char *handle_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{ {
struct ast_cli_entry *e = (struct ast_cli_entry *)argv[-1];
int oldval = option_debug; int oldval = option_debug;
int newlevel; int newlevel;
int atleast = 0; int atleast = 0;
char *filename = '\0'; char *filename = '\0';
static char *choices[] = { "off", "atleast", NULL }; static char *choices[] = { "off", "atleast", NULL };
struct ast_cli_args *a; int fd = a->fd;
int argc = a->argc;
char **argv = a->argv;
switch (argc) { switch (cmd) {
case CLI_CMD_STRING: case CLI_INIT:
return (int)"core set debug"; e->command = "core set debug";
e->usage =
case CLI_USAGE:
return (int)
"Usage: core set debug [atleast] <level> [filename]\n" "Usage: core set debug [atleast] <level> [filename]\n"
" core set debug off\n" " core set debug off\n"
" Sets level of core debug messages to be displayed. 0 or 'off' means\n" " Sets level of core debug messages to be displayed. 0 or 'off' means\n"
" no messages should be displayed. Equivalent to -d[d[d...]]\n" " no messages should be displayed. Equivalent to -d[d[d...]]\n"
" on startup. If filename is specified, debugging will be\n" " on startup. If filename is specified, debugging will be\n"
" limited to just that file.\n"; " limited to just that file.\n";
return NULL;
case CLI_GENERATE: case CLI_GENERATE:
a = (struct ast_cli_args *)argv[0];
if (a->pos > e->args) if (a->pos > e->args)
return (int)NULL; return NULL;
return (int)ast_cli_complete(a->word, choices, a->n); return ast_cli_complete(a->word, choices, a->n);
} }
/* all the above return, so we proceed with the handler. /* all the above return, so we proceed with the handler.
* we are guaranteed to be called with argc >= e->args; * we are guaranteed to be called with argc >= e->args;
*/ */
if (argc < e->args + 1) if (argc < e->args + 1)
return RESULT_SHOWUSAGE; return CLI_SHOWUSAGE;
if (argc == e->args + 1 && !strcasecmp(argv[e->args], "off")) { if (argc == e->args + 1 && !strcasecmp(argv[e->args], "off")) {
newlevel = 0; newlevel = 0;
@@ -272,9 +269,9 @@ static int handle_set_debug(int fd, int argc, char *argv[])
if (!strcasecmp(argv[e->args], "atleast")) if (!strcasecmp(argv[e->args], "atleast"))
atleast = 1; atleast = 1;
if (argc < e->args + atleast + 1 || argc > e->args + atleast + 2) if (argc < e->args + atleast + 1 || argc > e->args + atleast + 2)
return RESULT_SHOWUSAGE; return CLI_SHOWUSAGE;
if (sscanf(argv[e->args + atleast], "%d", &newlevel) != 1) if (sscanf(argv[e->args + atleast], "%d", &newlevel) != 1)
return RESULT_SHOWUSAGE; return CLI_SHOWUSAGE;
if (argc == e->args + atleast + 1) { if (argc == e->args + atleast + 1) {
debug_filename[0] = '\0'; debug_filename[0] = '\0';
@@ -302,7 +299,7 @@ done:
} }
} }
return RESULT_SUCCESS; return CLI_SUCCESS;
} }
static int handle_logger_mute(int fd, int argc, char *argv[]) static int handle_logger_mute(int fd, int argc, char *argv[])
@@ -415,80 +412,72 @@ static void print_uptimestr(int fd, time_t timeval, const char *prefix, int prin
ast_cli(fd, "%s: %s\n", prefix, timestr); ast_cli(fd, "%s: %s\n", prefix, timestr);
} }
static int handle_showuptime(int fd, int argc, char *argv[]) static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{ {
struct ast_cli_entry *e = (struct ast_cli_entry *)argv[-1];
time_t curtime; time_t curtime;
int printsec; int printsec;
struct ast_cli_args *a;
switch (argc) { switch (cmd) {
case CLI_CMD_STRING: case CLI_INIT:
return (int)"core show uptime"; e->command = "core show uptime";
e->usage =
case CLI_USAGE:
return (int)
"Usage: core show uptime [seconds]\n" "Usage: core show uptime [seconds]\n"
" Shows Asterisk uptime information.\n" " Shows Asterisk uptime information.\n"
" The seconds word returns the uptime in seconds only.\n"; " The seconds word returns the uptime in seconds only.\n";
return NULL;
case CLI_GENERATE: case CLI_GENERATE:
a = (struct ast_cli_args *)argv[0]; return (a->pos > e->args || a->n > 0) ? NULL : "seconds";
return (int)((a->pos > e->args || a->n > 0) ? NULL : "seconds");
} }
/* regular handler */ /* regular handler */
if (argc == e->args+1 && !strcasecmp(argv[e->args],"seconds")) if (a->argc == e->args+1 && !strcasecmp(a->argv[e->args],"seconds"))
printsec = 1; printsec = 1;
else if (argc == e->args) else if (a->argc == e->args)
printsec = 0; printsec = 0;
else else
return RESULT_SHOWUSAGE; return CLI_SHOWUSAGE;
curtime = time(NULL); curtime = time(NULL);
if (ast_startuptime) if (ast_startuptime)
print_uptimestr(fd, curtime - ast_startuptime, "System uptime", printsec); print_uptimestr(a->fd, curtime - ast_startuptime, "System uptime", printsec);
if (ast_lastreloadtime) if (ast_lastreloadtime)
print_uptimestr(fd, curtime - ast_lastreloadtime, "Last reload", printsec); print_uptimestr(a->fd, curtime - ast_lastreloadtime, "Last reload", printsec);
return RESULT_SUCCESS; return CLI_SUCCESS;
} }
static int handle_modlist(int fd, int argc, char *argv[]) static char *handle_modlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{ {
struct ast_cli_entry *e = (struct ast_cli_entry *)argv[-1];
char *like; char *like;
struct ast_cli_args *a;
switch(argc) { switch (cmd) {
case CLI_CMD_STRING: case CLI_INIT:
return (int)"module show"; e->command = "module show";
e->usage =
case CLI_USAGE:
return (int)
"Usage: module show [like keyword]\n" "Usage: module show [like keyword]\n"
" Shows Asterisk modules currently in use, and usage statistics.\n"; " Shows Asterisk modules currently in use, and usage statistics.\n";
return NULL;
case CLI_GENERATE: case CLI_GENERATE:
a = (struct ast_cli_args *)argv[0];
if (a->pos == e->args) if (a->pos == e->args)
return (int)(a->n == 0 ? strdup("like") : NULL); return a->n == 0 ? strdup("like") : NULL;
else if (a->pos == e->args+1 && strcasestr(a->line," like ")) else if (a->pos == e->args+1 && strcasestr(a->line," like "))
return (int)ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0); return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
else else
return (int)NULL; return NULL;
} }
/* all the above return, so we proceed with the handler. /* all the above return, so we proceed with the handler.
* we are guaranteed to have argc >= e->args * we are guaranteed to have argc >= e->args
*/ */
if (argc == e->args) if (a->argc == e->args)
like = ""; like = "";
else if (argc == e->args + 2 && !strcmp(argv[e->args],"like")) else if (a->argc == e->args + 2 && !strcmp(a->argv[e->args],"like"))
like = argv[e->args + 1]; like = a->argv[e->args + 1];
else else
return RESULT_SHOWUSAGE; return CLI_SHOWUSAGE;
ast_mutex_lock(&climodentrylock); ast_mutex_lock(&climodentrylock);
climodentryfd = fd; /* global, protected by climodentrylock */ climodentryfd = a->fd; /* global, protected by climodentrylock */
ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count"); ast_cli(a->fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
ast_cli(fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like)); ast_cli(a->fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
climodentryfd = -1; climodentryfd = -1;
ast_mutex_unlock(&climodentrylock); ast_mutex_unlock(&climodentrylock);
return RESULT_SUCCESS; return RESULT_SUCCESS;
@@ -1281,9 +1270,9 @@ static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *e
AST_LIST_UNLOCK(&helpers); AST_LIST_UNLOCK(&helpers);
free(e->_full_cmd); free(e->_full_cmd);
e->_full_cmd = NULL; e->_full_cmd = NULL;
if (e->command) { if (e->new_handler) {
/* this is a new-style entry. Reset fields and free memory. */ /* this is a new-style entry. Reset fields and free memory. */
((char **)e->cmda)[0] = NULL; bzero((char **)(e->cmda), sizeof(e->cmda));
free(e->command); free(e->command);
e->command = NULL; e->command = NULL;
e->usage = NULL; e->usage = NULL;
@@ -1298,12 +1287,15 @@ static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
char fulle[80] =""; char fulle[80] ="";
int i, lf, ret = -1; int i, lf, ret = -1;
if (e->cmda[0] == NULL) { /* new style entry, run the handler to init fields */ if (e->handler == NULL) { /* new style entry, run the handler to init fields */
char *args[2] = { (char *)e, NULL }; struct ast_cli_args a; /* fake argument */
char *s = (char *)(e->handler(-1, CLI_CMD_STRING, args+1));
char **dst = (char **)e->cmda; /* need to cast as the entry is readonly */ char **dst = (char **)e->cmda; /* need to cast as the entry is readonly */
char *s;
s = ast_skip_blanks(s); 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); s = e->command = ast_strdup(s);
for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) { for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) {
*dst++ = s; /* store string */ *dst++ = s; /* store string */
@@ -1314,7 +1306,6 @@ static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
s = ast_skip_blanks(s); s = ast_skip_blanks(s);
} }
*dst++ = NULL; *dst++ = NULL;
e->usage = (char *)(e->handler(-1, CLI_USAGE, args+1));
} }
for (i = 0; e->cmda[i]; i++) for (i = 0; e->cmda[i]; i++)
; ;
@@ -1638,21 +1629,12 @@ static char *__ast_cli_generator(const char *text, const char *word, int state,
*/ */
if (e->generator) if (e->generator)
ret = e->generator(matchstr, word, argindex, state - matchnum); ret = e->generator(matchstr, word, argindex, state - matchnum);
else if (e->command) { /* new style command */ else if (e->new_handler) { /* new style command */
/* prepare fake arguments for the generator.
* argv[-1] is the cli entry we use,
* argv[0] is a pointer to the generator arguments,
* with a fake string '-' at the beginning so we can
* dereference it as a string with no trouble,
* and then the usual NULL terminator.
*/
struct ast_cli_args a = { struct ast_cli_args a = {
.fake = "-",
.line = matchstr, .word = word, .line = matchstr, .word = word,
.pos = argindex, .pos = argindex,
.n = state - matchnum }; .n = state - matchnum };
char *args[] = { (char *)e, (char *)&a, NULL }; ret = e->new_handler(e, CLI_GENERATE, &a);
ret = (char *)e->handler(-1, CLI_GENERATE, args + 1);
} }
if (ret) if (ret)
break; break;
@@ -1688,11 +1670,19 @@ int ast_cli_command(int fd, const char *s)
e->inuse++; e->inuse++;
AST_LIST_UNLOCK(&helpers); AST_LIST_UNLOCK(&helpers);
if (e) { if (e) {
int res;
/* within calling the handler, argv[-1] contains a pointer /* within calling the handler, argv[-1] contains a pointer
* to the cli entry, and the array is null-terminated * to the cli entry, and the array is null-terminated
*/ */
args[0] = (char *)e; args[0] = (char *)e;
switch(e->handler(fd, x, args + 1)) { if (e->new_handler) { /* new style */
struct ast_cli_args a = {
.fd = fd, .argc = x, .argv = args+1 };
res = (int)e->new_handler(e, CLI_HANDLER, &a);
} else { /* old style */
res = e->handler(fd, x, args + 1);
}
switch (res) {
case RESULT_SHOWUSAGE: case RESULT_SHOWUSAGE:
if (e->usage) if (e->usage)
ast_cli(fd, "%s", e->usage); ast_cli(fd, "%s", e->usage);