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

Generated on Thu Apr 16 06:27:30 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6