diff --git a/include/asterisk/cli.h b/include/asterisk/cli.h index 3ecc7337bb39d99f3b60ef2c628fa952ba03d122..a718eb415a0e618f72738e51cb3f7ad4c8e08e1a 100644 --- a/include/asterisk/cli.h +++ b/include/asterisk/cli.h @@ -38,6 +38,10 @@ void ast_cli(int fd, char *fmt, ...) #define RESULT_SHOWUSAGE 1 #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_ARGS 64 @@ -67,67 +71,44 @@ void ast_cli(int fd, char *fmt, ...) In the "new-style" format, all the above functionalities are implemented 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, - 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[]); + char *new_setdebug(const struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); ... // this is how we create the entry to register NEW_CLI(new_setdebug, "short description") ... - Called with the default arguments (argc > 0), the new_handler implements - the command as before. - 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 * + To help the transition, we make the pointer to the struct ast_cli_entry + available to old-style handlers via argv[-1]. An example of new-style handler is the following \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 }; - switch(argc) { - case CLI_USAGE: - return (int) + switch (cmd) { + case CLI_INIT: + e->command = "do this well"; + e->usage = "Usage: do this well <arg>\n" " typically multiline with body indented\n"; - - case CLI_CMD_STRING: - return (int)"do this well"; + return NULL; case CLI_GENERATE: - a = (struct ast_cli_args *)argv[0]; if (a->pos > e->args) return NULL; return ast_cli_complete(a->word, choices, a->n); default: // we are guaranteed to be called with argc >= e->args; - if (argc > e->args + 1) // we accept one extra argument - return RESULT_SHOWUSAGE; - ast_cli(fd, "done this well for %s\n", e->args[argc-1]); - return RESULT_SUCCESS; + if (a->argc > e->args + 1) // we accept one extra argument + return CLI_SHOWUSAGE; + ast_cli(a->fd, "done this well for %s\n", e->args[argc-1]); + return CLI_SUCCESS; } } @@ -139,12 +120,25 @@ static int test_new_cli(int fd, int argc, char *argv[]) See \ref CLI_command_API */ enum ast_cli_fn { - CLI_USAGE = -1, /* return the usage string */ - CLI_CMD_STRING = -2, /* return the command string */ + CLI_INIT = -2, /* return the usage string */ 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 char *(*new_cli_fn)(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); /*! \brief descriptor for a cli entry 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 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 *usage; /*!< Detailed usage information */ + char *usage; /*!< Detailed usage information */ /*! Generate the n-th (starting from 0) possible completion 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 */ char *command; /*!< command, non-null for new-style entries */ int deprecated; + new_cli_fn new_handler; char *_deprecated_by; /*!< copied from the "parent" _full_cmd, on deprecated commands */ /*! For linking */ AST_LIST_ENTRY(ast_cli_entry) list; }; -#define NEW_CLI(fn, txt) { .handler = (old_cli_fn)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) */ -}; +#define NEW_CLI(fn, txt) { .new_handler = fn, .summary = txt } /*! * Helper function to generate cli entries from a NULL-terminated array. diff --git a/main/cli.c b/main/cli.c index 32bf8884c76506b183d97a9a19a4dba17943d0f7..3c0e80879fc1118d7247be5961237ed89c7e41a7 100644 --- a/main/cli.c +++ b/main/cli.c @@ -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() */ } -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 newlevel; int atleast = 0; - struct ast_cli_entry *e = (struct ast_cli_entry *)argv[-1]; static char *choices[] = { "off", "atleast", NULL }; - struct ast_cli_args *a; - - switch (argc) { - case CLI_CMD_STRING: - return (int)"core set verbose"; - - case CLI_USAGE: - return (int) + int fd = a->fd; + int argc = a->argc; + char **argv = a->argv; + + switch (cmd) { + case CLI_INIT: + e->command = "core set verbose"; + e->usage = "Usage: core set verbose [atleast] <level>\n" " core set verbose off\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" " on startup\n"; + return NULL; case CLI_GENERATE: - a = (struct ast_cli_args *)argv[0]; if (a->pos > e->args) - return (int)NULL; - return (int)ast_cli_complete(a->word, choices, a->n); + return NULL; + return ast_cli_complete(a->word, choices, a->n); } /* all the above return, so we proceed with the handler. * we are guaranteed to be called with argc >= e->args; */ if (argc < e->args + 1) - return RESULT_SHOWUSAGE; + return CLI_SHOWUSAGE; if (argc == e->args + 1 && !strcasecmp(argv[e->args], "off")) { newlevel = 0; @@ -210,9 +208,9 @@ static int handle_verbose(int fd, int argc, char *argv[]) if (!strcasecmp(argv[e->args], "atleast")) atleast = 1; if (argc != e->args + atleast + 1) - return RESULT_SHOWUSAGE; + return CLI_SHOWUSAGE; if (sscanf(argv[e->args + atleast], "%d", &newlevel) != 1) - return RESULT_SHOWUSAGE; + return CLI_SHOWUSAGE; done: if (!atleast || newlevel > option_verbose) @@ -226,44 +224,43 @@ done: 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 newlevel; int atleast = 0; char *filename = '\0'; static char *choices[] = { "off", "atleast", NULL }; - struct ast_cli_args *a; - - switch (argc) { - case CLI_CMD_STRING: - return (int)"core set debug"; - - case CLI_USAGE: - return (int) + int fd = a->fd; + int argc = a->argc; + char **argv = a->argv; + + switch (cmd) { + case CLI_INIT: + e->command = "core set debug"; + e->usage = "Usage: core set debug [atleast] <level> [filename]\n" " core set debug off\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" " on startup. If filename is specified, debugging will be\n" " limited to just that file.\n"; + return NULL; case CLI_GENERATE: - a = (struct ast_cli_args *)argv[0]; if (a->pos > e->args) - return (int)NULL; - return (int)ast_cli_complete(a->word, choices, a->n); + return NULL; + return ast_cli_complete(a->word, choices, a->n); } /* all the above return, so we proceed with the handler. * we are guaranteed to be called with argc >= e->args; */ if (argc < e->args + 1) - return RESULT_SHOWUSAGE; + return CLI_SHOWUSAGE; if (argc == e->args + 1 && !strcasecmp(argv[e->args], "off")) { newlevel = 0; @@ -272,9 +269,9 @@ static int handle_set_debug(int fd, int argc, char *argv[]) if (!strcasecmp(argv[e->args], "atleast")) atleast = 1; 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) - return RESULT_SHOWUSAGE; + return CLI_SHOWUSAGE; if (argc == e->args + atleast + 1) { 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[]) @@ -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); } -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; int printsec; - struct ast_cli_args *a; - - switch (argc) { - case CLI_CMD_STRING: - return (int)"core show uptime"; - case CLI_USAGE: - return (int) + switch (cmd) { + case CLI_INIT: + e->command = "core show uptime"; + e->usage = "Usage: core show uptime [seconds]\n" " Shows Asterisk uptime information.\n" " The seconds word returns the uptime in seconds only.\n"; + return NULL; case CLI_GENERATE: - a = (struct ast_cli_args *)argv[0]; - return (int)((a->pos > e->args || a->n > 0) ? NULL : "seconds"); + return (a->pos > e->args || a->n > 0) ? NULL : "seconds"; } /* 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; - else if (argc == e->args) + else if (a->argc == e->args) printsec = 0; else - return RESULT_SHOWUSAGE; + return CLI_SHOWUSAGE; curtime = time(NULL); 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) - print_uptimestr(fd, curtime - ast_lastreloadtime, "Last reload", printsec); - return RESULT_SUCCESS; + print_uptimestr(a->fd, curtime - ast_lastreloadtime, "Last reload", printsec); + 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; - struct ast_cli_args *a; - switch(argc) { - case CLI_CMD_STRING: - return (int)"module show"; - - case CLI_USAGE: - return (int) + switch (cmd) { + case CLI_INIT: + e->command = "module show"; + e->usage = "Usage: module show [like keyword]\n" " Shows Asterisk modules currently in use, and usage statistics.\n"; + return NULL; case CLI_GENERATE: - a = (struct ast_cli_args *)argv[0]; 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 ")) - 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 - return (int)NULL; + return NULL; } /* all the above return, so we proceed with the handler. * we are guaranteed to have argc >= e->args */ - if (argc == e->args) + if (a->argc == e->args) like = ""; - else if (argc == e->args + 2 && !strcmp(argv[e->args],"like")) - like = argv[e->args + 1]; + else if (a->argc == e->args + 2 && !strcmp(a->argv[e->args],"like")) + like = a->argv[e->args + 1]; else - return RESULT_SHOWUSAGE; + return CLI_SHOWUSAGE; ast_mutex_lock(&climodentrylock); - climodentryfd = fd; /* global, protected by climodentrylock */ - ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count"); - ast_cli(fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like)); + climodentryfd = a->fd; /* global, protected by climodentrylock */ + ast_cli(a->fd, MODLIST_FORMAT2, "Module", "Description", "Use Count"); + ast_cli(a->fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like)); climodentryfd = -1; ast_mutex_unlock(&climodentrylock); 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); free(e->_full_cmd); e->_full_cmd = NULL; - if (e->command) { + if (e->new_handler) { /* 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); e->command = 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] =""; int i, lf, ret = -1; - if (e->cmda[0] == NULL) { /* new style entry, run the handler to init fields */ - char *args[2] = { (char *)e, NULL }; - char *s = (char *)(e->handler(-1, CLI_CMD_STRING, args+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 */ + 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); for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) { *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); } *dst++ = NULL; - e->usage = (char *)(e->handler(-1, CLI_USAGE, args+1)); } 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) ret = e->generator(matchstr, word, argindex, state - matchnum); - else if (e->command) { /* 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. - */ + else if (e->new_handler) { /* new style command */ struct ast_cli_args a = { - .fake = "-", .line = matchstr, .word = word, .pos = argindex, .n = state - matchnum }; - char *args[] = { (char *)e, (char *)&a, NULL }; - ret = (char *)e->handler(-1, CLI_GENERATE, args + 1); + ret = e->new_handler(e, CLI_GENERATE, &a); } if (ret) break; @@ -1688,11 +1670,19 @@ int ast_cli_command(int fd, const char *s) e->inuse++; AST_LIST_UNLOCK(&helpers); if (e) { + int res; /* within calling the handler, argv[-1] contains a pointer * to the cli entry, and the array is null-terminated */ 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: if (e->usage) ast_cli(fd, "%s", e->usage);