Fri Jul 15 2011 11:58:02

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