Thu Apr 28 2011 17:13:33

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