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

Generated on 2 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1