Wed Aug 18 22:33:50 2010

Asterisk developer's documentation


cli.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Standard Command Line Interface
00022  *
00023  * \author Mark Spencer <markster@digium.com> 
00024  */
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 262802 $")
00029 
00030 #include "asterisk/_private.h"
00031 #include "asterisk/paths.h"   /* use ast_config_AST_MODULE_DIR */
00032 #include <sys/signal.h>
00033 #include <signal.h>
00034 #include <ctype.h>
00035 #include <regex.h>
00036 
00037 #include "asterisk/cli.h"
00038 #include "asterisk/linkedlists.h"
00039 #include "asterisk/module.h"
00040 #include "asterisk/pbx.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/utils.h"
00043 #include "asterisk/app.h"
00044 #include "asterisk/lock.h"
00045 #include "editline/readline/readline.h"
00046 #include "asterisk/threadstorage.h"
00047 
00048 /*!
00049  * \brief map a debug or verbose value to a filename
00050  */
00051 struct ast_debug_file {
00052    unsigned int level;
00053    AST_RWLIST_ENTRY(ast_debug_file) entry;
00054    char filename[0];
00055 };
00056 
00057 AST_RWLIST_HEAD(debug_file_list, ast_debug_file);
00058 
00059 /*! list of filenames and their debug settings */
00060 static struct debug_file_list debug_files;
00061 /*! list of filenames and their verbose settings */
00062 static struct debug_file_list verbose_files;
00063 
00064 AST_THREADSTORAGE(ast_cli_buf);
00065 
00066 /*! \brief Initial buffer size for resulting strings in ast_cli() */
00067 #define AST_CLI_INITLEN   256
00068 
00069 void ast_cli(int fd, const char *fmt, ...)
00070 {
00071    int res;
00072    struct ast_str *buf;
00073    va_list ap;
00074 
00075    if (!(buf = ast_str_thread_get(&ast_cli_buf, AST_CLI_INITLEN)))
00076       return;
00077 
00078    va_start(ap, fmt);
00079    res = ast_str_set_va(&buf, 0, fmt, ap);
00080    va_end(ap);
00081 
00082    if (res != AST_DYNSTR_BUILD_FAILED)
00083       ast_carefulwrite(fd, buf->str, strlen(buf->str), 100);
00084 }
00085 
00086 unsigned int ast_debug_get_by_file(const char *file) 
00087 {
00088    struct ast_debug_file *adf;
00089    unsigned int res = 0;
00090 
00091    AST_RWLIST_RDLOCK(&debug_files);
00092    AST_LIST_TRAVERSE(&debug_files, adf, entry) {
00093       if (!strncasecmp(adf->filename, file, strlen(adf->filename))) {
00094          res = adf->level;
00095          break;
00096       }
00097    }
00098    AST_RWLIST_UNLOCK(&debug_files);
00099 
00100    return res;
00101 }
00102 
00103 unsigned int ast_verbose_get_by_file(const char *file) 
00104 {
00105    struct ast_debug_file *adf;
00106    unsigned int res = 0;
00107 
00108    AST_RWLIST_RDLOCK(&verbose_files);
00109    AST_LIST_TRAVERSE(&verbose_files, adf, entry) {
00110       if (!strncasecmp(adf->filename, file, strlen(file))) {
00111          res = adf->level;
00112          break;
00113       }
00114    }
00115    AST_RWLIST_UNLOCK(&verbose_files);
00116 
00117    return res;
00118 }
00119 
00120 static AST_RWLIST_HEAD_STATIC(helpers, ast_cli_entry);
00121 
00122 static char *complete_fn(const char *word, int state)
00123 {
00124    char *c, *d;
00125    char filename[PATH_MAX];
00126 
00127    if (word[0] == '/')
00128       ast_copy_string(filename, word, sizeof(filename));
00129    else
00130       snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
00131 
00132    c = d = filename_completion_function(filename, state);
00133    
00134    if (c && word[0] != '/')
00135       c += (strlen(ast_config_AST_MODULE_DIR) + 1);
00136    if (c)
00137       c = ast_strdup(c);
00138 
00139    free(d);
00140    
00141    return c;
00142 }
00143 
00144 static char *handle_load(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00145 {
00146    /* "module load <mod>" */
00147    switch (cmd) {
00148    case CLI_INIT:
00149       e->command = "module load";
00150       e->usage =
00151          "Usage: module load <module name>\n"
00152          "       Loads the specified module into Asterisk.\n";
00153       return NULL;
00154 
00155    case CLI_GENERATE:
00156       if (a->pos != e->args)
00157          return NULL;
00158       return complete_fn(a->word, a->n);
00159    }
00160    if (a->argc != e->args + 1)
00161       return CLI_SHOWUSAGE;
00162    if (ast_load_resource(a->argv[e->args])) {
00163       ast_cli(a->fd, "Unable to load module %s\n", a->argv[e->args]);
00164       return CLI_FAILURE;
00165    }
00166    ast_cli(a->fd, "Loaded %s\n", a->argv[e->args]);
00167    return CLI_SUCCESS;
00168 }
00169 
00170 static char *handle_load_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00171 {
00172    char *res = handle_load(e, cmd, a);
00173    if (cmd == CLI_INIT)
00174       e->command = "load";
00175    return res;
00176 }
00177 
00178 static char *handle_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00179 {
00180    int x;
00181 
00182    switch (cmd) {
00183    case CLI_INIT:
00184       e->command = "module reload";
00185       e->usage =
00186          "Usage: module reload [module ...]\n"
00187          "       Reloads configuration files for all listed modules which support\n"
00188          "       reloading, or for all supported modules if none are listed.\n";
00189       return NULL;
00190 
00191    case CLI_GENERATE:
00192       return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 1);
00193    }
00194    if (a->argc == e->args) {
00195       ast_module_reload(NULL);
00196       return CLI_SUCCESS;
00197    }
00198    for (x = e->args; x < a->argc; x++) {
00199       int res = ast_module_reload(a->argv[x]);
00200       /* XXX reload has multiple error returns, including -1 on error and 2 on success */
00201       switch (res) {
00202       case 0:
00203          ast_cli(a->fd, "No such module '%s'\n", a->argv[x]);
00204          break;
00205       case 1:
00206          ast_cli(a->fd, "Module '%s' does not support reload\n", a->argv[x]);
00207          break;
00208       }
00209    }
00210    return CLI_SUCCESS;
00211 }
00212 
00213 static char *handle_reload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00214 {
00215    char *s = handle_reload(e, cmd, a);
00216    if (cmd == CLI_INIT)    /* override command name */
00217       e->command = "reload";
00218    return s;
00219 }
00220 
00221 /*! 
00222  * \brief Find the debug or verbose file setting 
00223  * \arg debug 1 for debug, 0 for verbose
00224  */
00225 static struct ast_debug_file *find_debug_file(const char *fn, unsigned int debug)
00226 {
00227    struct ast_debug_file *df = NULL;
00228    struct debug_file_list *dfl = debug ? &debug_files : &verbose_files;
00229 
00230    AST_LIST_TRAVERSE(dfl, df, entry) {
00231       if (!strcasecmp(df->filename, fn))
00232          break;
00233    }
00234 
00235    return df;
00236 }
00237 
00238 static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00239 {
00240    int oldval;
00241    int newlevel;
00242    int atleast = 0;
00243    int fd = a->fd;
00244    int argc = a->argc;
00245    char **argv = a->argv;
00246    int *dst;
00247    char *what;
00248    struct debug_file_list *dfl;
00249    struct ast_debug_file *adf;
00250    char *fn;
00251 
00252    switch (cmd) {
00253    case CLI_INIT:
00254       e->command = "core set {debug|verbose} [off|atleast]";
00255       e->usage =
00256          "Usage: core set {debug|verbose} [atleast] <level> [filename]\n"
00257          "       core set {debug|verbose} off\n"
00258          "       Sets level of debug or verbose messages to be displayed or \n"
00259          "       sets a filename to display debug messages from.\n"
00260          "  0 or off means no messages should be displayed.\n"
00261          "  Equivalent to -d[d[...]] or -v[v[v...]] on startup\n";
00262       return NULL;
00263 
00264    case CLI_GENERATE:
00265       return NULL;
00266    }
00267    /* all the above return, so we proceed with the handler.
00268     * we are guaranteed to be called with argc >= e->args;
00269     */
00270 
00271    if (argc < e->args)
00272       return CLI_SHOWUSAGE;
00273    if (!strcasecmp(argv[e->args - 2], "debug")) {
00274       dst = &option_debug;
00275       oldval = option_debug;
00276       what = "Core debug";
00277    } else {
00278       dst = &option_verbose;
00279       oldval = option_verbose;
00280       what = "Verbosity";
00281    }
00282    if (argc == e->args && !strcasecmp(argv[e->args - 1], "off")) {
00283       unsigned int debug = (*what == 'C');
00284       newlevel = 0;
00285 
00286       dfl = debug ? &debug_files : &verbose_files;
00287 
00288       AST_RWLIST_WRLOCK(dfl);
00289       while ((adf = AST_RWLIST_REMOVE_HEAD(dfl, entry)))
00290          ast_free(adf);
00291       ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
00292       AST_RWLIST_UNLOCK(dfl);
00293 
00294       goto done;
00295    }
00296    if (!strcasecmp(argv[e->args-1], "atleast"))
00297       atleast = 1;
00298    if (argc != e->args + atleast && argc != e->args + atleast + 1)
00299       return CLI_SHOWUSAGE;
00300    if (sscanf(argv[e->args + atleast - 1], "%30d", &newlevel) != 1)
00301       return CLI_SHOWUSAGE;
00302    if (argc == e->args + atleast + 1) {
00303       unsigned int debug = (*what == 'C');
00304       dfl = debug ? &debug_files : &verbose_files;
00305 
00306       fn = argv[e->args + atleast];
00307 
00308       AST_RWLIST_WRLOCK(dfl);
00309 
00310       if ((adf = find_debug_file(fn, debug)) && !newlevel) {
00311          AST_RWLIST_REMOVE(dfl, adf, entry);
00312          if (AST_RWLIST_EMPTY(dfl))
00313             ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
00314          AST_RWLIST_UNLOCK(dfl);
00315          ast_cli(fd, "%s was %d and has been set to 0 for '%s'\n", what, adf->level, fn);
00316          ast_free(adf);
00317          return CLI_SUCCESS;
00318       }
00319 
00320       if (adf) {
00321          if ((atleast && newlevel < adf->level) || adf->level == newlevel) {
00322             ast_cli(fd, "%s is %d for '%s'\n", what, adf->level, fn);
00323             AST_RWLIST_UNLOCK(dfl);
00324             return CLI_SUCCESS;
00325          }
00326       } else if (!(adf = ast_calloc(1, sizeof(*adf) + strlen(fn) + 1))) {
00327          AST_RWLIST_UNLOCK(dfl);
00328          return CLI_FAILURE;
00329       }
00330 
00331       oldval = adf->level;
00332       adf->level = newlevel;
00333       strcpy(adf->filename, fn);
00334 
00335       ast_set_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
00336 
00337       AST_RWLIST_INSERT_TAIL(dfl, adf, entry);
00338       AST_RWLIST_UNLOCK(dfl);
00339 
00340       ast_cli(fd, "%s was %d and has been set to %d for '%s'\n", what, oldval, adf->level, adf->filename);
00341 
00342       return CLI_SUCCESS;
00343    }
00344 
00345 done:
00346    if (!atleast || newlevel > *dst)
00347       *dst = newlevel;
00348    if (oldval > 0 && *dst == 0)
00349       ast_cli(fd, "%s is now OFF\n", what);
00350    else if (*dst > 0) {
00351       if (oldval == *dst)
00352          ast_cli(fd, "%s is at least %d\n", what, *dst);
00353       else
00354          ast_cli(fd, "%s was %d and is now %d\n", what, oldval, *dst);
00355    }
00356 
00357    return CLI_SUCCESS;
00358 }
00359 
00360 static char *handle_logger_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00361 {
00362    switch (cmd) {
00363    case CLI_INIT:
00364       e->command = "logger mute";
00365       e->usage = 
00366          "Usage: logger mute\n"
00367          "       Disables logging output to the current console, making it possible to\n"
00368          "       gather information without being disturbed by scrolling lines.\n";
00369       return NULL;
00370    case CLI_GENERATE:
00371       return NULL;
00372    }
00373 
00374    if (a->argc < 2 || a->argc > 3)
00375       return CLI_SHOWUSAGE;
00376 
00377    if (a->argc == 3 && !strcasecmp(a->argv[2], "silent"))
00378       ast_console_toggle_mute(a->fd, 1);
00379    else
00380       ast_console_toggle_mute(a->fd, 0);
00381 
00382    return CLI_SUCCESS;
00383 }
00384 
00385 static char *handle_unload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00386 {
00387    /* "module unload mod_1 [mod_2 .. mod_N]" */
00388    int x;
00389    int force = AST_FORCE_SOFT;
00390    char *s;
00391 
00392    switch (cmd) {
00393    case CLI_INIT:
00394       e->command = "module unload";
00395       e->usage =
00396          "Usage: module unload [-f|-h] <module_1> [<module_2> ... ]\n"
00397          "       Unloads the specified module from Asterisk. The -f\n"
00398          "       option causes the module to be unloaded even if it is\n"
00399          "       in use (may cause a crash) and the -h module causes the\n"
00400          "       module to be unloaded even if the module says it cannot, \n"
00401          "       which almost always will cause a crash.\n";
00402       return NULL;
00403 
00404    case CLI_GENERATE:
00405       return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
00406    }
00407    if (a->argc < e->args + 1)
00408       return CLI_SHOWUSAGE;
00409    x = e->args;   /* first argument */
00410    s = a->argv[x];
00411    if (s[0] == '-') {
00412       if (s[1] == 'f')
00413          force = AST_FORCE_FIRM;
00414       else if (s[1] == 'h')
00415          force = AST_FORCE_HARD;
00416       else
00417          return CLI_SHOWUSAGE;
00418       if (a->argc < e->args + 2) /* need at least one module name */
00419          return CLI_SHOWUSAGE;
00420       x++;  /* skip this argument */
00421    }
00422 
00423    for (; x < a->argc; x++) {
00424       if (ast_unload_resource(a->argv[x], force)) {
00425          ast_cli(a->fd, "Unable to unload resource %s\n", a->argv[x]);
00426          return CLI_FAILURE;
00427       }
00428       ast_cli(a->fd, "Unloaded %s\n", a->argv[x]);
00429    }
00430 
00431    return CLI_SUCCESS;
00432 }
00433 
00434 static char *handle_unload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00435 {
00436    char *res = handle_unload(e, cmd, a);
00437    if (cmd == CLI_INIT)
00438       e->command = "unload";  /* XXX override */
00439    return res;
00440 }
00441 
00442 #define MODLIST_FORMAT  "%-30s %-40.40s %-10d\n"
00443 #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
00444 
00445 AST_MUTEX_DEFINE_STATIC(climodentrylock);
00446 static int climodentryfd = -1;
00447 
00448 static int modlist_modentry(const char *module, const char *description, int usecnt, const char *like)
00449 {
00450    /* Comparing the like with the module */
00451    if (strcasestr(module, like) ) {
00452       ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
00453       return 1;
00454    } 
00455    return 0;
00456 }
00457 
00458 static void print_uptimestr(int fd, struct timeval timeval, const char *prefix, int printsec)
00459 {
00460    int x; /* the main part - years, weeks, etc. */
00461    struct ast_str *out;
00462 
00463 #define SECOND (1)
00464 #define MINUTE (SECOND*60)
00465 #define HOUR (MINUTE*60)
00466 #define DAY (HOUR*24)
00467 #define WEEK (DAY*7)
00468 #define YEAR (DAY*365)
00469 #define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */
00470    if (timeval.tv_sec < 0) /* invalid, nothing to show */
00471       return;
00472 
00473    if (printsec)  {  /* plain seconds output */
00474       ast_cli(fd, "%s: %lu\n", prefix, (u_long)timeval.tv_sec);
00475       return;
00476    }
00477    out = ast_str_alloca(256);
00478    if (timeval.tv_sec > YEAR) {
00479       x = (timeval.tv_sec / YEAR);
00480       timeval.tv_sec -= (x * YEAR);
00481       ast_str_append(&out, 0, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00482    }
00483    if (timeval.tv_sec > WEEK) {
00484       x = (timeval.tv_sec / WEEK);
00485       timeval.tv_sec -= (x * WEEK);
00486       ast_str_append(&out, 0, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00487    }
00488    if (timeval.tv_sec > DAY) {
00489       x = (timeval.tv_sec / DAY);
00490       timeval.tv_sec -= (x * DAY);
00491       ast_str_append(&out, 0, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00492    }
00493    if (timeval.tv_sec > HOUR) {
00494       x = (timeval.tv_sec / HOUR);
00495       timeval.tv_sec -= (x * HOUR);
00496       ast_str_append(&out, 0, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00497    }
00498    if (timeval.tv_sec > MINUTE) {
00499       x = (timeval.tv_sec / MINUTE);
00500       timeval.tv_sec -= (x * MINUTE);
00501       ast_str_append(&out, 0, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00502    }
00503    x = timeval.tv_sec;
00504    if (x > 0 || out->used == 0)  /* if there is nothing, print 0 seconds */
00505       ast_str_append(&out, 0, "%d second%s ", x, ESS(x));
00506    ast_cli(fd, "%s: %s\n", prefix, out->str);
00507 }
00508 
00509 static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00510 {
00511    struct timeval curtime = ast_tvnow();
00512    int printsec;
00513 
00514    switch (cmd) {
00515    case CLI_INIT:
00516       e->command = "core show uptime [seconds]";
00517       e->usage =
00518          "Usage: core show uptime [seconds]\n"
00519          "       Shows Asterisk uptime information.\n"
00520          "       The seconds word returns the uptime in seconds only.\n";
00521       return NULL;
00522 
00523    case CLI_GENERATE:
00524       return NULL;
00525    }
00526    /* regular handler */
00527    if (a->argc == e->args && !strcasecmp(a->argv[e->args-1],"seconds"))
00528       printsec = 1;
00529    else if (a->argc == e->args-1)
00530       printsec = 0;
00531    else
00532       return CLI_SHOWUSAGE;
00533    if (ast_startuptime.tv_sec)
00534       print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
00535    if (ast_lastreloadtime.tv_sec)
00536       print_uptimestr(a->fd, ast_tvsub(curtime, ast_lastreloadtime), "Last reload", printsec);
00537    return CLI_SUCCESS;
00538 }
00539 
00540 static char *handle_modlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00541 {
00542    char *like;
00543 
00544    switch (cmd) {
00545    case CLI_INIT:
00546       e->command = "module show [like]";
00547       e->usage =
00548          "Usage: module show [like keyword]\n"
00549          "       Shows Asterisk modules currently in use, and usage statistics.\n";
00550       return NULL;
00551 
00552    case CLI_GENERATE:
00553       if (a->pos == e->args)
00554          return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
00555       else
00556          return NULL;
00557    }
00558    /* all the above return, so we proceed with the handler.
00559     * we are guaranteed to have argc >= e->args
00560     */
00561    if (a->argc == e->args - 1)
00562       like = "";
00563    else if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args-1], "like") )
00564       like = a->argv[e->args];
00565    else
00566       return CLI_SHOWUSAGE;
00567       
00568    ast_mutex_lock(&climodentrylock);
00569    climodentryfd = a->fd; /* global, protected by climodentrylock */
00570    ast_cli(a->fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
00571    ast_cli(a->fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
00572    climodentryfd = -1;
00573    ast_mutex_unlock(&climodentrylock);
00574    return CLI_SUCCESS;
00575 }
00576 #undef MODLIST_FORMAT
00577 #undef MODLIST_FORMAT2
00578 
00579 static char *handle_showcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00580 {
00581    struct timeval curtime = ast_tvnow();
00582    int showuptime, printsec;
00583 
00584    switch (cmd) {
00585    case CLI_INIT:
00586       e->command = "core show calls [uptime]";
00587       e->usage =
00588          "Usage: core show calls [uptime] [seconds]\n"
00589          "       Lists number of currently active calls and total number of calls\n"
00590          "       processed through PBX since last restart. If 'uptime' is specified\n"
00591          "       the system uptime is also displayed. If 'seconds' is specified in\n"
00592          "       addition to 'uptime', the system uptime is displayed in seconds.\n";
00593       return NULL;
00594 
00595    case CLI_GENERATE:
00596       if (a->pos != e->args)
00597          return NULL;
00598       return a->n == 0  ? ast_strdup("seconds") : NULL;
00599    }
00600 
00601    /* regular handler */
00602    if (a->argc >= e->args && !strcasecmp(a->argv[e->args-1],"uptime")) {
00603       showuptime = 1;
00604 
00605       if (a->argc == e->args+1 && !strcasecmp(a->argv[e->args],"seconds"))
00606          printsec = 1;
00607       else if (a->argc == e->args)
00608          printsec = 0;
00609       else
00610          return CLI_SHOWUSAGE;
00611    } else if (a->argc == e->args-1) {
00612       showuptime = 0;
00613       printsec = 0;
00614    } else
00615       return CLI_SHOWUSAGE;
00616 
00617    if (option_maxcalls) {
00618       ast_cli(a->fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
00619          ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
00620          ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
00621    } else {
00622       ast_cli(a->fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
00623    }
00624    
00625    ast_cli(a->fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
00626 
00627    if (ast_startuptime.tv_sec && showuptime) {
00628       print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
00629    }
00630 
00631    return RESULT_SUCCESS;
00632 }
00633 
00634 static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00635 {
00636 #define FORMAT_STRING  "%-20.20s %-20.20s %-7.7s %-30.30s\n"
00637 #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
00638 #define CONCISE_FORMAT_STRING  "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s!%s\n"
00639 #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"
00640 #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"
00641 
00642    struct ast_channel *c = NULL;
00643    int numchans = 0, concise = 0, verbose = 0, count = 0;
00644    int fd, argc;
00645    char **argv;
00646 
00647    switch (cmd) {
00648    case CLI_INIT:
00649       e->command = "core show channels [concise|verbose|count]";
00650       e->usage =
00651          "Usage: core show channels [concise|verbose|count]\n"
00652          "       Lists currently defined channels and some information about them. If\n"
00653          "       'concise' is specified, the format is abridged and in a more easily\n"
00654          "       machine parsable format. If 'verbose' is specified, the output includes\n"
00655          "       more and longer fields. If 'count' is specified only the channel and call\n"
00656          "       count is output.\n"
00657          "  The 'concise' option is deprecated and will be removed from future versions\n"
00658          "  of Asterisk.\n";
00659       return NULL;
00660 
00661    case CLI_GENERATE:
00662       return NULL;
00663    }
00664    fd = a->fd;
00665    argc = a->argc;
00666    argv = a->argv;
00667 
00668    if (a->argc == e->args) {
00669       if (!strcasecmp(argv[e->args-1],"concise"))
00670          concise = 1;
00671       else if (!strcasecmp(argv[e->args-1],"verbose"))
00672          verbose = 1;
00673       else if (!strcasecmp(argv[e->args-1],"count"))
00674          count = 1;
00675       else
00676          return CLI_SHOWUSAGE;
00677    } else if (a->argc != e->args - 1)
00678       return CLI_SHOWUSAGE;
00679 
00680    if (!count) {
00681       if (!concise && !verbose)
00682          ast_cli(fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
00683       else if (verbose)
00684          ast_cli(fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data", 
00685             "CallerID", "Duration", "Accountcode", "BridgedTo");
00686    }
00687 
00688    while ((c = ast_channel_walk_locked(c)) != NULL) {
00689       struct ast_channel *bc = ast_bridged_channel(c);
00690       char durbuf[10] = "-";
00691 
00692       if (!count) {
00693          if ((concise || verbose)  && c->cdr && !ast_tvzero(c->cdr->start)) {
00694             int duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
00695             if (verbose) {
00696                int durh = duration / 3600;
00697                int durm = (duration % 3600) / 60;
00698                int durs = duration % 60;
00699                snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
00700             } else {
00701                snprintf(durbuf, sizeof(durbuf), "%d", duration);
00702             }           
00703          }
00704          if (concise) {
00705             ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00706                c->appl ? c->appl : "(None)",
00707                S_OR(c->data, ""),   /* XXX different from verbose ? */
00708                S_OR(c->cid.cid_num, ""),
00709                S_OR(c->accountcode, ""),
00710                c->amaflags, 
00711                durbuf,
00712                bc ? bc->name : "(None)",
00713                c->uniqueid);
00714          } else if (verbose) {
00715             ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00716                c->appl ? c->appl : "(None)",
00717                c->data ? S_OR(c->data, "(Empty)" ): "(None)",
00718                S_OR(c->cid.cid_num, ""),
00719                durbuf,
00720                S_OR(c->accountcode, ""),
00721                bc ? bc->name : "(None)");
00722          } else {
00723             char locbuf[40] = "(None)";
00724             char appdata[40] = "(None)";
00725             
00726             if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten)) 
00727                snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
00728             if (c->appl)
00729                snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, S_OR(c->data, ""));
00730             ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
00731          }
00732       }
00733       numchans++;
00734       ast_channel_unlock(c);
00735    }
00736    if (!concise) {
00737       ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
00738       if (option_maxcalls)
00739          ast_cli(fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
00740             ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
00741             ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
00742       else
00743          ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
00744 
00745       ast_cli(fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
00746    }
00747    return CLI_SUCCESS;
00748    
00749 #undef FORMAT_STRING
00750 #undef FORMAT_STRING2
00751 #undef CONCISE_FORMAT_STRING
00752 #undef VERBOSE_FORMAT_STRING
00753 #undef VERBOSE_FORMAT_STRING2
00754 }
00755 
00756 static char *handle_softhangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00757 {
00758    struct ast_channel *c=NULL;
00759 
00760    switch (cmd) {
00761    case CLI_INIT:
00762       e->command = "soft hangup";
00763       e->usage =
00764          "Usage: soft hangup <channel>\n"
00765          "       Request that a channel be hung up. The hangup takes effect\n"
00766          "       the next time the driver reads or writes from the channel\n";
00767       return NULL;
00768    case CLI_GENERATE:
00769       return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
00770    }
00771    if (a->argc != 3)
00772       return CLI_SHOWUSAGE;
00773    c = ast_get_channel_by_name_locked(a->argv[2]);
00774    if (c) {
00775       ast_cli(a->fd, "Requested Hangup on channel '%s'\n", c->name);
00776       ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
00777       ast_channel_unlock(c);
00778    } else
00779       ast_cli(a->fd, "%s is not a known channel\n", a->argv[2]);
00780    return CLI_SUCCESS;
00781 }
00782 
00783 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
00784 
00785 static char *handle_commandmatchesarray(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00786 {
00787    char *buf, *obuf;
00788    int buflen = 2048;
00789    int len = 0;
00790    char **matches;
00791    int x, matchlen;
00792    
00793    switch (cmd) {
00794    case CLI_INIT:
00795       e->command = "_command matchesarray";
00796       e->usage = 
00797          "Usage: _command matchesarray \"<line>\" text \n"
00798          "       This function is used internally to help with command completion and should.\n"
00799          "       never be called by the user directly.\n";
00800       return NULL;
00801    case CLI_GENERATE:
00802       return NULL;
00803    }
00804 
00805    if (a->argc != 4)
00806       return CLI_SHOWUSAGE;
00807    if (!(buf = ast_malloc(buflen)))
00808       return CLI_FAILURE;
00809    buf[len] = '\0';
00810    matches = ast_cli_completion_matches(a->argv[2], a->argv[3]);
00811    if (matches) {
00812       for (x=0; matches[x]; x++) {
00813          matchlen = strlen(matches[x]) + 1;
00814          if (len + matchlen >= buflen) {
00815             buflen += matchlen * 3;
00816             obuf = buf;
00817             if (!(buf = ast_realloc(obuf, buflen))) 
00818                /* Memory allocation failure...  Just free old buffer and be done */
00819                ast_free(obuf);
00820          }
00821          if (buf)
00822             len += sprintf( buf + len, "%s ", matches[x]);
00823          ast_free(matches[x]);
00824          matches[x] = NULL;
00825       }
00826       ast_free(matches);
00827    }
00828 
00829    if (buf) {
00830       ast_cli(a->fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
00831       ast_free(buf);
00832    } else
00833       ast_cli(a->fd, "NULL\n");
00834 
00835    return CLI_SUCCESS;
00836 }
00837 
00838 
00839 
00840 static char *handle_commandnummatches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00841 {
00842    int matches = 0;
00843 
00844    switch (cmd) {
00845    case CLI_INIT:
00846       e->command = "_command nummatches";
00847       e->usage = 
00848          "Usage: _command nummatches \"<line>\" text \n"
00849          "       This function is used internally to help with command completion and should.\n"
00850          "       never be called by the user directly.\n";
00851       return NULL;
00852    case CLI_GENERATE:
00853       return NULL;
00854    }
00855 
00856    if (a->argc != 4)
00857       return CLI_SHOWUSAGE;
00858 
00859    matches = ast_cli_generatornummatches(a->argv[2], a->argv[3]);
00860 
00861    ast_cli(a->fd, "%d", matches);
00862 
00863    return CLI_SUCCESS;
00864 }
00865 
00866 static char *handle_commandcomplete(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00867 {
00868    char *buf;
00869    switch (cmd) {
00870    case CLI_INIT:
00871       e->command = "_command complete";
00872       e->usage = 
00873          "Usage: _command complete \"<line>\" text state\n"
00874          "       This function is used internally to help with command completion and should.\n"
00875          "       never be called by the user directly.\n";
00876       return NULL;
00877    case CLI_GENERATE:
00878       return NULL;
00879    }
00880    if (a->argc != 5)
00881       return CLI_SHOWUSAGE;
00882    buf = __ast_cli_generator(a->argv[2], a->argv[3], atoi(a->argv[4]), 0);
00883    if (buf) {
00884       ast_cli(a->fd, "%s", buf);
00885       ast_free(buf);
00886    } else
00887       ast_cli(a->fd, "NULL\n");
00888    return CLI_SUCCESS;
00889 }
00890 
00891 static char *handle_core_set_debug_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00892 {
00893    struct ast_channel *c = NULL;
00894    int is_all, is_off = 0;
00895 
00896    switch (cmd) {
00897    case CLI_INIT:
00898       e->command = "core set debug channel";
00899       e->usage =
00900          "Usage: core set debug channel <all|channel> [off]\n"
00901          "       Enables/disables debugging on all or on a specific channel.\n";
00902       return NULL;
00903 
00904    case CLI_GENERATE:
00905       /* XXX remember to handle the optional "off" */
00906       if (a->pos != e->args)
00907          return NULL;
00908       return a->n == 0 ? ast_strdup("all") : ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
00909    }
00910    /* 'core set debug channel {all|chan_id}' */
00911    if (a->argc == e->args + 2) {
00912       if (!strcasecmp(a->argv[e->args + 1], "off"))
00913          is_off = 1;
00914       else
00915          return CLI_SHOWUSAGE;
00916    } else if (a->argc != e->args + 1)
00917       return CLI_SHOWUSAGE;
00918 
00919    is_all = !strcasecmp("all", a->argv[e->args]);
00920    if (is_all) {
00921       if (is_off) {
00922          global_fin &= ~DEBUGCHAN_FLAG;
00923          global_fout &= ~DEBUGCHAN_FLAG;
00924       } else {
00925          global_fin |= DEBUGCHAN_FLAG;
00926          global_fout |= DEBUGCHAN_FLAG;
00927       }
00928       c = ast_channel_walk_locked(NULL);
00929    } else {
00930       c = ast_get_channel_by_name_locked(a->argv[e->args]);
00931       if (c == NULL)
00932          ast_cli(a->fd, "No such channel %s\n", a->argv[e->args]);
00933    }
00934    while (c) {
00935       if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
00936          if (is_off) {
00937             c->fin &= ~DEBUGCHAN_FLAG;
00938             c->fout &= ~DEBUGCHAN_FLAG;
00939          } else {
00940             c->fin |= DEBUGCHAN_FLAG;
00941             c->fout |= DEBUGCHAN_FLAG;
00942          }
00943          ast_cli(a->fd, "Debugging %s on channel %s\n", is_off ? "disabled" : "enabled", c->name);
00944       }
00945       ast_channel_unlock(c);
00946       if (!is_all)
00947          break;
00948       c = ast_channel_walk_locked(c);
00949    }
00950    ast_cli(a->fd, "Debugging on new channels is %s\n", is_off ? "disabled" : "enabled");
00951    return CLI_SUCCESS;
00952 }
00953 
00954 static char *handle_debugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00955 {
00956    char *res;
00957 
00958    if (cmd == CLI_HANDLER && a->argc != e->args + 1)
00959       return CLI_SHOWUSAGE;
00960    res = handle_core_set_debug_channel(e, cmd, a);
00961    if (cmd == CLI_INIT)
00962       e->command = "debug channel";
00963    return res;
00964 }
00965 
00966 static char *handle_nodebugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00967 {
00968    char *res;
00969    if (cmd == CLI_HANDLER) {
00970       if (a->argc != e->args + 1)
00971          return CLI_SHOWUSAGE;
00972       /* pretend we have an extra "off" at the end. We can do this as the array
00973        * is NULL terminated so we overwrite that entry.
00974        */
00975       a->argv[e->args+1] = "off";
00976       a->argc++;
00977    }
00978    res = handle_core_set_debug_channel(e, cmd, a);
00979    if (cmd == CLI_INIT)
00980       e->command = "no debug channel";
00981    return res;
00982 }
00983       
00984 static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00985 {
00986    struct ast_channel *c=NULL;
00987    struct timeval now;
00988    struct ast_str *out = ast_str_alloca(2048);
00989    char cdrtime[256];
00990    char nf[256], wf[256], rf[256];
00991    long elapsed_seconds=0;
00992    int hour=0, min=0, sec=0;
00993 #ifdef CHANNEL_TRACE
00994    int trace_enabled;
00995 #endif
00996 
00997    switch (cmd) {
00998    case CLI_INIT:
00999       e->command = "core show channel";
01000       e->usage = 
01001          "Usage: core show channel <channel>\n"
01002          "       Shows lots of information about the specified channel.\n";
01003       return NULL;
01004    case CLI_GENERATE:
01005       return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
01006    }
01007    
01008    if (a->argc != 4)
01009       return CLI_SHOWUSAGE;
01010    now = ast_tvnow();
01011    c = ast_get_channel_by_name_locked(a->argv[3]);
01012    if (!c) {
01013       ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
01014       return CLI_SUCCESS;
01015    }
01016    if (c->cdr) {
01017       elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01018       hour = elapsed_seconds / 3600;
01019       min = (elapsed_seconds % 3600) / 60;
01020       sec = elapsed_seconds % 60;
01021       snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
01022    } else
01023       strcpy(cdrtime, "N/A");
01024    ast_cli(a->fd, 
01025       " -- General --\n"
01026       "           Name: %s\n"
01027       "           Type: %s\n"
01028       "       UniqueID: %s\n"
01029       "      Caller ID: %s\n"
01030       " Caller ID Name: %s\n"
01031       "    DNID Digits: %s\n"
01032       "       Language: %s\n"
01033       "          State: %s (%d)\n"
01034       "          Rings: %d\n"
01035       "  NativeFormats: %s\n"
01036       "    WriteFormat: %s\n"
01037       "     ReadFormat: %s\n"
01038       " WriteTranscode: %s\n"
01039       "  ReadTranscode: %s\n"
01040       "1st File Descriptor: %d\n"
01041       "      Frames in: %d%s\n"
01042       "     Frames out: %d%s\n"
01043       " Time to Hangup: %ld\n"
01044       "   Elapsed Time: %s\n"
01045       "  Direct Bridge: %s\n"
01046       "Indirect Bridge: %s\n"
01047       " --   PBX   --\n"
01048       "        Context: %s\n"
01049       "      Extension: %s\n"
01050       "       Priority: %d\n"
01051       "     Call Group: %llu\n"
01052       "   Pickup Group: %llu\n"
01053       "    Application: %s\n"
01054       "           Data: %s\n"
01055       "    Blocking in: %s\n",
01056       c->name, c->tech->type, c->uniqueid,
01057       S_OR(c->cid.cid_num, "(N/A)"),
01058       S_OR(c->cid.cid_name, "(N/A)"),
01059       S_OR(c->cid.cid_dnid, "(N/A)"), 
01060       c->language,   
01061       ast_state2str(c->_state), c->_state, c->rings, 
01062       ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats), 
01063       ast_getformatname_multiple(wf, sizeof(wf), c->writeformat), 
01064       ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
01065       c->writetrans ? "Yes" : "No",
01066       c->readtrans ? "Yes" : "No",
01067       c->fds[0],
01068       c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01069       c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01070       (long)c->whentohangup.tv_sec,
01071       cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>", 
01072       c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
01073       ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
01074       (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
01075    
01076    if (pbx_builtin_serialize_variables(c, &out))
01077       ast_cli(a->fd,"      Variables:\n%s\n", out->str);
01078    if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1))
01079       ast_cli(a->fd,"  CDR Variables:\n%s\n", out->str);
01080 #ifdef CHANNEL_TRACE
01081    trace_enabled = ast_channel_trace_is_enabled(c);
01082    ast_cli(a->fd, "  Context Trace: %s\n", trace_enabled ? "Enabled" : "Disabled");
01083    if (trace_enabled && ast_channel_trace_serialize(c, &out))
01084       ast_cli(a->fd, "          Trace:\n%s\n", out->str);
01085 #endif
01086    ast_channel_unlock(c);
01087    return CLI_SUCCESS;
01088 }
01089 
01090 /*
01091  * helper function to generate CLI matches from a fixed set of values.
01092  * A NULL word is acceptable.
01093  */
01094 char *ast_cli_complete(const char *word, char *const choices[], int state)
01095 {
01096    int i, which = 0, len;
01097    len = ast_strlen_zero(word) ? 0 : strlen(word);
01098 
01099    for (i = 0; choices[i]; i++) {
01100       if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
01101          return ast_strdup(choices[i]);
01102    }
01103    return NULL;
01104 }
01105 
01106 char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
01107 {
01108    struct ast_channel *c = NULL;
01109    int which = 0;
01110    int wordlen;
01111    char notfound = '\0';
01112    char *ret = &notfound; /* so NULL can break the loop */
01113 
01114    if (pos != rpos)
01115       return NULL;
01116 
01117    wordlen = strlen(word); 
01118 
01119    while (ret == &notfound && (c = ast_channel_walk_locked(c))) {
01120       if (!strncasecmp(word, c->name, wordlen) && ++which > state)
01121          ret = ast_strdup(c->name);
01122       ast_channel_unlock(c);
01123    }
01124    return ret == &notfound ? NULL : ret;
01125 }
01126 
01127 static char *group_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01128 {
01129 #define FORMAT_STRING  "%-25s  %-20s  %-20s\n"
01130 
01131    struct ast_group_info *gi = NULL;
01132    int numchans = 0;
01133    regex_t regexbuf;
01134    int havepattern = 0;
01135 
01136    switch (cmd) {
01137    case CLI_INIT:
01138       e->command = "group show channels";
01139       e->usage = 
01140          "Usage: group show channels [pattern]\n"
01141          "       Lists all currently active channels with channel group(s) specified.\n"
01142          "       Optional regular expression pattern is matched to group names for each\n"
01143          "       channel.\n";
01144       return NULL;
01145    case CLI_GENERATE:
01146       return NULL;
01147    }
01148 
01149    if (a->argc < 3 || a->argc > 4)
01150       return CLI_SHOWUSAGE;
01151    
01152    if (a->argc == 4) {
01153       if (regcomp(&regexbuf, a->argv[3], REG_EXTENDED | REG_NOSUB))
01154          return CLI_SHOWUSAGE;
01155       havepattern = 1;
01156    }
01157 
01158    ast_cli(a->fd, FORMAT_STRING, "Channel", "Group", "Category");
01159 
01160    ast_app_group_list_rdlock();
01161    
01162    gi = ast_app_group_list_head();
01163    while (gi) {
01164       if (!havepattern || !regexec(&regexbuf, gi->group, 0, NULL, 0)) {
01165          ast_cli(a->fd, FORMAT_STRING, gi->chan->name, gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
01166          numchans++;
01167       }
01168       gi = AST_LIST_NEXT(gi, group_list);
01169    }
01170    
01171    ast_app_group_list_unlock();
01172    
01173    if (havepattern)
01174       regfree(&regexbuf);
01175 
01176    ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
01177    return CLI_SUCCESS;
01178 #undef FORMAT_STRING
01179 }
01180 
01181 static struct ast_cli_entry cli_debug_channel_deprecated = AST_CLI_DEFINE(handle_debugchan_deprecated, "Enable debugging on channel");
01182 static struct ast_cli_entry cli_module_load_deprecated = AST_CLI_DEFINE(handle_load_deprecated, "Load a module");
01183 static struct ast_cli_entry cli_module_reload_deprecated = AST_CLI_DEFINE(handle_reload_deprecated, "reload modules by name");
01184 static struct ast_cli_entry cli_module_unload_deprecated = AST_CLI_DEFINE(handle_unload_deprecated, "unload modules by name");
01185 
01186 static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
01187 
01188 static struct ast_cli_entry cli_cli[] = {
01189    /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
01190    AST_CLI_DEFINE(handle_commandcomplete, "Command complete"),
01191    AST_CLI_DEFINE(handle_commandnummatches, "Returns number of command matches"),
01192    AST_CLI_DEFINE(handle_commandmatchesarray, "Returns command matches array"),
01193 
01194    AST_CLI_DEFINE(handle_nodebugchan_deprecated, "Disable debugging on channel(s)"),
01195 
01196    AST_CLI_DEFINE(handle_chanlist, "Display information on channels"),
01197 
01198    AST_CLI_DEFINE(handle_showcalls, "Display information on calls"),
01199 
01200    AST_CLI_DEFINE(handle_showchan, "Display information on a specific channel"),
01201 
01202    AST_CLI_DEFINE(handle_core_set_debug_channel, "Enable/disable debugging on a channel",
01203       .deprecate_cmd = &cli_debug_channel_deprecated),
01204 
01205    AST_CLI_DEFINE(handle_verbose, "Set level of debug/verbose chattiness"),
01206 
01207    AST_CLI_DEFINE(group_show_channels, "Display active channels with group(s)"),
01208 
01209    AST_CLI_DEFINE(handle_help, "Display help list, or specific help on a command"),
01210 
01211    AST_CLI_DEFINE(handle_logger_mute, "Toggle logging output to a console"),
01212 
01213    AST_CLI_DEFINE(handle_modlist, "List modules and info"),
01214 
01215    AST_CLI_DEFINE(handle_load, "Load a module by name", .deprecate_cmd = &cli_module_load_deprecated),
01216 
01217    AST_CLI_DEFINE(handle_reload, "Reload configuration", .deprecate_cmd = &cli_module_reload_deprecated),
01218 
01219    AST_CLI_DEFINE(handle_unload, "Unload a module by name", .deprecate_cmd = &cli_module_unload_deprecated ),
01220 
01221    AST_CLI_DEFINE(handle_showuptime, "Show uptime information"),
01222 
01223    AST_CLI_DEFINE(handle_softhangup, "Request a hangup on a given channel"),
01224 };
01225 
01226 /*!
01227  * Some regexp characters in cli arguments are reserved and used as separators.
01228  */
01229 static const char cli_rsvd[] = "[]{}|*%";
01230 
01231 /*!
01232  * initialize the _full_cmd string and related parameters,
01233  * return 0 on success, -1 on error.
01234  */
01235 static int set_full_cmd(struct ast_cli_entry *e)
01236 {
01237    int i;
01238    char buf[80];
01239 
01240    ast_join(buf, sizeof(buf), e->cmda);
01241    e->_full_cmd = ast_strdup(buf);
01242    if (!e->_full_cmd) {
01243       ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
01244       return -1;
01245    }
01246    e->cmdlen = strcspn(e->_full_cmd, cli_rsvd);
01247    for (i = 0; e->cmda[i]; i++)
01248       ;
01249    e->args = i;
01250    return 0;
01251 }
01252 
01253 /*! \brief initialize the _full_cmd string in * each of the builtins. */
01254 void ast_builtins_init(void)
01255 {
01256    ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
01257 }
01258 
01259 static struct ast_cli_entry *cli_next(struct ast_cli_entry *e)
01260 {
01261    if (e) {
01262       return AST_LIST_NEXT(e, list);
01263    } else {
01264       return AST_LIST_FIRST(&helpers);
01265    }
01266 }
01267 
01268 /*!
01269  * match a word in the CLI entry.
01270  * returns -1 on mismatch, 0 on match of an optional word,
01271  * 1 on match of a full word.
01272  *
01273  * The pattern can be
01274  *   any_word     match for equal
01275  *   [foo|bar|baz]   optionally, one of these words
01276  *   {foo|bar|baz}   exactly, one of these words
01277  *   %         any word
01278  */
01279 static int word_match(const char *cmd, const char *cli_word)
01280 {
01281    int l;
01282    char *pos;
01283 
01284    if (ast_strlen_zero(cmd) || ast_strlen_zero(cli_word))
01285       return -1;
01286    if (!strchr(cli_rsvd, cli_word[0])) /* normal match */
01287       return (strcasecmp(cmd, cli_word) == 0) ? 1 : -1;
01288    /* regexp match, takes [foo|bar] or {foo|bar} */
01289    l = strlen(cmd);
01290    /* wildcard match - will extend in the future */
01291    if (l > 0 && cli_word[0] == '%') {
01292       return 1;   /* wildcard */
01293    }
01294    pos = strcasestr(cli_word, cmd);
01295    if (pos == NULL) /* not found, say ok if optional */
01296       return cli_word[0] == '[' ? 0 : -1;
01297    if (pos == cli_word) /* no valid match at the beginning */
01298       return -1;
01299    if (strchr(cli_rsvd, pos[-1]) && strchr(cli_rsvd, pos[l]))
01300       return 1;   /* valid match */
01301    return -1;  /* not found */
01302 }
01303 
01304 /*! \brief if word is a valid prefix for token, returns the pos-th
01305  * match as a malloced string, or NULL otherwise.
01306  * Always tell in *actual how many matches we got.
01307  */
01308 static char *is_prefix(const char *word, const char *token,
01309    int pos, int *actual)
01310 {
01311    int lw;
01312    char *s, *t1;
01313 
01314    *actual = 0;
01315    if (ast_strlen_zero(token))
01316       return NULL;
01317    if (ast_strlen_zero(word))
01318       word = "";  /* dummy */
01319    lw = strlen(word);
01320    if (strcspn(word, cli_rsvd) != lw)
01321       return NULL;   /* no match if word has reserved chars */
01322    if (strchr(cli_rsvd, token[0]) == NULL) { /* regular match */
01323       if (strncasecmp(token, word, lw))   /* no match */
01324          return NULL;
01325       *actual = 1;
01326       return (pos != 0) ? NULL : ast_strdup(token);
01327    }
01328    /* now handle regexp match */
01329 
01330    /* Wildcard always matches, so we never do is_prefix on them */
01331 
01332    t1 = ast_strdupa(token + 1);  /* copy, skipping first char */
01333    while (pos >= 0 && (s = strsep(&t1, cli_rsvd)) && *s) {
01334       if (*s == '%') /* wildcard */
01335          continue;
01336       if (strncasecmp(s, word, lw)) /* no match */
01337          continue;
01338       (*actual)++;
01339       if (pos-- == 0)
01340          return ast_strdup(s);
01341    }
01342    return NULL;
01343 }
01344 
01345 /*!
01346  * \brief locate a cli command in the 'helpers' list (which must be locked).
01347  * exact has 3 values:
01348  *      0       returns if the search key is equal or longer than the entry.
01349  *    note that trailing optional arguments are skipped.
01350  *      -1      true if the mismatch is on the last word XXX not true!
01351  *      1       true only on complete, exact match.
01352  *
01353  * The search compares word by word taking care of regexps in e->cmda
01354  */
01355 static struct ast_cli_entry *find_cli(char *const cmds[], int match_type)
01356 {
01357    int matchlen = -1;   /* length of longest match so far */
01358    struct ast_cli_entry *cand = NULL, *e=NULL;
01359 
01360    while ( (e = cli_next(e)) ) {
01361       /* word-by word regexp comparison */
01362       char * const *src = cmds;
01363       char * const *dst = e->cmda;
01364       int n = 0;
01365       for (;; dst++, src += n) {
01366          n = word_match(*src, *dst);
01367          if (n < 0)
01368             break;
01369       }
01370       if (ast_strlen_zero(*dst) || ((*dst)[0] == '[' && ast_strlen_zero(dst[1]))) {
01371          /* no more words in 'e' */
01372          if (ast_strlen_zero(*src)) /* exact match, cannot do better */
01373             break;
01374          /* Here, cmds has more words than the entry 'e' */
01375          if (match_type != 0) /* but we look for almost exact match... */
01376             continue;   /* so we skip this one. */
01377          /* otherwise we like it (case 0) */
01378       } else { /* still words in 'e' */
01379          if (ast_strlen_zero(*src))
01380             continue; /* cmds is shorter than 'e', not good */
01381          /* Here we have leftover words in cmds and 'e',
01382           * but there is a mismatch. We only accept this one if match_type == -1
01383           * and this is the last word for both.
01384           */
01385          if (match_type != -1 || !ast_strlen_zero(src[1]) ||
01386              !ast_strlen_zero(dst[1])) /* not the one we look for */
01387             continue;
01388          /* good, we are in case match_type == -1 and mismatch on last word */
01389       }
01390       if (src - cmds > matchlen) {  /* remember the candidate */
01391          matchlen = src - cmds;
01392          cand = e;
01393       }
01394    }
01395    return e ? e : cand;
01396 }
01397 
01398 static char *find_best(char *argv[])
01399 {
01400    static char cmdline[80];
01401    int x;
01402    /* See how close we get, then print the candidate */
01403    char *myargv[AST_MAX_CMD_LEN];
01404    for (x=0;x<AST_MAX_CMD_LEN;x++)
01405       myargv[x]=NULL;
01406    AST_RWLIST_RDLOCK(&helpers);
01407    for (x=0;argv[x];x++) {
01408       myargv[x] = argv[x];
01409       if (!find_cli(myargv, -1))
01410          break;
01411    }
01412    AST_RWLIST_UNLOCK(&helpers);
01413    ast_join(cmdline, sizeof(cmdline), myargv);
01414    return cmdline;
01415 }
01416 
01417 static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
01418 {
01419    if (e->deprecate_cmd) {
01420       __ast_cli_unregister(e->deprecate_cmd, e);
01421    }
01422    if (e->inuse) {
01423       ast_log(LOG_WARNING, "Can't remove command that is in use\n");
01424    } else {
01425       AST_RWLIST_WRLOCK(&helpers);
01426       AST_RWLIST_REMOVE(&helpers, e, list);
01427       AST_RWLIST_UNLOCK(&helpers);
01428       ast_free(e->_full_cmd);
01429       e->_full_cmd = NULL;
01430       if (e->handler) {
01431          /* this is a new-style entry. Reset fields and free memory. */
01432          char *cmda = (char *) e->cmda;
01433          memset(cmda, '\0', sizeof(e->cmda));
01434          ast_free(e->command);
01435          e->command = NULL;
01436          e->usage = NULL;
01437       }
01438    }
01439    return 0;
01440 }
01441 
01442 static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
01443 {
01444    struct ast_cli_entry *cur;
01445    int i, lf, ret = -1;
01446 
01447    struct ast_cli_args a;  /* fake argument */
01448    char **dst = (char **)e->cmda;   /* need to cast as the entry is readonly */
01449    char *s;
01450 
01451    memset(&a, '\0', sizeof(a));
01452    e->handler(e, CLI_INIT, &a);
01453    /* XXX check that usage and command are filled up */
01454    s = ast_skip_blanks(e->command);
01455    s = e->command = ast_strdup(s);
01456    for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) {
01457       *dst++ = s; /* store string */
01458       s = ast_skip_nonblanks(s);
01459       if (*s == '\0')   /* we are done */
01460          break;
01461       *s++ = '\0';
01462       s = ast_skip_blanks(s);
01463    }
01464    *dst++ = NULL;
01465    
01466    AST_RWLIST_WRLOCK(&helpers);
01467    
01468    if (find_cli(e->cmda, 1)) {
01469       ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", e->_full_cmd);
01470       goto done;
01471    }
01472    if (set_full_cmd(e))
01473       goto done;
01474    if (!ed) {
01475       e->deprecated = 0;
01476    } else {
01477       e->deprecated = 1;
01478       e->summary = ed->summary;
01479       e->usage = ed->usage;
01480       /* XXX If command A deprecates command B, and command B deprecates command C...
01481          Do we want to show command A or command B when telling the user to use new syntax?
01482          This currently would show command A.
01483          To show command B, you just need to always use ed->_full_cmd.
01484        */
01485       e->_deprecated_by = S_OR(ed->_deprecated_by, ed->_full_cmd);
01486    }
01487 
01488    lf = e->cmdlen;
01489    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
01490       int len = cur->cmdlen;
01491       if (lf < len)
01492          len = lf;
01493       if (strncasecmp(e->_full_cmd, cur->_full_cmd, len) < 0) {
01494          AST_RWLIST_INSERT_BEFORE_CURRENT(e, list); 
01495          break;
01496       }
01497    }
01498    AST_RWLIST_TRAVERSE_SAFE_END;
01499 
01500    if (!cur)
01501       AST_RWLIST_INSERT_TAIL(&helpers, e, list); 
01502    ret = 0; /* success */
01503 
01504 done:
01505    AST_RWLIST_UNLOCK(&helpers);
01506 
01507    if (e->deprecate_cmd) {
01508       /* This command deprecates another command.  Register that one also. */
01509       __ast_cli_register(e->deprecate_cmd, e);
01510    }
01511    
01512    return ret;
01513 }
01514 
01515 /* wrapper function, so we can unregister deprecated commands recursively */
01516 int ast_cli_unregister(struct ast_cli_entry *e)
01517 {
01518    return __ast_cli_unregister(e, NULL);
01519 }
01520 
01521 /* wrapper function, so we can register deprecated commands recursively */
01522 int ast_cli_register(struct ast_cli_entry *e)
01523 {
01524    return __ast_cli_register(e, NULL);
01525 }
01526 
01527 /*
01528  * register/unregister an array of entries.
01529  */
01530 int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
01531 {
01532    int i, res = 0;
01533 
01534    for (i = 0; i < len; i++)
01535       res |= ast_cli_register(e + i);
01536 
01537    return res;
01538 }
01539 
01540 int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
01541 {
01542    int i, res = 0;
01543 
01544    for (i = 0; i < len; i++)
01545       res |= ast_cli_unregister(e + i);
01546 
01547    return res;
01548 }
01549 
01550 
01551 /*! \brief helper for final part of handle_help
01552  *  if locked = 1, assume the list is already locked
01553  */
01554 static char *help1(int fd, char *match[], int locked)
01555 {
01556    char matchstr[80] = "";
01557    struct ast_cli_entry *e = NULL;
01558    int len = 0;
01559    int found = 0;
01560 
01561    if (match) {
01562       ast_join(matchstr, sizeof(matchstr), match);
01563       len = strlen(matchstr);
01564    }
01565    if (!locked)
01566       AST_RWLIST_RDLOCK(&helpers);
01567    while ( (e = cli_next(e)) ) {
01568       /* Hide commands that start with '_' */
01569       if (e->_full_cmd[0] == '_')
01570          continue;
01571       /* Hide commands that are marked as deprecated. */
01572       if (e->deprecated)
01573          continue;
01574       if (match && strncasecmp(matchstr, e->_full_cmd, len))
01575          continue;
01576       ast_cli(fd, "%30.30s %s\n", e->_full_cmd, S_OR(e->summary, "<no description available>"));
01577       found++;
01578    }
01579    if (!locked)
01580       AST_RWLIST_UNLOCK(&helpers);
01581    if (!found && matchstr[0])
01582       ast_cli(fd, "No such command '%s'.\n", matchstr);
01583    return CLI_SUCCESS;
01584 }
01585 
01586 static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01587 {
01588    char fullcmd[80];
01589    struct ast_cli_entry *my_e;
01590    char *res = CLI_SUCCESS;
01591 
01592    if (cmd == CLI_INIT) {
01593       e->command = "help";
01594       e->usage =
01595          "Usage: help [topic]\n"
01596          "       When called with a topic as an argument, displays usage\n"
01597          "       information on the given command. If called without a\n"
01598          "       topic, it provides a list of commands.\n";
01599       return NULL;
01600 
01601    } else if (cmd == CLI_GENERATE) {
01602       /* skip first 4 or 5 chars, "help " */
01603       int l = strlen(a->line);
01604 
01605       if (l > 5)
01606          l = 5;
01607       /* XXX watch out, should stop to the non-generator parts */
01608       return __ast_cli_generator(a->line + l, a->word, a->n, 0);
01609    }
01610    if (a->argc == 1)
01611       return help1(a->fd, NULL, 0);
01612 
01613    AST_RWLIST_RDLOCK(&helpers);
01614    my_e = find_cli(a->argv + 1, 1); /* try exact match first */
01615    if (!my_e) {
01616       res = help1(a->fd, a->argv + 1, 1 /* locked */);
01617       AST_RWLIST_UNLOCK(&helpers);
01618       return res;
01619    }
01620    if (my_e->usage)
01621       ast_cli(a->fd, "%s", my_e->usage);
01622    else {
01623       ast_join(fullcmd, sizeof(fullcmd), a->argv+1);
01624       ast_cli(a->fd, "No help text available for '%s'.\n", fullcmd);
01625    }
01626    AST_RWLIST_UNLOCK(&helpers);
01627    return res;
01628 }
01629 
01630 static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
01631 {
01632    char *duplicate, *cur;
01633    int x = 0;
01634    int quoted = 0;
01635    int escaped = 0;
01636    int whitespace = 1;
01637    int dummy = 0;
01638 
01639    if (trailingwhitespace == NULL)
01640       trailingwhitespace = &dummy;
01641    *trailingwhitespace = 0;
01642    if (s == NULL) /* invalid, though! */
01643       return NULL;
01644    /* make a copy to store the parsed string */
01645    if (!(duplicate = ast_strdup(s)))
01646       return NULL;
01647 
01648    cur = duplicate;
01649    /* scan the original string copying into cur when needed */
01650    for (; *s ; s++) {
01651       if (x >= max - 1) {
01652          ast_log(LOG_WARNING, "Too many arguments, truncating at %s\n", s);
01653          break;
01654       }
01655       if (*s == '"' && !escaped) {
01656          quoted = !quoted;
01657          if (quoted && whitespace) {
01658             /* start a quoted string from previous whitespace: new argument */
01659             argv[x++] = cur;
01660             whitespace = 0;
01661          }
01662       } else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) {
01663          /* If we are not already in whitespace, and not in a quoted string or
01664             processing an escape sequence, and just entered whitespace, then
01665             finalize the previous argument and remember that we are in whitespace
01666          */
01667          if (!whitespace) {
01668             *cur++ = '\0';
01669             whitespace = 1;
01670          }
01671       } else if (*s == '\\' && !escaped) {
01672          escaped = 1;
01673       } else {
01674          if (whitespace) {
01675             /* we leave whitespace, and are not quoted. So it's a new argument */
01676             argv[x++] = cur;
01677             whitespace = 0;
01678          }
01679          *cur++ = *s;
01680          escaped = 0;
01681       }
01682    }
01683    /* Null terminate */
01684    *cur++ = '\0';
01685    /* XXX put a NULL in the last argument, because some functions that take
01686     * the array may want a null-terminated array.
01687     * argc still reflects the number of non-NULL entries.
01688     */
01689    argv[x] = NULL;
01690    *argc = x;
01691    *trailingwhitespace = whitespace;
01692    return duplicate;
01693 }
01694 
01695 /*! \brief Return the number of unique matches for the generator */
01696 int ast_cli_generatornummatches(const char *text, const char *word)
01697 {
01698    int matches = 0, i = 0;
01699    char *buf = NULL, *oldbuf = NULL;
01700 
01701    while ((buf = ast_cli_generator(text, word, i++))) {
01702       if (!oldbuf || strcmp(buf,oldbuf))
01703          matches++;
01704       if (oldbuf)
01705          ast_free(oldbuf);
01706       oldbuf = buf;
01707    }
01708    if (oldbuf)
01709       ast_free(oldbuf);
01710    return matches;
01711 }
01712 
01713 char **ast_cli_completion_matches(const char *text, const char *word)
01714 {
01715    char **match_list = NULL, *retstr, *prevstr;
01716    size_t match_list_len, max_equal, which, i;
01717    int matches = 0;
01718 
01719    /* leave entry 0 free for the longest common substring */
01720    match_list_len = 1;
01721    while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
01722       if (matches + 1 >= match_list_len) {
01723          match_list_len <<= 1;
01724          if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
01725             return NULL;
01726       }
01727       match_list[++matches] = retstr;
01728    }
01729 
01730    if (!match_list)
01731       return match_list; /* NULL */
01732 
01733    /* Find the longest substring that is common to all results
01734     * (it is a candidate for completion), and store a copy in entry 0.
01735     */
01736    prevstr = match_list[1];
01737    max_equal = strlen(prevstr);
01738    for (which = 2; which <= matches; which++) {
01739       for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
01740          continue;
01741       max_equal = i;
01742    }
01743 
01744    if (!(retstr = ast_malloc(max_equal + 1)))
01745       return NULL;
01746    
01747    ast_copy_string(retstr, match_list[1], max_equal + 1);
01748    match_list[0] = retstr;
01749 
01750    /* ensure that the array is NULL terminated */
01751    if (matches + 1 >= match_list_len) {
01752       if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list))))
01753          return NULL;
01754    }
01755    match_list[matches + 1] = NULL;
01756 
01757    return match_list;
01758 }
01759 
01760 /*! \brief returns true if there are more words to match */
01761 static int more_words (char * const *dst)
01762 {
01763    int i;
01764    for (i = 0; dst[i]; i++) {
01765       if (dst[i][0] != '[')
01766          return -1;
01767    }
01768    return 0;
01769 }
01770    
01771 /*
01772  * generate the entry at position 'state'
01773  */
01774 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
01775 {
01776    char *argv[AST_MAX_ARGS];
01777    struct ast_cli_entry *e = NULL;
01778    int x = 0, argindex, matchlen;
01779    int matchnum=0;
01780    char *ret = NULL;
01781    char matchstr[80] = "";
01782    int tws = 0;
01783    /* Split the argument into an array of words */
01784    char *duplicate = parse_args(text, &x, argv, ARRAY_LEN(argv), &tws);
01785 
01786    if (!duplicate)   /* malloc error */
01787       return NULL;
01788 
01789    /* Compute the index of the last argument (could be an empty string) */
01790    argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
01791 
01792    /* rebuild the command, ignore terminating white space and flatten space */
01793    ast_join(matchstr, sizeof(matchstr)-1, argv);
01794    matchlen = strlen(matchstr);
01795    if (tws) {
01796       strcat(matchstr, " "); /* XXX */
01797       if (matchlen)
01798          matchlen++;
01799    }
01800    if (lock)
01801       AST_RWLIST_RDLOCK(&helpers);
01802    while ( (e = cli_next(e)) ) {
01803       /* XXX repeated code */
01804       int src = 0, dst = 0, n = 0;
01805 
01806       if (e->command[0] == '_')
01807          continue;
01808 
01809       /*
01810        * Try to match words, up to and excluding the last word, which
01811        * is either a blank or something that we want to extend.
01812        */
01813       for (;src < argindex; dst++, src += n) {
01814          n = word_match(argv[src], e->cmda[dst]);
01815          if (n < 0)
01816             break;
01817       }
01818 
01819       if (src != argindex && more_words(e->cmda + dst))  /* not a match */
01820          continue;
01821       ret = is_prefix(argv[src], e->cmda[dst], state - matchnum, &n);
01822       matchnum += n; /* this many matches here */
01823       if (ret) {
01824          /*
01825           * argv[src] is a valid prefix of the next word in this
01826           * command. If this is also the correct entry, return it.
01827           */
01828          if (matchnum > state)
01829             break;
01830          ast_free(ret);
01831          ret = NULL;
01832       } else if (ast_strlen_zero(e->cmda[dst])) {
01833          /*
01834           * This entry is a prefix of the command string entered
01835           * (only one entry in the list should have this property).
01836           * Run the generator if one is available. In any case we are done.
01837           */
01838          if (e->handler) { /* new style command */
01839             struct ast_cli_args a = {
01840                .line = matchstr, .word = word,
01841                .pos = argindex,
01842                .n = state - matchnum };
01843             ret = e->handler(e, CLI_GENERATE, &a);
01844          }
01845          if (ret)
01846             break;
01847       }
01848    }
01849    if (lock)
01850       AST_RWLIST_UNLOCK(&helpers);
01851    ast_free(duplicate);
01852    return ret;
01853 }
01854 
01855 char *ast_cli_generator(const char *text, const char *word, int state)
01856 {
01857    return __ast_cli_generator(text, word, state, 1);
01858 }
01859 
01860 int ast_cli_command(int fd, const char *s)
01861 {
01862    char *args[AST_MAX_ARGS + 1];
01863    struct ast_cli_entry *e;
01864    int x;
01865    char *duplicate = parse_args(s, &x, args + 1, AST_MAX_ARGS, NULL);
01866    char *retval = NULL;
01867    struct ast_cli_args a = {
01868       .fd = fd, .argc = x, .argv = args+1 };
01869 
01870    if (duplicate == NULL)
01871       return -1;
01872 
01873    if (x < 1)  /* We need at least one entry, otherwise ignore */
01874       goto done;
01875 
01876    AST_RWLIST_RDLOCK(&helpers);
01877    e = find_cli(args + 1, 0);
01878    if (e)
01879       ast_atomic_fetchadd_int(&e->inuse, 1);
01880    AST_RWLIST_UNLOCK(&helpers);
01881    if (e == NULL) {
01882       ast_cli(fd, "No such command '%s' (type 'help %s' for other possible commands)\n", s, find_best(args + 1));
01883       goto done;
01884    }
01885    /*
01886     * Within the handler, argv[-1] contains a pointer to the ast_cli_entry.
01887     * Remember that the array returned by parse_args is NULL-terminated.
01888     */
01889    args[0] = (char *)e;
01890 
01891    retval = e->handler(e, CLI_HANDLER, &a);
01892 
01893    if (retval == CLI_SHOWUSAGE) {
01894       ast_cli(fd, "%s", S_OR(e->usage, "Invalid usage, but no usage information available.\n"));
01895       AST_RWLIST_RDLOCK(&helpers);
01896       if (e->deprecated)
01897          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);
01898       AST_RWLIST_UNLOCK(&helpers);
01899    } else {
01900       if (retval == CLI_FAILURE)
01901          ast_cli(fd, "Command '%s' failed.\n", s);
01902       AST_RWLIST_RDLOCK(&helpers);
01903       if (e->deprecated == 1) {
01904          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);
01905          e->deprecated = 2;
01906       }
01907       AST_RWLIST_UNLOCK(&helpers);
01908    }
01909    ast_atomic_fetchadd_int(&e->inuse, -1);
01910 done:
01911    ast_free(duplicate);
01912    return 0;
01913 }
01914 
01915 int ast_cli_command_multiple(int fd, size_t size, const char *s)
01916 {
01917    char cmd[512];
01918    int x, y = 0, count = 0;
01919 
01920    for (x = 0; x < size; x++) {
01921       cmd[y] = s[x];
01922       y++;
01923       if (s[x] == '\0') {
01924          ast_cli_command(fd, cmd);
01925          y = 0;
01926          count++;
01927       }
01928    }
01929    return count;
01930 }

Generated on Wed Aug 18 22:33:50 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7