Mon Jun 18 2012 03:55:31

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