Fri Sep 25 19:28:11 2009

Asterisk developer's documentation


cli.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Standard Command Line Interface
00022  *
00023  * \author Mark Spencer <markster@digium.com> 
00024  */
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 85533 $")
00029 
00030 #include <unistd.h>
00031 #include <stdlib.h>
00032 #include <sys/signal.h>
00033 #include <stdio.h>
00034 #include <signal.h>
00035 #include <string.h>
00036 #include <ctype.h>
00037 #include <regex.h>
00038 
00039 #include "asterisk/logger.h"
00040 #include "asterisk/options.h"
00041 #include "asterisk/cli.h"
00042 #include "asterisk/linkedlists.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/pbx.h"
00045 #include "asterisk/channel.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/app.h"
00048 #include "asterisk/lock.h"
00049 #include "editline/readline/readline.h"
00050 #include "asterisk/threadstorage.h"
00051 
00052 extern unsigned long global_fin, global_fout;
00053 
00054 AST_THREADSTORAGE(ast_cli_buf, ast_cli_buf_init);
00055 
00056 /*! \brief Initial buffer size for resulting strings in ast_cli() */
00057 #define AST_CLI_INITLEN   256
00058 
00059 void ast_cli(int fd, char *fmt, ...)
00060 {
00061    int res;
00062    struct ast_dynamic_str *buf;
00063    va_list ap;
00064 
00065    if (!(buf = ast_dynamic_str_thread_get(&ast_cli_buf, AST_CLI_INITLEN)))
00066       return;
00067 
00068    va_start(ap, fmt);
00069    res = ast_dynamic_str_thread_set_va(&buf, 0, &ast_cli_buf, fmt, ap);
00070    va_end(ap);
00071 
00072    if (res != AST_DYNSTR_BUILD_FAILED)
00073       ast_carefulwrite(fd, buf->str, strlen(buf->str), 100);
00074 }
00075 
00076 static AST_LIST_HEAD_STATIC(helpers, ast_cli_entry);
00077 
00078 static char load_help[] = 
00079 "Usage: module load <module name>\n"
00080 "       Loads the specified module into Asterisk.\n";
00081 
00082 static char unload_help[] = 
00083 "Usage: module unload [-f|-h] <module name>\n"
00084 "       Unloads the specified module from Asterisk. The -f\n"
00085 "       option causes the module to be unloaded even if it is\n"
00086 "       in use (may cause a crash) and the -h module causes the\n"
00087 "       module to be unloaded even if the module says it cannot, \n"
00088 "       which almost always will cause a crash.\n";
00089 
00090 static char help_help[] =
00091 "Usage: help [topic]\n"
00092 "       When called with a topic as an argument, displays usage\n"
00093 "       information on the given command. If called without a\n"
00094 "       topic, it provides a list of commands.\n";
00095 
00096 static char chanlist_help[] = 
00097 "Usage: core show channels [concise|verbose]\n"
00098 "       Lists currently defined channels and some information about them. If\n"
00099 "       'concise' is specified, the format is abridged and in a more easily\n"
00100 "       machine parsable format. If 'verbose' is specified, the output includes\n"
00101 "       more and longer fields.\n";
00102 
00103 static char reload_help[] = 
00104 "Usage: module reload [module ...]\n"
00105 "       Reloads configuration files for all listed modules which support\n"
00106 "       reloading, or for all supported modules if none are listed.\n";
00107 
00108 static char verbose_help[] = 
00109 "Usage: core set verbose <level>\n"
00110 "       Sets level of verbose messages to be displayed.  0 means\n"
00111 "       no messages should be displayed. Equivalent to -v[v[v...]]\n"
00112 "       on startup\n";
00113 
00114 static char debug_help[] = 
00115 "Usage: core set debug <level> [filename]\n"
00116 "       Sets level of core debug messages to be displayed.  0 means\n"
00117 "       no messages should be displayed.  Equivalent to -d[d[d...]]\n"
00118 "       on startup.  If filename is specified, debugging will be\n"
00119 "       limited to just that file.\n";
00120 
00121 static char nodebug_help[] = 
00122 "Usage: core set debug off\n"
00123 "       Turns off core debug messages.\n";
00124 
00125 static char logger_mute_help[] = 
00126 "Usage: logger mute\n"
00127 "       Disables logging output to the current console, making it possible to\n"
00128 "       gather information without being disturbed by scrolling lines.\n";
00129 
00130 static char softhangup_help[] =
00131 "Usage: soft hangup <channel>\n"
00132 "       Request that a channel be hung up. The hangup takes effect\n"
00133 "       the next time the driver reads or writes from the channel\n";
00134 
00135 static char group_show_channels_help[] = 
00136 "Usage: group show channels [pattern]\n"
00137 "       Lists all currently active channels with channel group(s) specified.\n"
00138 "       Optional regular expression pattern is matched to group names for each\n"
00139 "       channel.\n";
00140 
00141 static int handle_load_deprecated(int fd, int argc, char *argv[])
00142 {
00143    if (argc != 2)
00144       return RESULT_SHOWUSAGE;
00145    if (ast_load_resource(argv[1])) {
00146       ast_cli(fd, "Unable to load module %s\n", argv[1]);
00147       return RESULT_FAILURE;
00148    }
00149    return RESULT_SUCCESS;
00150 }
00151 
00152 static int handle_load(int fd, int argc, char *argv[])
00153 {
00154    if (argc != 3)
00155       return RESULT_SHOWUSAGE;
00156    if (ast_load_resource(argv[2])) {
00157       ast_cli(fd, "Unable to load module %s\n", argv[2]);
00158       return RESULT_FAILURE;
00159    }
00160    return RESULT_SUCCESS;
00161 }
00162 
00163 static int handle_reload_deprecated(int fd, int argc, char *argv[])
00164 {
00165    int x;
00166    int res;
00167    if (argc < 1)
00168       return RESULT_SHOWUSAGE;
00169    if (argc > 1) { 
00170       for (x = 1; x < argc; x++) {
00171          res = ast_module_reload(argv[x]);
00172          switch(res) {
00173          case 0:
00174             ast_cli(fd, "No such module '%s'\n", argv[x]);
00175             break;
00176          case 1:
00177             ast_cli(fd, "Module '%s' does not support reload\n", argv[x]);
00178             break;
00179          }
00180       }
00181    } else
00182       ast_module_reload(NULL);
00183    return RESULT_SUCCESS;
00184 }
00185 
00186 static int handle_reload(int fd, int argc, char *argv[])
00187 {
00188    int x;
00189    int res;
00190    if (argc < 2)
00191       return RESULT_SHOWUSAGE;
00192    if (argc > 2) { 
00193       for (x = 2; x < argc; x++) {
00194          res = ast_module_reload(argv[x]);
00195          switch(res) {
00196          case 0:
00197             ast_cli(fd, "No such module '%s'\n", argv[x]);
00198             break;
00199          case 1:
00200             ast_cli(fd, "Module '%s' does not support reload\n", argv[x]);
00201             break;
00202          }
00203       }
00204    } else
00205       ast_module_reload(NULL);
00206    return RESULT_SUCCESS;
00207 }
00208 
00209 static int handle_set_verbose_deprecated(int fd, int argc, char *argv[])
00210 {
00211    int val = 0;
00212    int oldval = option_verbose;
00213 
00214    /* "set verbose [atleast] N" */
00215    if (argc == 3)
00216       option_verbose = atoi(argv[2]);
00217    else if (argc == 4) {
00218       if (strcasecmp(argv[2], "atleast"))
00219          return RESULT_SHOWUSAGE;
00220       val = atoi(argv[3]);
00221       if (val > option_verbose)
00222          option_verbose = val;
00223    } else
00224       return RESULT_SHOWUSAGE;
00225 
00226    if (oldval != option_verbose && option_verbose > 0)
00227       ast_cli(fd, "Verbosity was %d and is now %d\n", oldval, option_verbose);
00228    else if (oldval > 0 && option_verbose > 0)
00229       ast_cli(fd, "Verbosity is at least %d\n", option_verbose);
00230    else if (oldval > 0 && option_verbose == 0)
00231       ast_cli(fd, "Verbosity is now OFF\n");
00232 
00233    return RESULT_SUCCESS;
00234 }
00235 
00236 static int handle_verbose(int fd, int argc, char *argv[])
00237 {
00238    int oldval = option_verbose;
00239    int newlevel;
00240    int atleast = 0;
00241 
00242    if ((argc < 4) || (argc > 5))
00243       return RESULT_SHOWUSAGE;
00244 
00245    if (!strcasecmp(argv[3], "atleast"))
00246       atleast = 1;
00247 
00248    if (!atleast) {
00249       if (argc > 4)
00250          return RESULT_SHOWUSAGE;
00251 
00252       option_verbose = atoi(argv[3]);
00253    } else {
00254       if (argc < 5)
00255          return RESULT_SHOWUSAGE;
00256 
00257       newlevel = atoi(argv[4]);
00258       if (newlevel > option_verbose)
00259          option_verbose = newlevel;
00260         }
00261    if (oldval > 0 && option_verbose == 0)
00262       ast_cli(fd, "Verbosity is now OFF\n");
00263    else if (option_verbose > 0) {
00264       if (oldval == option_verbose)
00265          ast_cli(fd, "Verbosity is at least %d\n", option_verbose);
00266       else
00267          ast_cli(fd, "Verbosity was %d and is now %d\n", oldval, option_verbose);
00268    }
00269 
00270    return RESULT_SUCCESS;
00271 }
00272 
00273 static int handle_set_debug_deprecated(int fd, int argc, char *argv[])
00274 {
00275    int val = 0;
00276    int oldval = option_debug;
00277 
00278    /* "set debug [atleast] N" */
00279    if (argc == 3)
00280       option_debug = atoi(argv[2]);
00281    else if (argc == 4) {
00282       if (strcasecmp(argv[2], "atleast"))
00283          return RESULT_SHOWUSAGE;
00284       val = atoi(argv[3]);
00285       if (val > option_debug)
00286          option_debug = val;
00287    } else
00288       return RESULT_SHOWUSAGE;
00289 
00290    if (oldval != option_debug && option_debug > 0)
00291       ast_cli(fd, "Core debug was %d and is now %d\n", oldval, option_debug);
00292    else if (oldval > 0 && option_debug > 0)
00293       ast_cli(fd, "Core debug is at least %d\n", option_debug);
00294    else if (oldval > 0 && option_debug == 0)
00295       ast_cli(fd, "Core debug is now OFF\n");
00296 
00297    return RESULT_SUCCESS;
00298 }
00299 
00300 static int handle_set_debug(int fd, int argc, char *argv[])
00301 {
00302    int oldval = option_debug;
00303    int newlevel;
00304    int atleast = 0;
00305    char *filename = '\0';
00306 
00307    /* 'core set debug <level>'
00308     * 'core set debug <level> <fn>'
00309     * 'core set debug atleast <level>'
00310     * 'core set debug atleast <level> <fn>'
00311     */
00312    if ((argc < 4) || (argc > 6))
00313       return RESULT_SHOWUSAGE;
00314 
00315    if (!strcasecmp(argv[3], "atleast"))
00316       atleast = 1;
00317 
00318    if (!atleast) {
00319       if (argc > 5)
00320          return RESULT_SHOWUSAGE;
00321 
00322       if (sscanf(argv[3], "%d", &newlevel) != 1)
00323          return RESULT_SHOWUSAGE;
00324 
00325       if (argc == 4) {
00326          debug_filename[0] = '\0';
00327       } else {
00328          filename = argv[4];
00329          ast_copy_string(debug_filename, filename, sizeof(debug_filename));
00330       }
00331 
00332       option_debug = newlevel;
00333    } else {
00334       if (argc < 5 || argc > 6)
00335          return RESULT_SHOWUSAGE;
00336 
00337       if (sscanf(argv[4], "%d", &newlevel) != 1)
00338          return RESULT_SHOWUSAGE;
00339 
00340       if (argc == 5) {
00341          debug_filename[0] = '\0';
00342       } else {
00343          filename = argv[5];
00344          ast_copy_string(debug_filename, filename, sizeof(debug_filename));
00345       }
00346 
00347       if (newlevel > option_debug)
00348          option_debug = newlevel;
00349    }
00350 
00351    if (oldval > 0 && option_debug == 0)
00352       ast_cli(fd, "Core debug is now OFF\n");
00353    else if (option_debug > 0) {
00354       if (filename) {
00355          if (oldval == option_debug)
00356             ast_cli(fd, "Core debug is at least %d, file '%s'\n", option_debug, filename);
00357          else
00358             ast_cli(fd, "Core debug was %d and is now %d, file '%s'\n", oldval, option_debug, filename);
00359       } else {
00360          if (oldval == option_debug)
00361             ast_cli(fd, "Core debug is at least %d\n", option_debug);
00362          else
00363             ast_cli(fd, "Core debug was %d and is now %d\n", oldval, option_debug);
00364       }
00365    }
00366 
00367    return RESULT_SUCCESS;
00368 }
00369 
00370 static int handle_nodebug(int fd, int argc, char *argv[])
00371 {
00372    int oldval = option_debug;
00373    if (argc != 4)
00374       return RESULT_SHOWUSAGE;
00375 
00376    option_debug = 0;
00377    debug_filename[0] = '\0';
00378 
00379    if (oldval > 0)
00380       ast_cli(fd, "Core debug is now OFF\n");
00381    return RESULT_SUCCESS;
00382 }
00383 
00384 static int handle_debuglevel_deprecated(int fd, int argc, char *argv[])
00385 {
00386    int newlevel;
00387    char *filename = "<any>";
00388    if ((argc < 3) || (argc > 4))
00389       return RESULT_SHOWUSAGE;
00390    if (sscanf(argv[2], "%d", &newlevel) != 1)
00391       return RESULT_SHOWUSAGE;
00392    option_debug = newlevel;
00393    if (argc == 4) {
00394       filename = argv[3];
00395       ast_copy_string(debug_filename, filename, sizeof(debug_filename));
00396    } else {
00397       debug_filename[0] = '\0';
00398    }
00399    ast_cli(fd, "Debugging level set to %d, file '%s'\n", newlevel, filename);
00400    return RESULT_SUCCESS;
00401 }
00402 
00403 static int handle_logger_mute(int fd, int argc, char *argv[])
00404 {
00405    if (argc < 2 || argc > 3)
00406       return RESULT_SHOWUSAGE;
00407    if (argc == 3 && !strcasecmp(argv[2], "silent"))
00408       ast_console_toggle_mute(fd, 1);
00409    else
00410       ast_console_toggle_mute(fd, 0);
00411    return RESULT_SUCCESS;
00412 }
00413 
00414 static int handle_unload_deprecated(int fd, int argc, char *argv[])
00415 {
00416    int x;
00417    int force = AST_FORCE_SOFT;
00418    if (argc < 2)
00419       return RESULT_SHOWUSAGE;
00420    for (x = 1; x < argc; x++) {
00421       if (argv[x][0] == '-') {
00422          switch(argv[x][1]) {
00423          case 'f':
00424             force = AST_FORCE_FIRM;
00425             break;
00426          case 'h':
00427             force = AST_FORCE_HARD;
00428             break;
00429          default:
00430             return RESULT_SHOWUSAGE;
00431          }
00432       } else if (x != argc - 1) 
00433          return RESULT_SHOWUSAGE;
00434       else if (ast_unload_resource(argv[x], force)) {
00435          ast_cli(fd, "Unable to unload resource %s\n", argv[x]);
00436          return RESULT_FAILURE;
00437       }
00438    }
00439    return RESULT_SUCCESS;
00440 }
00441 
00442 static int handle_unload(int fd, int argc, char *argv[])
00443 {
00444    int x;
00445    int force = AST_FORCE_SOFT;
00446    if (argc < 3)
00447       return RESULT_SHOWUSAGE;
00448    for (x = 2; x < argc; x++) {
00449       if (argv[x][0] == '-') {
00450          switch(argv[x][1]) {
00451          case 'f':
00452             force = AST_FORCE_FIRM;
00453             break;
00454          case 'h':
00455             force = AST_FORCE_HARD;
00456             break;
00457          default:
00458             return RESULT_SHOWUSAGE;
00459          }
00460       } else if (x != argc - 1) 
00461          return RESULT_SHOWUSAGE;
00462       else if (ast_unload_resource(argv[x], force)) {
00463          ast_cli(fd, "Unable to unload resource %s\n", argv[x]);
00464          return RESULT_FAILURE;
00465       }
00466    }
00467    return RESULT_SUCCESS;
00468 }
00469 
00470 #define MODLIST_FORMAT  "%-30s %-40.40s %-10d\n"
00471 #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
00472 
00473 AST_MUTEX_DEFINE_STATIC(climodentrylock);
00474 static int climodentryfd = -1;
00475 
00476 static int modlist_modentry(const char *module, const char *description, int usecnt, const char *like)
00477 {
00478    /* Comparing the like with the module */
00479    if (strcasestr(module, like) ) {
00480       ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
00481       return 1;
00482    } 
00483    return 0;
00484 }
00485 
00486 static char modlist_help[] =
00487 "Usage: module show [like <keyword>]\n"
00488 "       Shows Asterisk modules currently in use, and usage statistics.\n";
00489 
00490 static char uptime_help[] =
00491 "Usage: core show uptime [seconds]\n"
00492 "       Shows Asterisk uptime information.\n"
00493 "       The seconds word returns the uptime in seconds only.\n";
00494 
00495 static void print_uptimestr(int fd, time_t timeval, const char *prefix, int printsec)
00496 {
00497    int x; /* the main part - years, weeks, etc. */
00498    char timestr[256]="", *s = timestr;
00499    size_t maxbytes = sizeof(timestr);
00500 
00501 #define SECOND (1)
00502 #define MINUTE (SECOND*60)
00503 #define HOUR (MINUTE*60)
00504 #define DAY (HOUR*24)
00505 #define WEEK (DAY*7)
00506 #define YEAR (DAY*365)
00507 #define ESS(x) ((x == 1) ? "" : "s")   /* plural suffix */
00508 #define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */
00509    if (timeval < 0)  /* invalid, nothing to show */
00510       return;
00511    if (printsec)  {  /* plain seconds output */
00512       ast_build_string(&s, &maxbytes, "%lu", (u_long)timeval);
00513       timeval = 0; /* bypass the other cases */
00514    }
00515    if (timeval > YEAR) {
00516       x = (timeval / YEAR);
00517       timeval -= (x * YEAR);
00518       ast_build_string(&s, &maxbytes, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval));
00519    }
00520    if (timeval > WEEK) {
00521       x = (timeval / WEEK);
00522       timeval -= (x * WEEK);
00523       ast_build_string(&s, &maxbytes, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval));
00524    }
00525    if (timeval > DAY) {
00526       x = (timeval / DAY);
00527       timeval -= (x * DAY);
00528       ast_build_string(&s, &maxbytes, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval));
00529    }
00530    if (timeval > HOUR) {
00531       x = (timeval / HOUR);
00532       timeval -= (x * HOUR);
00533       ast_build_string(&s, &maxbytes, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval));
00534    }
00535    if (timeval > MINUTE) {
00536       x = (timeval / MINUTE);
00537       timeval -= (x * MINUTE);
00538       ast_build_string(&s, &maxbytes, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval));
00539    }
00540    x = timeval;
00541    if (x > 0)
00542       ast_build_string(&s, &maxbytes, "%d second%s ", x, ESS(x));
00543    if (timestr[0] != '\0')
00544       ast_cli(fd, "%s: %s\n", prefix, timestr);
00545 }
00546 
00547 static int handle_showuptime_deprecated(int fd, int argc, char *argv[])
00548 {
00549    /* 'show uptime [seconds]' */
00550    time_t curtime = time(NULL);
00551    int printsec = (argc == 3 && !strcasecmp(argv[2],"seconds"));
00552 
00553    if (argc != 2 && !printsec)
00554       return RESULT_SHOWUSAGE;
00555    if (ast_startuptime)
00556       print_uptimestr(fd, curtime - ast_startuptime, "System uptime", printsec);
00557    if (ast_lastreloadtime)
00558       print_uptimestr(fd, curtime - ast_lastreloadtime, "Last reload", printsec);
00559    return RESULT_SUCCESS;
00560 }
00561 
00562 static int handle_showuptime(int fd, int argc, char *argv[])
00563 {
00564    /* 'core show uptime [seconds]' */
00565    time_t curtime = time(NULL);
00566    int printsec = (argc == 4 && !strcasecmp(argv[3],"seconds"));
00567 
00568    if (argc != 3 && !printsec)
00569       return RESULT_SHOWUSAGE;
00570    if (ast_startuptime)
00571       print_uptimestr(fd, curtime - ast_startuptime, "System uptime", printsec);
00572    if (ast_lastreloadtime)
00573       print_uptimestr(fd, curtime - ast_lastreloadtime, "Last reload", printsec);
00574    return RESULT_SUCCESS;
00575 }
00576 
00577 static int handle_modlist(int fd, int argc, char *argv[])
00578 {
00579    char *like = "";
00580    if (argc == 3)
00581       return RESULT_SHOWUSAGE;
00582    else if (argc >= 4) {
00583       if (strcmp(argv[2],"like")) 
00584          return RESULT_SHOWUSAGE;
00585       like = argv[3];
00586    }
00587       
00588    ast_mutex_lock(&climodentrylock);
00589    climodentryfd = fd; /* global, protected by climodentrylock */
00590    ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
00591    ast_cli(fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
00592    climodentryfd = -1;
00593    ast_mutex_unlock(&climodentrylock);
00594    return RESULT_SUCCESS;
00595 }
00596 #undef MODLIST_FORMAT
00597 #undef MODLIST_FORMAT2
00598 
00599 #define FORMAT_STRING  "%-20.20s %-20.20s %-7.7s %-30.30s\n"
00600 #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
00601 #define CONCISE_FORMAT_STRING  "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s\n"
00602 #define VERBOSE_FORMAT_STRING  "%-20.20s %-20.20s %-16.16s %4d %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n"
00603 #define VERBOSE_FORMAT_STRING2 "%-20.20s %-20.20s %-16.16s %-4.4s %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n"
00604 
00605 static int handle_chanlist_deprecated(int fd, int argc, char *argv[])
00606 {
00607    struct ast_channel *c = NULL;
00608    char durbuf[10] = "-";
00609    char locbuf[40];
00610    char appdata[40];
00611    int duration;
00612    int durh, durm, durs;
00613    int numchans = 0, concise = 0, verbose = 0;
00614 
00615    concise = (argc == 3 && (!strcasecmp(argv[2],"concise")));
00616    verbose = (argc == 3 && (!strcasecmp(argv[2],"verbose")));
00617 
00618    if (argc < 2 || argc > 3 || (argc == 3 && !concise && !verbose))
00619       return RESULT_SHOWUSAGE;
00620 
00621    if (!concise && !verbose)
00622       ast_cli(fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
00623    else if (verbose)
00624       ast_cli(fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data", 
00625               "CallerID", "Duration", "Accountcode", "BridgedTo");
00626 
00627    while ((c = ast_channel_walk_locked(c)) != NULL) {
00628       struct ast_channel *bc = ast_bridged_channel(c);
00629       if ((concise || verbose)  && c->cdr && !ast_tvzero(c->cdr->start)) {
00630          duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
00631          if (verbose) {
00632             durh = duration / 3600;
00633             durm = (duration % 3600) / 60;
00634             durs = duration % 60;
00635             snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
00636          } else {
00637             snprintf(durbuf, sizeof(durbuf), "%d", duration);
00638          }           
00639       } else {
00640          durbuf[0] = '\0';
00641       }
00642       if (concise) {
00643          ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00644                  c->appl ? c->appl : "(None)",
00645             S_OR(c->data, ""),   /* XXX different from verbose ? */
00646                  S_OR(c->cid.cid_num, ""),
00647                  S_OR(c->accountcode, ""),
00648             c->amaflags, 
00649                  durbuf,
00650             bc ? bc->name : "(None)");
00651       } else if (verbose) {
00652          ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00653                  c->appl ? c->appl : "(None)",
00654             c->data ? S_OR(c->data, "(Empty)" ): "(None)",
00655                  S_OR(c->cid.cid_num, ""),
00656             durbuf,
00657                  S_OR(c->accountcode, ""),
00658             bc ? bc->name : "(None)");
00659       } else {
00660          if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten)) 
00661             snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
00662          else
00663             strcpy(locbuf, "(None)");
00664          if (c->appl)
00665             snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, c->data ? c->data : "");
00666          else
00667             strcpy(appdata, "(None)");
00668          ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
00669       }
00670       numchans++;
00671       ast_channel_unlock(c);
00672    }
00673    if (!concise) {
00674       ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
00675       if (option_maxcalls)
00676          ast_cli(fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
00677             ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
00678             ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
00679       else
00680          ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
00681    }
00682    return RESULT_SUCCESS;
00683 }
00684    
00685 static int handle_chanlist(int fd, int argc, char *argv[])
00686 {
00687    struct ast_channel *c = NULL;
00688    char durbuf[10] = "-";
00689    char locbuf[40];
00690    char appdata[40];
00691    int duration;
00692    int durh, durm, durs;
00693    int numchans = 0, concise = 0, verbose = 0;
00694 
00695    concise = (argc == 4 && (!strcasecmp(argv[3],"concise")));
00696    verbose = (argc == 4 && (!strcasecmp(argv[3],"verbose")));
00697 
00698    if (argc < 3 || argc > 4 || (argc == 4 && !concise && !verbose))
00699       return RESULT_SHOWUSAGE;
00700 
00701    if (!concise && !verbose)
00702       ast_cli(fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
00703    else if (verbose)
00704       ast_cli(fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data", 
00705               "CallerID", "Duration", "Accountcode", "BridgedTo");
00706 
00707    while ((c = ast_channel_walk_locked(c)) != NULL) {
00708       struct ast_channel *bc = ast_bridged_channel(c);
00709       if ((concise || verbose)  && c->cdr && !ast_tvzero(c->cdr->start)) {
00710          duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
00711          if (verbose) {
00712             durh = duration / 3600;
00713             durm = (duration % 3600) / 60;
00714             durs = duration % 60;
00715             snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
00716          } else {
00717             snprintf(durbuf, sizeof(durbuf), "%d", duration);
00718          }           
00719       } else {
00720          durbuf[0] = '\0';
00721       }
00722       if (concise) {
00723          ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00724                  c->appl ? c->appl : "(None)",
00725             S_OR(c->data, ""),   /* XXX different from verbose ? */
00726                  S_OR(c->cid.cid_num, ""),
00727                  S_OR(c->accountcode, ""),
00728             c->amaflags, 
00729                  durbuf,
00730             bc ? bc->name : "(None)");
00731       } else if (verbose) {
00732          ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00733                  c->appl ? c->appl : "(None)",
00734             c->data ? S_OR(c->data, "(Empty)" ): "(None)",
00735                  S_OR(c->cid.cid_num, ""),
00736             durbuf,
00737                  S_OR(c->accountcode, ""),
00738             bc ? bc->name : "(None)");
00739       } else {
00740          if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten)) 
00741             snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
00742          else
00743             strcpy(locbuf, "(None)");
00744          if (c->appl)
00745             snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, c->data ? c->data : "");
00746          else
00747             strcpy(appdata, "(None)");
00748          ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
00749       }
00750       numchans++;
00751       ast_channel_unlock(c);
00752    }
00753    if (!concise) {
00754       ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
00755       if (option_maxcalls)
00756          ast_cli(fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
00757             ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
00758             ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
00759       else
00760          ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
00761    }
00762    return RESULT_SUCCESS;
00763 }
00764    
00765 #undef FORMAT_STRING
00766 #undef FORMAT_STRING2
00767 #undef CONCISE_FORMAT_STRING
00768 #undef VERBOSE_FORMAT_STRING
00769 #undef VERBOSE_FORMAT_STRING2
00770 
00771 static char showchan_help[] = 
00772 "Usage: core show channel <channel>\n"
00773 "       Shows lots of information about the specified channel.\n";
00774 
00775 static char debugchan_help[] = 
00776 "Usage: core set debug channel <channel> [off]\n"
00777 "       Enables/disables debugging on a specific channel.\n";
00778 
00779 static char commandcomplete_help[] = 
00780 "Usage: _command complete \"<line>\" text state\n"
00781 "       This function is used internally to help with command completion and should.\n"
00782 "       never be called by the user directly.\n";
00783 
00784 static char commandnummatches_help[] = 
00785 "Usage: _command nummatches \"<line>\" text \n"
00786 "       This function is used internally to help with command completion and should.\n"
00787 "       never be called by the user directly.\n";
00788 
00789 static char commandmatchesarray_help[] = 
00790 "Usage: _command matchesarray \"<line>\" text \n"
00791 "       This function is used internally to help with command completion and should.\n"
00792 "       never be called by the user directly.\n";
00793 
00794 static int handle_softhangup(int fd, int argc, char *argv[])
00795 {
00796    struct ast_channel *c=NULL;
00797    if (argc != 3)
00798       return RESULT_SHOWUSAGE;
00799    c = ast_get_channel_by_name_locked(argv[2]);
00800    if (c) {
00801       ast_cli(fd, "Requested Hangup on channel '%s'\n", c->name);
00802       ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
00803       ast_channel_unlock(c);
00804    } else
00805       ast_cli(fd, "%s is not a known channel\n", argv[2]);
00806    return RESULT_SUCCESS;
00807 }
00808 
00809 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
00810 
00811 static int handle_commandmatchesarray(int fd, int argc, char *argv[])
00812 {
00813    char *buf, *obuf;
00814    int buflen = 2048;
00815    int len = 0;
00816    char **matches;
00817    int x, matchlen;
00818 
00819    if (argc != 4)
00820       return RESULT_SHOWUSAGE;
00821    if (!(buf = ast_malloc(buflen)))
00822       return RESULT_FAILURE;
00823    buf[len] = '\0';
00824    matches = ast_cli_completion_matches(argv[2], argv[3]);
00825    if (matches) {
00826       for (x=0; matches[x]; x++) {
00827          matchlen = strlen(matches[x]) + 1;
00828          if (len + matchlen >= buflen) {
00829             buflen += matchlen * 3;
00830             obuf = buf;
00831             if (!(buf = ast_realloc(obuf, buflen))) 
00832                /* Memory allocation failure...  Just free old buffer and be done */
00833                free(obuf);
00834          }
00835          if (buf)
00836             len += sprintf( buf + len, "%s ", matches[x]);
00837          free(matches[x]);
00838          matches[x] = NULL;
00839       }
00840       free(matches);
00841    }
00842 
00843    if (buf) {
00844       ast_cli(fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
00845       free(buf);
00846    } else
00847       ast_cli(fd, "NULL\n");
00848 
00849    return RESULT_SUCCESS;
00850 }
00851 
00852 
00853 
00854 static int handle_commandnummatches(int fd, int argc, char *argv[])
00855 {
00856    int matches = 0;
00857 
00858    if (argc != 4)
00859       return RESULT_SHOWUSAGE;
00860 
00861    matches = ast_cli_generatornummatches(argv[2], argv[3]);
00862 
00863    ast_cli(fd, "%d", matches);
00864 
00865    return RESULT_SUCCESS;
00866 }
00867 
00868 static int handle_commandcomplete(int fd, int argc, char *argv[])
00869 {
00870    char *buf;
00871 
00872    if (argc != 5)
00873       return RESULT_SHOWUSAGE;
00874    buf = __ast_cli_generator(argv[2], argv[3], atoi(argv[4]), 0);
00875    if (buf) {
00876       ast_cli(fd, buf);
00877       free(buf);
00878    } else
00879       ast_cli(fd, "NULL\n");
00880    return RESULT_SUCCESS;
00881 }
00882 
00883 static int handle_debugchan_deprecated(int fd, int argc, char *argv[])
00884 {
00885    struct ast_channel *c=NULL;
00886    int is_all;
00887 
00888    /* 'debug channel {all|chan_id}' */
00889    if (argc != 3)
00890       return RESULT_SHOWUSAGE;
00891 
00892    is_all = !strcasecmp("all", argv[2]);
00893    if (is_all) {
00894       global_fin |= DEBUGCHAN_FLAG;
00895       global_fout |= DEBUGCHAN_FLAG;
00896       c = ast_channel_walk_locked(NULL);
00897    } else {
00898       c = ast_get_channel_by_name_locked(argv[2]);
00899       if (c == NULL)
00900          ast_cli(fd, "No such channel %s\n", argv[2]);
00901    }
00902    while (c) {
00903       if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
00904          c->fin |= DEBUGCHAN_FLAG;
00905          c->fout |= DEBUGCHAN_FLAG;
00906          ast_cli(fd, "Debugging enabled on channel %s\n", c->name);
00907       }
00908       ast_channel_unlock(c);
00909       if (!is_all)
00910          break;
00911       c = ast_channel_walk_locked(c);
00912    }
00913    ast_cli(fd, "Debugging on new channels is enabled\n");
00914    return RESULT_SUCCESS;
00915 }
00916 
00917 static int handle_core_set_debug_channel(int fd, int argc, char *argv[])
00918 {
00919    struct ast_channel *c = NULL;
00920    int is_all, is_off = 0;
00921 
00922    /* 'core set debug channel {all|chan_id}' */
00923    if (argc == 6 && strcmp(argv[5], "off") == 0)
00924       is_off = 1;
00925    else if (argc != 5)
00926       return RESULT_SHOWUSAGE;
00927 
00928    is_all = !strcasecmp("all", argv[4]);
00929    if (is_all) {
00930       if (is_off) {
00931          global_fin &= ~DEBUGCHAN_FLAG;
00932          global_fout &= ~DEBUGCHAN_FLAG;
00933       } else {
00934          global_fin |= DEBUGCHAN_FLAG;
00935          global_fout |= DEBUGCHAN_FLAG;
00936       }
00937       c = ast_channel_walk_locked(NULL);
00938    } else {
00939       c = ast_get_channel_by_name_locked(argv[4]);
00940       if (c == NULL)
00941          ast_cli(fd, "No such channel %s\n", argv[4]);
00942    }
00943    while (c) {
00944       if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
00945          if (is_off) {
00946             c->fin &= ~DEBUGCHAN_FLAG;
00947             c->fout &= ~DEBUGCHAN_FLAG;
00948          } else {
00949             c->fin |= DEBUGCHAN_FLAG;
00950             c->fout |= DEBUGCHAN_FLAG;
00951          }
00952          ast_cli(fd, "Debugging %s on channel %s\n", is_off ? "disabled" : "enabled", c->name);
00953       }
00954       ast_channel_unlock(c);
00955       if (!is_all)
00956          break;
00957       c = ast_channel_walk_locked(c);
00958    }
00959    ast_cli(fd, "Debugging on new channels is %s\n", is_off ? "disabled" : "enabled");
00960    return RESULT_SUCCESS;
00961 }
00962 
00963 static int handle_nodebugchan_deprecated(int fd, int argc, char *argv[])
00964 {
00965    struct ast_channel *c=NULL;
00966    int is_all;
00967    /* 'no debug channel {all|chan_id}' */
00968    if (argc != 4)
00969       return RESULT_SHOWUSAGE;
00970    is_all = !strcasecmp("all", argv[3]);
00971    if (is_all) {
00972       global_fin &= ~DEBUGCHAN_FLAG;
00973       global_fout &= ~DEBUGCHAN_FLAG;
00974       c = ast_channel_walk_locked(NULL);
00975    } else {
00976       c = ast_get_channel_by_name_locked(argv[3]);
00977       if (c == NULL)
00978          ast_cli(fd, "No such channel %s\n", argv[3]);
00979    }
00980    while(c) {
00981       if ((c->fin & DEBUGCHAN_FLAG) || (c->fout & DEBUGCHAN_FLAG)) {
00982          c->fin &= ~DEBUGCHAN_FLAG;
00983          c->fout &= ~DEBUGCHAN_FLAG;
00984          ast_cli(fd, "Debugging disabled on channel %s\n", c->name);
00985       }
00986       ast_channel_unlock(c);
00987       if (!is_all)
00988          break;
00989       c = ast_channel_walk_locked(c);
00990    }
00991    ast_cli(fd, "Debugging on new channels is disabled\n");
00992    return RESULT_SUCCESS;
00993 }
00994       
00995 static int handle_showchan_deprecated(int fd, int argc, char *argv[])
00996 {
00997    struct ast_channel *c=NULL;
00998    struct timeval now;
00999    char buf[2048];
01000    char cdrtime[256];
01001    char nf[256], wf[256], rf[256];
01002    long elapsed_seconds=0;
01003    int hour=0, min=0, sec=0;
01004    
01005    if (argc != 3)
01006       return RESULT_SHOWUSAGE;
01007    now = ast_tvnow();
01008    c = ast_get_channel_by_name_locked(argv[2]);
01009    if (!c) {
01010       ast_cli(fd, "%s is not a known channel\n", argv[2]);
01011       return RESULT_SUCCESS;
01012    }
01013    if(c->cdr) {
01014       elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01015       hour = elapsed_seconds / 3600;
01016       min = (elapsed_seconds % 3600) / 60;
01017       sec = elapsed_seconds % 60;
01018       snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
01019    } else
01020       strcpy(cdrtime, "N/A");
01021    ast_cli(fd, 
01022       " -- General --\n"
01023       "           Name: %s\n"
01024       "           Type: %s\n"
01025       "       UniqueID: %s\n"
01026       "      Caller ID: %s\n"
01027       " Caller ID Name: %s\n"
01028       "    DNID Digits: %s\n"
01029       "          State: %s (%d)\n"
01030       "          Rings: %d\n"
01031       "  NativeFormats: %s\n"
01032       "    WriteFormat: %s\n"
01033       "     ReadFormat: %s\n"
01034       " WriteTranscode: %s\n"
01035       "  ReadTranscode: %s\n"
01036       "1st File Descriptor: %d\n"
01037       "      Frames in: %d%s\n"
01038       "     Frames out: %d%s\n"
01039       " Time to Hangup: %ld\n"
01040       "   Elapsed Time: %s\n"
01041       "  Direct Bridge: %s\n"
01042       "Indirect Bridge: %s\n"
01043       " --   PBX   --\n"
01044       "        Context: %s\n"
01045       "      Extension: %s\n"
01046       "       Priority: %d\n"
01047       "     Call Group: %llu\n"
01048       "   Pickup Group: %llu\n"
01049       "    Application: %s\n"
01050       "           Data: %s\n"
01051       "    Blocking in: %s\n",
01052       c->name, c->tech->type, c->uniqueid,
01053       S_OR(c->cid.cid_num, "(N/A)"),
01054       S_OR(c->cid.cid_name, "(N/A)"),
01055       S_OR(c->cid.cid_dnid, "(N/A)"), ast_state2str(c->_state), c->_state, c->rings, 
01056       ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats), 
01057       ast_getformatname_multiple(wf, sizeof(wf), c->writeformat), 
01058       ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
01059       c->writetrans ? "Yes" : "No",
01060       c->readtrans ? "Yes" : "No",
01061       c->fds[0],
01062       c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01063       c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01064       (long)c->whentohangup,
01065       cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>", 
01066       c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
01067       ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
01068       (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
01069    
01070    if(pbx_builtin_serialize_variables(c,buf,sizeof(buf)))
01071       ast_cli(fd,"      Variables:\n%s\n",buf);
01072    if(c->cdr && ast_cdr_serialize_variables(c->cdr,buf, sizeof(buf), '=', '\n', 1))
01073       ast_cli(fd,"  CDR Variables:\n%s\n",buf);
01074    
01075    ast_channel_unlock(c);
01076    return RESULT_SUCCESS;
01077 }
01078 
01079 static int handle_showchan(int fd, int argc, char *argv[])
01080 {
01081    struct ast_channel *c=NULL;
01082    struct timeval now;
01083    char buf[2048];
01084    char cdrtime[256];
01085    char nf[256], wf[256], rf[256];
01086    long elapsed_seconds=0;
01087    int hour=0, min=0, sec=0;
01088    
01089    if (argc != 4)
01090       return RESULT_SHOWUSAGE;
01091    now = ast_tvnow();
01092    c = ast_get_channel_by_name_locked(argv[3]);
01093    if (!c) {
01094       ast_cli(fd, "%s is not a known channel\n", argv[3]);
01095       return RESULT_SUCCESS;
01096    }
01097    if(c->cdr) {
01098       elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01099       hour = elapsed_seconds / 3600;
01100       min = (elapsed_seconds % 3600) / 60;
01101       sec = elapsed_seconds % 60;
01102       snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
01103    } else
01104       strcpy(cdrtime, "N/A");
01105    ast_cli(fd, 
01106       " -- General --\n"
01107       "           Name: %s\n"
01108       "           Type: %s\n"
01109       "       UniqueID: %s\n"
01110       "      Caller ID: %s\n"
01111       " Caller ID Name: %s\n"
01112       "    DNID Digits: %s\n"
01113       "          State: %s (%d)\n"
01114       "          Rings: %d\n"
01115       "  NativeFormats: %s\n"
01116       "    WriteFormat: %s\n"
01117       "     ReadFormat: %s\n"
01118       " WriteTranscode: %s\n"
01119       "  ReadTranscode: %s\n"
01120       "1st File Descriptor: %d\n"
01121       "      Frames in: %d%s\n"
01122       "     Frames out: %d%s\n"
01123       " Time to Hangup: %ld\n"
01124       "   Elapsed Time: %s\n"
01125       "  Direct Bridge: %s\n"
01126       "Indirect Bridge: %s\n"
01127       " --   PBX   --\n"
01128       "        Context: %s\n"
01129       "      Extension: %s\n"
01130       "       Priority: %d\n"
01131       "     Call Group: %llu\n"
01132       "   Pickup Group: %llu\n"
01133       "    Application: %s\n"
01134       "           Data: %s\n"
01135       "    Blocking in: %s\n",
01136       c->name, c->tech->type, c->uniqueid,
01137       S_OR(c->cid.cid_num, "(N/A)"),
01138       S_OR(c->cid.cid_name, "(N/A)"),
01139       S_OR(c->cid.cid_dnid, "(N/A)"), ast_state2str(c->_state), c->_state, c->rings, 
01140       ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats), 
01141       ast_getformatname_multiple(wf, sizeof(wf), c->writeformat), 
01142       ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
01143       c->writetrans ? "Yes" : "No",
01144       c->readtrans ? "Yes" : "No",
01145       c->fds[0],
01146       c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01147       c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01148       (long)c->whentohangup,
01149       cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>", 
01150       c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
01151       ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
01152       (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
01153    
01154    if(pbx_builtin_serialize_variables(c,buf,sizeof(buf)))
01155       ast_cli(fd,"      Variables:\n%s\n",buf);
01156    if(c->cdr && ast_cdr_serialize_variables(c->cdr,buf, sizeof(buf), '=', '\n', 1))
01157       ast_cli(fd,"  CDR Variables:\n%s\n",buf);
01158    
01159    ast_channel_unlock(c);
01160    return RESULT_SUCCESS;
01161 }
01162 
01163 /*
01164  * helper function to generate CLI matches from a fixed set of values.
01165  * A NULL word is acceptable.
01166  */
01167 char *ast_cli_complete(const char *word, char *const choices[], int state)
01168 {
01169    int i, which = 0, len;
01170    len = ast_strlen_zero(word) ? 0 : strlen(word);
01171 
01172    for (i = 0; choices[i]; i++) {
01173       if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
01174          return ast_strdup(choices[i]);
01175    }
01176    return NULL;
01177 }
01178 
01179 static char *complete_show_channels_deprecated(const char *line, const char *word, int pos, int state)
01180 {
01181    static char *choices[] = { "concise", "verbose", NULL };
01182 
01183    return (pos != 2) ? NULL : ast_cli_complete(word, choices, state);
01184 }
01185 
01186 static char *complete_show_channels(const char *line, const char *word, int pos, int state)
01187 {
01188    static char *choices[] = { "concise", "verbose", NULL };
01189 
01190    return (pos != 3) ? NULL : ast_cli_complete(word, choices, state);
01191 }
01192 
01193 char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
01194 {
01195    struct ast_channel *c = NULL;
01196    int which = 0;
01197    int wordlen;
01198    char notfound = '\0';
01199    char *ret = &notfound; /* so NULL can break the loop */
01200 
01201    if (pos != rpos)
01202       return NULL;
01203 
01204    wordlen = strlen(word); 
01205 
01206    while (ret == &notfound && (c = ast_channel_walk_locked(c))) {
01207       if (!strncasecmp(word, c->name, wordlen) && ++which > state)
01208          ret = ast_strdup(c->name);
01209       ast_channel_unlock(c);
01210    }
01211    return ret == &notfound ? NULL : ret;
01212 }
01213 
01214 static char *complete_ch_3(const char *line, const char *word, int pos, int state)
01215 {
01216    return ast_complete_channels(line, word, pos, state, 2);
01217 }
01218 
01219 static char *complete_ch_4(const char *line, const char *word, int pos, int state)
01220 {
01221    return ast_complete_channels(line, word, pos, state, 3);
01222 }
01223 
01224 static char *complete_ch_5(const char *line, const char *word, int pos, int state)
01225 {
01226    return ast_complete_channels(line, word, pos, state, 4);
01227 }
01228 
01229 static char *complete_mod_2(const char *line, const char *word, int pos, int state)
01230 {
01231    return ast_module_helper(line, word, pos, state, 1, 1);
01232 }
01233 
01234 static char *complete_mod_3_nr(const char *line, const char *word, int pos, int state)
01235 {
01236    return ast_module_helper(line, word, pos, state, 2, 0);
01237 }
01238 
01239 static char *complete_mod_3(const char *line, const char *word, int pos, int state)
01240 {
01241    return ast_module_helper(line, word, pos, state, 2, 1);
01242 }
01243 
01244 static char *complete_mod_4(const char *line, const char *word, int pos, int state)
01245 {
01246    return ast_module_helper(line, word, pos, state, 3, 0);
01247 }
01248 
01249 static char *complete_fn_2(const char *line, const char *word, int pos, int state)
01250 {
01251    char *c;
01252    char filename[256];
01253 
01254    if (pos != 1)
01255       return NULL;
01256    
01257    if (word[0] == '/')
01258       ast_copy_string(filename, word, sizeof(filename));
01259    else
01260       snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
01261    
01262    c = filename_completion_function(filename, state);
01263    
01264    if (c && word[0] != '/')
01265       c += (strlen(ast_config_AST_MODULE_DIR) + 1);
01266    
01267    return c ? strdup(c) : c;
01268 }
01269 
01270 static char *complete_fn_3(const char *line, const char *word, int pos, int state)
01271 {
01272    char *c;
01273    char filename[256];
01274 
01275    if (pos != 2)
01276       return NULL;
01277    
01278    if (word[0] == '/')
01279       ast_copy_string(filename, word, sizeof(filename));
01280    else
01281       snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
01282    
01283    c = filename_completion_function(filename, state);
01284    
01285    if (c && word[0] != '/')
01286       c += (strlen(ast_config_AST_MODULE_DIR) + 1);
01287    
01288    return c ? strdup(c) : c;
01289 }
01290 
01291 static int group_show_channels(int fd, int argc, char *argv[])
01292 {
01293 #define FORMAT_STRING  "%-25s  %-20s  %-20s\n"
01294 
01295    struct ast_group_info *gi = NULL;
01296    int numchans = 0;
01297    regex_t regexbuf;
01298    int havepattern = 0;
01299 
01300    if (argc < 3 || argc > 4)
01301       return RESULT_SHOWUSAGE;
01302    
01303    if (argc == 4) {
01304       if (regcomp(&regexbuf, argv[3], REG_EXTENDED | REG_NOSUB))
01305          return RESULT_SHOWUSAGE;
01306       havepattern = 1;
01307    }
01308 
01309    ast_cli(fd, FORMAT_STRING, "Channel", "Group", "Category");
01310 
01311    ast_app_group_list_lock();
01312    
01313    gi = ast_app_group_list_head();
01314    while (gi) {
01315       if (!havepattern || !regexec(&regexbuf, gi->group, 0, NULL, 0)) {
01316          ast_cli(fd, FORMAT_STRING, gi->chan->name, gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
01317          numchans++;
01318       }
01319       gi = AST_LIST_NEXT(gi, list);
01320    }
01321    
01322    ast_app_group_list_unlock();
01323    
01324    if (havepattern)
01325       regfree(&regexbuf);
01326 
01327    ast_cli(fd, "%d active channel%s\n", numchans, (numchans != 1) ? "s" : "");
01328    return RESULT_SUCCESS;
01329 #undef FORMAT_STRING
01330 }
01331 
01332 static int handle_help(int fd, int argc, char *argv[]);
01333 
01334 static char * complete_help(const char *text, const char *word, int pos, int state)
01335 {
01336    /* skip first 4 or 5 chars, "help "*/
01337    int l = strlen(text);
01338 
01339    if (l > 5)
01340       l = 5;
01341    text += l;
01342    /* XXX watch out, should stop to the non-generator parts */
01343    return __ast_cli_generator(text, word, state, 0);
01344 }
01345 
01346 /* XXX Nothing in this array can currently be deprecated...
01347    You have to change the way find_cli works in order to remove this array
01348    I recommend doing this eventually...
01349  */
01350 static struct ast_cli_entry builtins[] = {
01351    /* Keep alphabetized, with longer matches first (example: abcd before abc) */
01352    { { "_command", "complete", NULL },
01353    handle_commandcomplete, "Command complete",
01354    commandcomplete_help },
01355 
01356    { { "_command", "nummatches", NULL },
01357    handle_commandnummatches, "Returns number of command matches",
01358    commandnummatches_help },
01359 
01360    { { "_command", "matchesarray", NULL },
01361    handle_commandmatchesarray, "Returns command matches array",
01362    commandmatchesarray_help },
01363 
01364    { { NULL }, NULL, NULL, NULL }
01365 };
01366 
01367 static struct ast_cli_entry cli_debug_channel_deprecated = {
01368    { "debug", "channel", NULL },
01369    handle_debugchan_deprecated, NULL,
01370    NULL, complete_ch_3 };
01371 
01372 static struct ast_cli_entry cli_debug_level_deprecated = {
01373    { "debug", "level", NULL },
01374    handle_debuglevel_deprecated, NULL,
01375    NULL };
01376 
01377 static struct ast_cli_entry cli_set_debug_deprecated = {
01378    { "set", "debug", NULL },
01379    handle_set_debug_deprecated, NULL,
01380    NULL, NULL, &cli_debug_level_deprecated };
01381 
01382 static struct ast_cli_entry cli_set_verbose_deprecated = {
01383    { "set", "verbose", NULL },
01384    handle_set_verbose_deprecated, NULL,
01385    NULL };
01386 
01387 static struct ast_cli_entry cli_show_channel_deprecated = {
01388    { "show", "channel", NULL },
01389    handle_showchan_deprecated, NULL,
01390    NULL, complete_ch_3 };
01391 
01392 static struct ast_cli_entry cli_show_channels_deprecated = {
01393    { "show", "channels", NULL },
01394    handle_chanlist_deprecated, NULL,
01395    NULL, complete_show_channels_deprecated };
01396 
01397 static struct ast_cli_entry cli_show_modules_deprecated = {
01398    { "show", "modules", NULL },
01399    handle_modlist, NULL,
01400    NULL };
01401 
01402 static struct ast_cli_entry cli_show_modules_like_deprecated = {
01403    { "show", "modules", "like", NULL },
01404    handle_modlist, NULL,
01405    NULL, complete_mod_4 };
01406 
01407 static struct ast_cli_entry cli_module_load_deprecated = {
01408    { "load", NULL },
01409    handle_load_deprecated, NULL,
01410    NULL, complete_fn_2 };
01411 
01412 static struct ast_cli_entry cli_module_reload_deprecated = {
01413    { "reload", NULL },
01414    handle_reload_deprecated, NULL,
01415    NULL, complete_mod_2 };
01416 
01417 static struct ast_cli_entry cli_module_unload_deprecated = {
01418    { "unload", NULL },
01419    handle_unload_deprecated, NULL,
01420    NULL, complete_mod_2 };
01421 
01422 static struct ast_cli_entry cli_show_uptime_deprecated = {
01423    { "show", "uptime", NULL },
01424    handle_showuptime_deprecated, "Show uptime information",
01425    NULL };
01426 
01427 static struct ast_cli_entry cli_cli[] = {
01428    /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
01429    { { "no", "debug", "channel", NULL },
01430    handle_nodebugchan_deprecated, NULL,
01431    NULL, complete_ch_4 },
01432 
01433    { { "core", "show", "channels", NULL },
01434    handle_chanlist, "Display information on channels",
01435    chanlist_help, complete_show_channels, &cli_show_channels_deprecated },
01436 
01437    { { "core", "show", "channel", NULL },
01438    handle_showchan, "Display information on a specific channel",
01439    showchan_help, complete_ch_4, &cli_show_channel_deprecated },
01440 
01441    { { "core", "set", "debug", "channel", NULL },
01442    handle_core_set_debug_channel, "Enable/disable debugging on a channel",
01443    debugchan_help, complete_ch_5, &cli_debug_channel_deprecated },
01444 
01445    { { "core", "set", "debug", NULL },
01446    handle_set_debug, "Set level of debug chattiness",
01447    debug_help, NULL, &cli_set_debug_deprecated },
01448 
01449    { { "core", "set", "debug", "off", NULL },
01450    handle_nodebug, "Turns off debug chattiness",
01451    nodebug_help },
01452 
01453    { { "core", "set", "verbose", NULL },
01454    handle_verbose, "Set level of verboseness",
01455    verbose_help, NULL, &cli_set_verbose_deprecated },
01456 
01457    { { "group", "show", "channels", NULL },
01458    group_show_channels, "Display active channels with group(s)",
01459    group_show_channels_help },
01460 
01461    { { "help", NULL },
01462    handle_help, "Display help list, or specific help on a command",
01463    help_help, complete_help },
01464 
01465    { { "logger", "mute", NULL },
01466    handle_logger_mute, "Toggle logging output to a console",
01467    logger_mute_help },
01468 
01469    { { "module", "show", NULL },
01470    handle_modlist, "List modules and info",
01471    modlist_help, NULL, &cli_show_modules_deprecated },
01472 
01473    { { "module", "show", "like", NULL },
01474    handle_modlist, "List modules and info",
01475    modlist_help, complete_mod_4, &cli_show_modules_like_deprecated },
01476 
01477    { { "module", "load", NULL },
01478    handle_load, "Load a module by name",
01479    load_help, complete_fn_3, &cli_module_load_deprecated },
01480 
01481    { { "module", "reload", NULL },
01482    handle_reload, "Reload configuration",
01483    reload_help, complete_mod_3, &cli_module_reload_deprecated },
01484 
01485    { { "module", "unload", NULL },
01486    handle_unload, "Unload a module by name",
01487    unload_help, complete_mod_3_nr, &cli_module_unload_deprecated },
01488 
01489    { { "core", "show", "uptime", NULL },
01490    handle_showuptime, "Show uptime information",
01491    uptime_help, NULL, &cli_show_uptime_deprecated },
01492 
01493    { { "soft", "hangup", NULL },
01494    handle_softhangup, "Request a hangup on a given channel",
01495    softhangup_help, complete_ch_3 },
01496 };
01497 
01498 /*! \brief initialize the _full_cmd string in * each of the builtins. */
01499 void ast_builtins_init(void)
01500 {
01501    struct ast_cli_entry *e;
01502 
01503    for (e = builtins; e->cmda[0] != NULL; e++) {
01504       char buf[80];
01505       ast_join(buf, sizeof(buf), e->cmda);
01506       e->_full_cmd = strdup(buf);
01507       if (!e->_full_cmd)
01508          ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
01509    }
01510 
01511    ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
01512 }
01513 
01514 /*
01515  * We have two sets of commands: builtins are stored in a
01516  * NULL-terminated array of ast_cli_entry, whereas external
01517  * commands are in a list.
01518  * When navigating, we need to keep two pointers and get
01519  * the next one in lexicographic order. For the purpose,
01520  * we use a structure.
01521  */
01522 
01523 struct cli_iterator {
01524    struct ast_cli_entry *builtins;
01525    struct ast_cli_entry *helpers;
01526 };
01527 
01528 static struct ast_cli_entry *cli_next(struct cli_iterator *i)
01529 {
01530    struct ast_cli_entry *e;
01531 
01532    if (i->builtins == NULL && i->helpers == NULL) {
01533       /* initialize */
01534       i->builtins = builtins;
01535       i->helpers = AST_LIST_FIRST(&helpers);
01536    }
01537    e = i->builtins; /* temporary */
01538    if (!e->cmda[0] || (i->helpers &&
01539           strcmp(i->helpers->_full_cmd, e->_full_cmd) < 0)) {
01540       /* Use helpers */
01541       e = i->helpers;
01542       if (e)
01543          i->helpers = AST_LIST_NEXT(e, list);
01544    } else { /* use builtin. e is already set  */
01545       (i->builtins)++;  /* move to next */
01546    }
01547    return e;
01548 }
01549 
01550 /*!
01551  * \brief locate a cli command in the 'helpers' list (which must be locked).
01552  * exact has 3 values:
01553  *      0       returns if the search key is equal or longer than the entry.
01554  *      -1      true if the mismatch is on the last word XXX not true!
01555  *      1       true only on complete, exact match.
01556  */
01557 static struct ast_cli_entry *find_cli(char *const cmds[], int match_type)
01558 {
01559    int matchlen = -1;   /* length of longest match so far */
01560    struct ast_cli_entry *cand = NULL, *e=NULL;
01561    struct cli_iterator i = { NULL, NULL};
01562 
01563    while( (e = cli_next(&i)) ) {
01564       int y;
01565       for (y = 0 ; cmds[y] && e->cmda[y]; y++) {
01566          if (strcasecmp(e->cmda[y], cmds[y]))
01567             break;
01568       }
01569       if (e->cmda[y] == NULL) {  /* no more words in candidate */
01570          if (cmds[y] == NULL) /* this is an exact match, cannot do better */
01571             break;
01572          /* here the search key is longer than the candidate */
01573          if (match_type != 0) /* but we look for almost exact match... */
01574             continue;   /* so we skip this one. */
01575          /* otherwise we like it (case 0) */
01576       } else {       /* still words in candidate */
01577          if (cmds[y] == NULL) /* search key is shorter, not good */
01578             continue;
01579          /* if we get here, both words exist but there is a mismatch */
01580          if (match_type == 0) /* not the one we look for */
01581             continue;
01582          if (match_type == 1) /* not the one we look for */
01583             continue;
01584          if (cmds[y+1] != NULL || e->cmda[y+1] != NULL)  /* not the one we look for */
01585             continue;
01586          /* we are in case match_type == -1 and mismatch on last word */
01587       }
01588       if (y > matchlen) {  /* remember the candidate */
01589          matchlen = y;
01590          cand = e;
01591       }
01592    }
01593    return e ? e : cand;
01594 }
01595 
01596 static char *find_best(char *argv[])
01597 {
01598    static char cmdline[80];
01599    int x;
01600    /* See how close we get, then print the candidate */
01601    char *myargv[AST_MAX_CMD_LEN];
01602    for (x=0;x<AST_MAX_CMD_LEN;x++)
01603       myargv[x]=NULL;
01604    AST_LIST_LOCK(&helpers);
01605    for (x=0;argv[x];x++) {
01606       myargv[x] = argv[x];
01607       if (!find_cli(myargv, -1))
01608          break;
01609    }
01610    AST_LIST_UNLOCK(&helpers);
01611    ast_join(cmdline, sizeof(cmdline), myargv);
01612    return cmdline;
01613 }
01614 
01615 static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
01616 {
01617    if (e->deprecate_cmd) {
01618       __ast_cli_unregister(e->deprecate_cmd, e);
01619    }
01620    if (e->inuse) {
01621       ast_log(LOG_WARNING, "Can't remove command that is in use\n");
01622    } else {
01623       AST_LIST_LOCK(&helpers);
01624       AST_LIST_REMOVE(&helpers, e, list);
01625       AST_LIST_UNLOCK(&helpers);
01626       free(e->_full_cmd);
01627    }
01628    return 0;
01629 }
01630 
01631 static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
01632 {
01633    struct ast_cli_entry *cur;
01634    char fulle[80] ="";
01635    int lf, ret = -1;
01636    
01637    ast_join(fulle, sizeof(fulle), e->cmda);
01638    AST_LIST_LOCK(&helpers);
01639    
01640    if (find_cli(e->cmda, 1)) {
01641       ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", fulle);
01642       goto done;
01643    }
01644    e->_full_cmd = ast_strdup(fulle);
01645    if (!e->_full_cmd)
01646       goto done;
01647 
01648    if (ed) {
01649       e->deprecated = 1;
01650       e->summary = ed->summary;
01651       e->usage = ed->usage;
01652       /* XXX If command A deprecates command B, and command B deprecates command C...
01653          Do we want to show command A or command B when telling the user to use new syntax?
01654          This currently would show command A.
01655          To show command B, you just need to always use ed->_full_cmd.
01656        */
01657       e->_deprecated_by = S_OR(ed->_deprecated_by, ed->_full_cmd);
01658    } else {
01659       e->deprecated = 0;
01660    }
01661 
01662    lf = strlen(fulle);
01663    AST_LIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
01664       int len = strlen(cur->_full_cmd);
01665       if (lf < len)
01666          len = lf;
01667       if (strncasecmp(fulle, cur->_full_cmd, len) < 0) {
01668          AST_LIST_INSERT_BEFORE_CURRENT(&helpers, e, list); 
01669          break;
01670       }
01671    }
01672    AST_LIST_TRAVERSE_SAFE_END;
01673 
01674    if (!cur)
01675       AST_LIST_INSERT_TAIL(&helpers, e, list); 
01676    ret = 0; /* success */
01677 
01678 done:
01679    AST_LIST_UNLOCK(&helpers);
01680 
01681    if (e->deprecate_cmd) {
01682       /* This command deprecates another command.  Register that one also. */
01683       __ast_cli_register(e->deprecate_cmd, e);
01684    }
01685    
01686    return ret;
01687 }
01688 
01689 /* wrapper function, so we can unregister deprecated commands recursively */
01690 int ast_cli_unregister(struct ast_cli_entry *e)
01691 {
01692    return __ast_cli_unregister(e, NULL);
01693 }
01694 
01695 /* wrapper function, so we can register deprecated commands recursively */
01696 int ast_cli_register(struct ast_cli_entry *e)
01697 {
01698    return __ast_cli_register(e, NULL);
01699 }
01700 
01701 /*
01702  * register/unregister an array of entries.
01703  */
01704 void ast_cli_register_multiple(struct ast_cli_entry *e, int len)
01705 {
01706    int i;
01707 
01708    for (i = 0; i < len; i++)
01709       ast_cli_register(e + i);
01710 }
01711 
01712 void ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
01713 {
01714    int i;
01715 
01716    for (i = 0; i < len; i++)
01717       ast_cli_unregister(e + i);
01718 }
01719 
01720 
01721 /*! \brief helper for help_workhorse and final part of
01722  * handle_help. if locked = 0 it's just help_workhorse,
01723  * otherwise assume the list is already locked and print
01724  * an error message if not found.
01725  */
01726 static int help1(int fd, char *match[], int locked)
01727 {
01728    char matchstr[80] = "";
01729    struct ast_cli_entry *e;
01730    int len = 0;
01731    int found = 0;
01732    struct cli_iterator i = { NULL, NULL};
01733 
01734    if (match) {
01735       ast_join(matchstr, sizeof(matchstr), match);
01736       len = strlen(matchstr);
01737    }
01738    if (!locked)
01739       AST_LIST_LOCK(&helpers);
01740    while ( (e = cli_next(&i)) ) {
01741       /* Hide commands that start with '_' */
01742       if (e->_full_cmd[0] == '_')
01743          continue;
01744       /* Hide commands that are marked as deprecated. */
01745       if (e->deprecated)
01746          continue;
01747       if (match && strncasecmp(matchstr, e->_full_cmd, len))
01748          continue;
01749       ast_cli(fd, "%25.25s  %s\n", e->_full_cmd, S_OR(e->summary, ""));
01750       found++;
01751    }
01752    AST_LIST_UNLOCK(&helpers);
01753    if (!locked && !found && matchstr[0])
01754       ast_cli(fd, "No such command '%s'.\n", matchstr);
01755    return 0;
01756 }
01757 
01758 static int help_workhorse(int fd, char *match[])
01759 {
01760    return help1(fd, match, 0 /* do not print errors */);
01761 }
01762 
01763 static int handle_help(int fd, int argc, char *argv[])
01764 {
01765    char fullcmd[80];
01766    struct ast_cli_entry *e;
01767 
01768    if (argc < 1)
01769       return RESULT_SHOWUSAGE;
01770    if (argc == 1)
01771       return help_workhorse(fd, NULL);
01772 
01773    AST_LIST_LOCK(&helpers);
01774    e = find_cli(argv + 1, 1); /* try exact match first */
01775    if (!e)
01776       return help1(fd, argv + 1, 1 /* locked */);
01777    if (e->usage)
01778       ast_cli(fd, "%s", e->usage);
01779    else {
01780       ast_join(fullcmd, sizeof(fullcmd), argv+1);
01781       ast_cli(fd, "No help text available for '%s'.\n", fullcmd);
01782    }
01783    AST_LIST_UNLOCK(&helpers);
01784    return RESULT_SUCCESS;
01785 }
01786 
01787 static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
01788 {
01789    char *dup, *cur;
01790    int x = 0;
01791    int quoted = 0;
01792    int escaped = 0;
01793    int whitespace = 1;
01794 
01795    *trailingwhitespace = 0;
01796    if (s == NULL) /* invalid, though! */
01797       return NULL;
01798    /* make a copy to store the parsed string */
01799    if (!(dup = ast_strdup(s)))
01800       return NULL;
01801 
01802    cur = dup;
01803    /* scan the original string copying into cur when needed */
01804    for (; *s ; s++) {
01805       if (x >= max - 1) {
01806          ast_log(LOG_WARNING, "Too many arguments, truncating at %s\n", s);
01807          break;
01808       }
01809       if (*s == '"' && !escaped) {
01810          quoted = !quoted;
01811          if (quoted && whitespace) {
01812             /* start a quoted string from previous whitespace: new argument */
01813             argv[x++] = cur;
01814             whitespace = 0;
01815          }
01816       } else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) {
01817          /* If we are not already in whitespace, and not in a quoted string or
01818             processing an escape sequence, and just entered whitespace, then
01819             finalize the previous argument and remember that we are in whitespace
01820          */
01821          if (!whitespace) {
01822             *cur++ = '\0';
01823             whitespace = 1;
01824          }
01825       } else if (*s == '\\' && !escaped) {
01826          escaped = 1;
01827       } else {
01828          if (whitespace) {
01829             /* we leave whitespace, and are not quoted. So it's a new argument */
01830             argv[x++] = cur;
01831             whitespace = 0;
01832          }
01833          *cur++ = *s;
01834          escaped = 0;
01835       }
01836    }
01837    /* Null terminate */
01838    *cur++ = '\0';
01839    /* XXX put a NULL in the last argument, because some functions that take
01840     * the array may want a null-terminated array.
01841     * argc still reflects the number of non-NULL entries.
01842     */
01843    argv[x] = NULL;
01844    *argc = x;
01845    *trailingwhitespace = whitespace;
01846    return dup;
01847 }
01848 
01849 /*! \brief Return the number of unique matches for the generator */
01850 int ast_cli_generatornummatches(const char *text, const char *word)
01851 {
01852    int matches = 0, i = 0;
01853    char *buf = NULL, *oldbuf = NULL;
01854 
01855    while ((buf = ast_cli_generator(text, word, i++))) {
01856       if (!oldbuf || strcmp(buf,oldbuf))
01857          matches++;
01858       if (oldbuf)
01859          free(oldbuf);
01860       oldbuf = buf;
01861    }
01862    if (oldbuf)
01863       free(oldbuf);
01864    return matches;
01865 }
01866 
01867 char **ast_cli_completion_matches(const char *text, const char *word)
01868 {
01869    char **match_list = NULL, *retstr, *prevstr;
01870    size_t match_list_len, max_equal, which, i;
01871    int matches = 0;
01872 
01873    /* leave entry 0 free for the longest common substring */
01874    match_list_len = 1;
01875    while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
01876       if (matches + 1 >= match_list_len) {
01877          match_list_len <<= 1;
01878          if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
01879             return NULL;
01880       }
01881       match_list[++matches] = retstr;
01882    }
01883 
01884    if (!match_list)
01885       return match_list; /* NULL */
01886 
01887    /* Find the longest substring that is common to all results
01888     * (it is a candidate for completion), and store a copy in entry 0.
01889     */
01890    prevstr = match_list[1];
01891    max_equal = strlen(prevstr);
01892    for (which = 2; which <= matches; which++) {
01893       for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
01894          continue;
01895       max_equal = i;
01896    }
01897 
01898    if (!(retstr = ast_malloc(max_equal + 1)))
01899       return NULL;
01900    
01901    ast_copy_string(retstr, match_list[1], max_equal + 1);
01902    match_list[0] = retstr;
01903 
01904    /* ensure that the array is NULL terminated */
01905    if (matches + 1 >= match_list_len) {
01906       if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list))))
01907          return NULL;
01908    }
01909    match_list[matches + 1] = NULL;
01910 
01911    return match_list;
01912 }
01913 
01914 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
01915 {
01916    char *argv[AST_MAX_ARGS];
01917    struct ast_cli_entry *e;
01918    struct cli_iterator i = { NULL, NULL };
01919    int x = 0, argindex, matchlen;
01920    int matchnum=0;
01921    char *ret = NULL;
01922    char matchstr[80] = "";
01923    int tws = 0;
01924    char *dup = parse_args(text, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws);
01925 
01926    if (!dup)   /* error */
01927       return NULL;
01928    argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
01929    /* rebuild the command, ignore tws */
01930    ast_join(matchstr, sizeof(matchstr)-1, argv);
01931    matchlen = strlen(matchstr);
01932    if (tws) {
01933       strcat(matchstr, " "); /* XXX */
01934       if (matchlen)
01935          matchlen++;
01936    }
01937    if (lock)
01938       AST_LIST_LOCK(&helpers);
01939    while( !ret && (e = cli_next(&i)) ) {
01940       int lc = strlen(e->_full_cmd);
01941       if (e->_full_cmd[0] != '_' && lc > 0 && matchlen <= lc &&
01942             !strncasecmp(matchstr, e->_full_cmd, matchlen)) {
01943          /* Found initial part, return a copy of the next word... */
01944          if (e->cmda[argindex] && ++matchnum > state)
01945             ret = strdup(e->cmda[argindex]); /* we need a malloced string */
01946       } else if (e->generator && !strncasecmp(matchstr, e->_full_cmd, lc) && matchstr[lc] < 33) {
01947          /* We have a command in its entirity within us -- theoretically only one
01948             command can have this occur */
01949          ret = e->generator(matchstr, word, argindex, state);
01950       }
01951    }
01952    if (lock)
01953       AST_LIST_UNLOCK(&helpers);
01954    free(dup);
01955    return ret;
01956 }
01957 
01958 char *ast_cli_generator(const char *text, const char *word, int state)
01959 {
01960    return __ast_cli_generator(text, word, state, 1);
01961 }
01962 
01963 int ast_cli_command(int fd, const char *s)
01964 {
01965    char *argv[AST_MAX_ARGS];
01966    struct ast_cli_entry *e;
01967    int x;
01968    char *dup;
01969    int tws;
01970    
01971    if (!(dup = parse_args(s, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws)))
01972       return -1;
01973 
01974    /* We need at least one entry, or ignore */
01975    if (x > 0) {
01976       AST_LIST_LOCK(&helpers);
01977       e = find_cli(argv, 0);
01978       if (e)
01979          e->inuse++;
01980       AST_LIST_UNLOCK(&helpers);
01981       if (e) {
01982          switch(e->handler(fd, x, argv)) {
01983          case RESULT_SHOWUSAGE:
01984             if (e->usage)
01985                ast_cli(fd, "%s", e->usage);
01986             else
01987                ast_cli(fd, "Invalid usage, but no usage information available.\n");
01988             AST_LIST_LOCK(&helpers);
01989             if (e->deprecated)
01990                ast_cli(fd, "The '%s' command is deprecated and will be removed in a future release. Please use '%s' instead.\n", e->_full_cmd, e->_deprecated_by);
01991             AST_LIST_UNLOCK(&helpers);
01992             break;
01993          default:
01994             AST_LIST_LOCK(&helpers);
01995             if (e->deprecated == 1) {
01996                ast_cli(fd, "The '%s' command is deprecated and will be removed in a future release. Please use '%s' instead.\n", e->_full_cmd, e->_deprecated_by);
01997                e->deprecated = 2;
01998             }
01999             AST_LIST_UNLOCK(&helpers);
02000             break;
02001          }
02002       } else 
02003          ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(argv));
02004       if (e)
02005          ast_atomic_fetchadd_int(&e->inuse, -1);
02006    }
02007    free(dup);
02008    
02009    return 0;
02010 }
02011 
02012 int ast_cli_command_multiple(int fd, size_t size, const char *s)
02013 {
02014    char cmd[512];
02015    int x, y = 0, count = 0;
02016 
02017    for (x = 0; x < size; x++) {
02018       cmd[y] = s[x];
02019       y++;
02020       if (s[x] == '\0') {
02021          ast_cli_command(fd, cmd);
02022          y = 0;
02023          count++;
02024       }
02025    }
02026    return count;
02027 }

Generated on Fri Sep 25 19:28:11 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.5