Fri Aug 24 02:22:15 2007

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$")
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)
00406       return RESULT_SHOWUSAGE;
00407    ast_console_toggle_mute(fd);
00408    return RESULT_SUCCESS;
00409 }
00410 
00411 static int handle_unload_deprecated(int fd, int argc, char *argv[])
00412 {
00413    int x;
00414    int force = AST_FORCE_SOFT;
00415    if (argc < 2)
00416       return RESULT_SHOWUSAGE;
00417    for (x = 1; x < argc; x++) {
00418       if (argv[x][0] == '-') {
00419          switch(argv[x][1]) {
00420          case 'f':
00421             force = AST_FORCE_FIRM;
00422             break;
00423          case 'h':
00424             force = AST_FORCE_HARD;
00425             break;
00426          default:
00427             return RESULT_SHOWUSAGE;
00428          }
00429       } else if (x != argc - 1) 
00430          return RESULT_SHOWUSAGE;
00431       else if (ast_unload_resource(argv[x], force)) {
00432          ast_cli(fd, "Unable to unload resource %s\n", argv[x]);
00433          return RESULT_FAILURE;
00434       }
00435    }
00436    return RESULT_SUCCESS;
00437 }
00438 
00439 static int handle_unload(int fd, int argc, char *argv[])
00440 {
00441    int x;
00442    int force = AST_FORCE_SOFT;
00443    if (argc < 3)
00444       return RESULT_SHOWUSAGE;
00445    for (x = 2; x < argc; x++) {
00446       if (argv[x][0] == '-') {
00447          switch(argv[x][1]) {
00448          case 'f':
00449             force = AST_FORCE_FIRM;
00450             break;
00451          case 'h':
00452             force = AST_FORCE_HARD;
00453             break;
00454          default:
00455             return RESULT_SHOWUSAGE;
00456          }
00457       } else if (x != argc - 1) 
00458          return RESULT_SHOWUSAGE;
00459       else if (ast_unload_resource(argv[x], force)) {
00460          ast_cli(fd, "Unable to unload resource %s\n", argv[x]);
00461          return RESULT_FAILURE;
00462       }
00463    }
00464    return RESULT_SUCCESS;
00465 }
00466 
00467 #define MODLIST_FORMAT  "%-30s %-40.40s %-10d\n"
00468 #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
00469 
00470 AST_MUTEX_DEFINE_STATIC(climodentrylock);
00471 static int climodentryfd = -1;
00472 
00473 static int modlist_modentry(const char *module, const char *description, int usecnt, const char *like)
00474 {
00475    /* Comparing the like with the module */
00476    if (strcasestr(module, like) ) {
00477       ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
00478       return 1;
00479    } 
00480    return 0;
00481 }
00482 
00483 static char modlist_help[] =
00484 "Usage: module show [like <keyword>]\n"
00485 "       Shows Asterisk modules currently in use, and usage statistics.\n";
00486 
00487 static char uptime_help[] =
00488 "Usage: core show uptime [seconds]\n"
00489 "       Shows Asterisk uptime information.\n"
00490 "       The seconds word returns the uptime in seconds only.\n";
00491 
00492 static void print_uptimestr(int fd, time_t timeval, const char *prefix, int printsec)
00493 {
00494    int x; /* the main part - years, weeks, etc. */
00495    char timestr[256]="", *s = timestr;
00496    size_t maxbytes = sizeof(timestr);
00497 
00498 #define SECOND (1)
00499 #define MINUTE (SECOND*60)
00500 #define HOUR (MINUTE*60)
00501 #define DAY (HOUR*24)
00502 #define WEEK (DAY*7)
00503 #define YEAR (DAY*365)
00504 #define ESS(x) ((x == 1) ? "" : "s")   /* plural suffix */
00505 #define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */
00506    if (timeval < 0)  /* invalid, nothing to show */
00507       return;
00508    if (printsec)  {  /* plain seconds output */
00509       ast_build_string(&s, &maxbytes, "%lu", (u_long)timeval);
00510       timeval = 0; /* bypass the other cases */
00511    }
00512    if (timeval > YEAR) {
00513       x = (timeval / YEAR);
00514       timeval -= (x * YEAR);
00515       ast_build_string(&s, &maxbytes, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval));
00516    }
00517    if (timeval > WEEK) {
00518       x = (timeval / WEEK);
00519       timeval -= (x * WEEK);
00520       ast_build_string(&s, &maxbytes, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval));
00521    }
00522    if (timeval > DAY) {
00523       x = (timeval / DAY);
00524       timeval -= (x * DAY);
00525       ast_build_string(&s, &maxbytes, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval));
00526    }
00527    if (timeval > HOUR) {
00528       x = (timeval / HOUR);
00529       timeval -= (x * HOUR);
00530       ast_build_string(&s, &maxbytes, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval));
00531    }
00532    if (timeval > MINUTE) {
00533       x = (timeval / MINUTE);
00534       timeval -= (x * MINUTE);
00535       ast_build_string(&s, &maxbytes, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval));
00536    }
00537    x = timeval;
00538    if (x > 0)
00539       ast_build_string(&s, &maxbytes, "%d second%s ", x, ESS(x));
00540    if (timestr[0] != '\0')
00541       ast_cli(fd, "%s: %s\n", prefix, timestr);
00542 }
00543 
00544 static int handle_showuptime_deprecated(int fd, int argc, char *argv[])
00545 {
00546    /* 'show uptime [seconds]' */
00547    time_t curtime = time(NULL);
00548    int printsec = (argc == 3 && !strcasecmp(argv[2],"seconds"));
00549 
00550    if (argc != 2 && !printsec)
00551       return RESULT_SHOWUSAGE;
00552    if (ast_startuptime)
00553       print_uptimestr(fd, curtime - ast_startuptime, "System uptime", printsec);
00554    if (ast_lastreloadtime)
00555       print_uptimestr(fd, curtime - ast_lastreloadtime, "Last reload", printsec);
00556    return RESULT_SUCCESS;
00557 }
00558 
00559 static int handle_showuptime(int fd, int argc, char *argv[])
00560 {
00561    /* 'core show uptime [seconds]' */
00562    time_t curtime = time(NULL);
00563    int printsec = (argc == 4 && !strcasecmp(argv[3],"seconds"));
00564 
00565    if (argc != 3 && !printsec)
00566       return RESULT_SHOWUSAGE;
00567    if (ast_startuptime)
00568       print_uptimestr(fd, curtime - ast_startuptime, "System uptime", printsec);
00569    if (ast_lastreloadtime)
00570       print_uptimestr(fd, curtime - ast_lastreloadtime, "Last reload", printsec);
00571    return RESULT_SUCCESS;
00572 }
00573 
00574 static int handle_modlist(int fd, int argc, char *argv[])
00575 {
00576    char *like = "";
00577    if (argc == 3)
00578       return RESULT_SHOWUSAGE;
00579    else if (argc >= 4) {
00580       if (strcmp(argv[2],"like")) 
00581          return RESULT_SHOWUSAGE;
00582       like = argv[3];
00583    }
00584       
00585    ast_mutex_lock(&climodentrylock);
00586    climodentryfd = fd; /* global, protected by climodentrylock */
00587    ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
00588    ast_cli(fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
00589    climodentryfd = -1;
00590    ast_mutex_unlock(&climodentrylock);
00591    return RESULT_SUCCESS;
00592 }
00593 #undef MODLIST_FORMAT
00594 #undef MODLIST_FORMAT2
00595 
00596 #define FORMAT_STRING  "%-20.20s %-20.20s %-7.7s %-30.30s\n"
00597 #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
00598 #define CONCISE_FORMAT_STRING  "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s\n"
00599 #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"
00600 #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"
00601 
00602 static int handle_chanlist_deprecated(int fd, int argc, char *argv[])
00603 {
00604    struct ast_channel *c = NULL;
00605    char durbuf[10] = "-";
00606    char locbuf[40];
00607    char appdata[40];
00608    int duration;
00609    int durh, durm, durs;
00610    int numchans = 0, concise = 0, verbose = 0;
00611 
00612    concise = (argc == 3 && (!strcasecmp(argv[2],"concise")));
00613    verbose = (argc == 3 && (!strcasecmp(argv[2],"verbose")));
00614 
00615    if (argc < 2 || argc > 3 || (argc == 3 && !concise && !verbose))
00616       return RESULT_SHOWUSAGE;
00617 
00618    if (!concise && !verbose)
00619       ast_cli(fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
00620    else if (verbose)
00621       ast_cli(fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data", 
00622               "CallerID", "Duration", "Accountcode", "BridgedTo");
00623 
00624    while ((c = ast_channel_walk_locked(c)) != NULL) {
00625       struct ast_channel *bc = ast_bridged_channel(c);
00626       if ((concise || verbose)  && c->cdr && !ast_tvzero(c->cdr->start)) {
00627          duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
00628          if (verbose) {
00629             durh = duration / 3600;
00630             durm = (duration % 3600) / 60;
00631             durs = duration % 60;
00632             snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
00633          } else {
00634             snprintf(durbuf, sizeof(durbuf), "%d", duration);
00635          }           
00636       } else {
00637          durbuf[0] = '\0';
00638       }
00639       if (concise) {
00640          ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00641                  c->appl ? c->appl : "(None)",
00642             S_OR(c->data, ""),   /* XXX different from verbose ? */
00643                  S_OR(c->cid.cid_num, ""),
00644                  S_OR(c->accountcode, ""),
00645             c->amaflags, 
00646                  durbuf,
00647             bc ? bc->name : "(None)");
00648       } else if (verbose) {
00649          ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00650                  c->appl ? c->appl : "(None)",
00651             c->data ? S_OR(c->data, "(Empty)" ): "(None)",
00652                  S_OR(c->cid.cid_num, ""),
00653             durbuf,
00654                  S_OR(c->accountcode, ""),
00655             bc ? bc->name : "(None)");
00656       } else {
00657          if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten)) 
00658             snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
00659          else
00660             strcpy(locbuf, "(None)");
00661          if (c->appl)
00662             snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, c->data ? c->data : "");
00663          else
00664             strcpy(appdata, "(None)");
00665          ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
00666       }
00667       numchans++;
00668       ast_channel_unlock(c);
00669    }
00670    if (!concise) {
00671       ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
00672       if (option_maxcalls)
00673          ast_cli(fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
00674             ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
00675             ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
00676       else
00677          ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
00678    }
00679    return RESULT_SUCCESS;
00680 }
00681    
00682 static int handle_chanlist(int fd, int argc, char *argv[])
00683 {
00684    struct ast_channel *c = NULL;
00685    char durbuf[10] = "-";
00686    char locbuf[40];
00687    char appdata[40];
00688    int duration;
00689    int durh, durm, durs;
00690    int numchans = 0, concise = 0, verbose = 0;
00691 
00692    concise = (argc == 4 && (!strcasecmp(argv[3],"concise")));
00693    verbose = (argc == 4 && (!strcasecmp(argv[3],"verbose")));
00694 
00695    if (argc < 3 || argc > 4 || (argc == 4 && !concise && !verbose))
00696       return RESULT_SHOWUSAGE;
00697 
00698    if (!concise && !verbose)
00699       ast_cli(fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
00700    else if (verbose)
00701       ast_cli(fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data", 
00702               "CallerID", "Duration", "Accountcode", "BridgedTo");
00703 
00704    while ((c = ast_channel_walk_locked(c)) != NULL) {
00705       struct ast_channel *bc = ast_bridged_channel(c);
00706       if ((concise || verbose)  && c->cdr && !ast_tvzero(c->cdr->start)) {
00707          duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
00708          if (verbose) {
00709             durh = duration / 3600;
00710             durm = (duration % 3600) / 60;
00711             durs = duration % 60;
00712             snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
00713          } else {
00714             snprintf(durbuf, sizeof(durbuf), "%d", duration);
00715          }           
00716       } else {
00717          durbuf[0] = '\0';
00718       }
00719       if (concise) {
00720          ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00721                  c->appl ? c->appl : "(None)",
00722             S_OR(c->data, ""),   /* XXX different from verbose ? */
00723                  S_OR(c->cid.cid_num, ""),
00724                  S_OR(c->accountcode, ""),
00725             c->amaflags, 
00726                  durbuf,
00727             bc ? bc->name : "(None)");
00728       } else if (verbose) {
00729          ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00730                  c->appl ? c->appl : "(None)",
00731             c->data ? S_OR(c->data, "(Empty)" ): "(None)",
00732                  S_OR(c->cid.cid_num, ""),
00733             durbuf,
00734                  S_OR(c->accountcode, ""),
00735             bc ? bc->name : "(None)");
00736       } else {
00737          if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten)) 
00738             snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
00739          else
00740             strcpy(locbuf, "(None)");
00741          if (c->appl)
00742             snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, c->data ? c->data : "");
00743          else
00744             strcpy(appdata, "(None)");
00745          ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
00746       }
00747       numchans++;
00748       ast_channel_unlock(c);
00749    }
00750    if (!concise) {
00751       ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
00752       if (option_maxcalls)
00753          ast_cli(fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
00754             ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
00755             ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
00756       else
00757          ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
00758    }
00759    return RESULT_SUCCESS;
00760 }
00761    
00762 #undef FORMAT_STRING
00763 #undef FORMAT_STRING2
00764 #undef CONCISE_FORMAT_STRING
00765 #undef VERBOSE_FORMAT_STRING
00766 #undef VERBOSE_FORMAT_STRING2
00767 
00768 static char showchan_help[] = 
00769 "Usage: core show channel <channel>\n"
00770 "       Shows lots of information about the specified channel.\n";
00771 
00772 static char debugchan_help[] = 
00773 "Usage: core set debug channel <channel> [off]\n"
00774 "       Enables/disables debugging on a specific channel.\n";
00775 
00776 static char commandcomplete_help[] = 
00777 "Usage: _command complete \"<line>\" text state\n"
00778 "       This function is used internally to help with command completion and should.\n"
00779 "       never be called by the user directly.\n";
00780 
00781 static char commandnummatches_help[] = 
00782 "Usage: _command nummatches \"<line>\" text \n"
00783 "       This function is used internally to help with command completion and should.\n"
00784 "       never be called by the user directly.\n";
00785 
00786 static char commandmatchesarray_help[] = 
00787 "Usage: _command matchesarray \"<line>\" text \n"
00788 "       This function is used internally to help with command completion and should.\n"
00789 "       never be called by the user directly.\n";
00790 
00791 static int handle_softhangup(int fd, int argc, char *argv[])
00792 {
00793    struct ast_channel *c=NULL;
00794    if (argc != 3)
00795       return RESULT_SHOWUSAGE;
00796    c = ast_get_channel_by_name_locked(argv[2]);
00797    if (c) {
00798       ast_cli(fd, "Requested Hangup on channel '%s'\n", c->name);
00799       ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
00800       ast_channel_unlock(c);
00801    } else
00802       ast_cli(fd, "%s is not a known channel\n", argv[2]);
00803    return RESULT_SUCCESS;
00804 }
00805 
00806 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
00807 
00808 static int handle_commandmatchesarray(int fd, int argc, char *argv[])
00809 {
00810    char *buf, *obuf;
00811    int buflen = 2048;
00812    int len = 0;
00813    char **matches;
00814    int x, matchlen;
00815 
00816    if (argc != 4)
00817       return RESULT_SHOWUSAGE;
00818    if (!(buf = ast_malloc(buflen)))
00819       return RESULT_FAILURE;
00820    buf[len] = '\0';
00821    matches = ast_cli_completion_matches(argv[2], argv[3]);
00822    if (matches) {
00823       for (x=0; matches[x]; x++) {
00824          matchlen = strlen(matches[x]) + 1;
00825          if (len + matchlen >= buflen) {
00826             buflen += matchlen * 3;
00827             obuf = buf;
00828             if (!(buf = ast_realloc(obuf, buflen))) 
00829                /* Memory allocation failure...  Just free old buffer and be done */
00830                free(obuf);
00831          }
00832          if (buf)
00833             len += sprintf( buf + len, "%s ", matches[x]);
00834          free(matches[x]);
00835          matches[x] = NULL;
00836       }
00837       free(matches);
00838    }
00839 
00840    if (buf) {
00841       ast_cli(fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
00842       free(buf);
00843    } else
00844       ast_cli(fd, "NULL\n");
00845 
00846    return RESULT_SUCCESS;
00847 }
00848 
00849 
00850 
00851 static int handle_commandnummatches(int fd, int argc, char *argv[])
00852 {
00853    int matches = 0;
00854 
00855    if (argc != 4)
00856       return RESULT_SHOWUSAGE;
00857 
00858    matches = ast_cli_generatornummatches(argv[2], argv[3]);
00859 
00860    ast_cli(fd, "%d", matches);
00861 
00862    return RESULT_SUCCESS;
00863 }
00864 
00865 static int handle_commandcomplete(int fd, int argc, char *argv[])
00866 {
00867    char *buf;
00868 
00869    if (argc != 5)
00870       return RESULT_SHOWUSAGE;
00871    buf = __ast_cli_generator(argv[2], argv[3], atoi(argv[4]), 0);
00872    if (buf) {
00873       ast_cli(fd, buf);
00874       free(buf);
00875    } else
00876       ast_cli(fd, "NULL\n");
00877    return RESULT_SUCCESS;
00878 }
00879 
00880 static int handle_debugchan_deprecated(int fd, int argc, char *argv[])
00881 {
00882    struct ast_channel *c=NULL;
00883    int is_all;
00884 
00885    /* 'debug channel {all|chan_id}' */
00886    if (argc != 3)
00887       return RESULT_SHOWUSAGE;
00888 
00889    is_all = !strcasecmp("all", argv[2]);
00890    if (is_all) {
00891       global_fin |= DEBUGCHAN_FLAG;
00892       global_fout |= DEBUGCHAN_FLAG;
00893       c = ast_channel_walk_locked(NULL);
00894    } else {
00895       c = ast_get_channel_by_name_locked(argv[2]);
00896       if (c == NULL)
00897          ast_cli(fd, "No such channel %s\n", argv[2]);
00898    }
00899    while (c) {
00900       if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
00901          c->fin |= DEBUGCHAN_FLAG;
00902          c->fout |= DEBUGCHAN_FLAG;
00903          ast_cli(fd, "Debugging enabled on channel %s\n", c->name);
00904       }
00905       ast_channel_unlock(c);
00906       if (!is_all)
00907          break;
00908       c = ast_channel_walk_locked(c);
00909    }
00910    ast_cli(fd, "Debugging on new channels is enabled\n");
00911    return RESULT_SUCCESS;
00912 }
00913 
00914 static int handle_core_set_debug_channel(int fd, int argc, char *argv[])
00915 {
00916    struct ast_channel *c = NULL;
00917    int is_all, is_off = 0;
00918 
00919    /* 'core set debug channel {all|chan_id}' */
00920    if (argc == 6 && strcmp(argv[5], "off") == 0)
00921       is_off = 1;
00922    else if (argc != 5)
00923       return RESULT_SHOWUSAGE;
00924 
00925    is_all = !strcasecmp("all", argv[4]);
00926    if (is_all) {
00927       if (is_off) {
00928          global_fin &= ~DEBUGCHAN_FLAG;
00929          global_fout &= ~DEBUGCHAN_FLAG;
00930       } else {
00931          global_fin |= DEBUGCHAN_FLAG;
00932          global_fout |= DEBUGCHAN_FLAG;
00933       }
00934       c = ast_channel_walk_locked(NULL);
00935    } else {
00936       c = ast_get_channel_by_name_locked(argv[4]);
00937       if (c == NULL)
00938          ast_cli(fd, "No such channel %s\n", argv[4]);
00939    }
00940    while (c) {
00941       if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
00942          if (is_off) {
00943             c->fin &= ~DEBUGCHAN_FLAG;
00944             c->fout &= ~DEBUGCHAN_FLAG;
00945          } else {
00946             c->fin |= DEBUGCHAN_FLAG;
00947             c->fout |= DEBUGCHAN_FLAG;
00948          }
00949          ast_cli(fd, "Debugging %s on channel %s\n", is_off ? "disabled" : "enabled", c->name);
00950       }
00951       ast_channel_unlock(c);
00952       if (!is_all)
00953          break;
00954       c = ast_channel_walk_locked(c);
00955    }
00956    ast_cli(fd, "Debugging on new channels is %s\n", is_off ? "disabled" : "enabled");
00957    return RESULT_SUCCESS;
00958 }
00959 
00960 static int handle_nodebugchan_deprecated(int fd, int argc, char *argv[])
00961 {
00962    struct ast_channel *c=NULL;
00963    int is_all;
00964    /* 'no debug channel {all|chan_id}' */
00965    if (argc != 4)
00966       return RESULT_SHOWUSAGE;
00967    is_all = !strcasecmp("all", argv[3]);
00968    if (is_all) {
00969       global_fin &= ~DEBUGCHAN_FLAG;
00970       global_fout &= ~DEBUGCHAN_FLAG;
00971       c = ast_channel_walk_locked(NULL);
00972    } else {
00973       c = ast_get_channel_by_name_locked(argv[3]);
00974       if (c == NULL)
00975          ast_cli(fd, "No such channel %s\n", argv[3]);
00976    }
00977    while(c) {
00978       if ((c->fin & DEBUGCHAN_FLAG) || (c->fout & DEBUGCHAN_FLAG)) {
00979          c->fin &= ~DEBUGCHAN_FLAG;
00980          c->fout &= ~DEBUGCHAN_FLAG;
00981          ast_cli(fd, "Debugging disabled on channel %s\n", c->name);
00982       }
00983       ast_channel_unlock(c);
00984       if (!is_all)
00985          break;
00986       c = ast_channel_walk_locked(c);
00987    }
00988    ast_cli(fd, "Debugging on new channels is disabled\n");
00989    return RESULT_SUCCESS;
00990 }
00991       
00992 static int handle_showchan_deprecated(int fd, int argc, char *argv[])
00993 {
00994    struct ast_channel *c=NULL;
00995    struct timeval now;
00996    char buf[2048];
00997    char cdrtime[256];
00998    char nf[256], wf[256], rf[256];
00999    long elapsed_seconds=0;
01000    int hour=0, min=0, sec=0;
01001    
01002    if (argc != 3)
01003       return RESULT_SHOWUSAGE;
01004    now = ast_tvnow();
01005    c = ast_get_channel_by_name_locked(argv[2]);
01006    if (!c) {
01007       ast_cli(fd, "%s is not a known channel\n", argv[2]);
01008       return RESULT_SUCCESS;
01009    }
01010    if(c->cdr) {
01011       elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01012       hour = elapsed_seconds / 3600;
01013       min = (elapsed_seconds % 3600) / 60;
01014       sec = elapsed_seconds % 60;
01015       snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
01016    } else
01017       strcpy(cdrtime, "N/A");
01018    ast_cli(fd, 
01019       " -- General --\n"
01020       "           Name: %s\n"
01021       "           Type: %s\n"
01022       "       UniqueID: %s\n"
01023       "      Caller ID: %s\n"
01024       " Caller ID Name: %s\n"
01025       "    DNID Digits: %s\n"
01026       "          State: %s (%d)\n"
01027       "          Rings: %d\n"
01028       "  NativeFormats: %s\n"
01029       "    WriteFormat: %s\n"
01030       "     ReadFormat: %s\n"
01031       " WriteTranscode: %s\n"
01032       "  ReadTranscode: %s\n"
01033       "1st File Descriptor: %d\n"
01034       "      Frames in: %d%s\n"
01035       "     Frames out: %d%s\n"
01036       " Time to Hangup: %ld\n"
01037       "   Elapsed Time: %s\n"
01038       "  Direct Bridge: %s\n"
01039       "Indirect Bridge: %s\n"
01040       " --   PBX   --\n"
01041       "        Context: %s\n"
01042       "      Extension: %s\n"
01043       "       Priority: %d\n"
01044       "     Call Group: %llu\n"
01045       "   Pickup Group: %llu\n"
01046       "    Application: %s\n"
01047       "           Data: %s\n"
01048       "    Blocking in: %s\n",
01049       c->name, c->tech->type, c->uniqueid,
01050       S_OR(c->cid.cid_num, "(N/A)"),
01051       S_OR(c->cid.cid_name, "(N/A)"),
01052       S_OR(c->cid.cid_dnid, "(N/A)"), ast_state2str(c->_state), c->_state, c->rings, 
01053       ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats), 
01054       ast_getformatname_multiple(wf, sizeof(wf), c->writeformat), 
01055       ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
01056       c->writetrans ? "Yes" : "No",
01057       c->readtrans ? "Yes" : "No",
01058       c->fds[0],
01059       c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01060       c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01061       (long)c->whentohangup,
01062       cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>", 
01063       c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
01064       ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
01065       (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
01066    
01067    if(pbx_builtin_serialize_variables(c,buf,sizeof(buf)))
01068       ast_cli(fd,"      Variables:\n%s\n",buf);
01069    if(c->cdr && ast_cdr_serialize_variables(c->cdr,buf, sizeof(buf), '=', '\n', 1))
01070       ast_cli(fd,"  CDR Variables:\n%s\n",buf);
01071    
01072    ast_channel_unlock(c);
01073    return RESULT_SUCCESS;
01074 }
01075 
01076 static int handle_showchan(int fd, int argc, char *argv[])
01077 {
01078    struct ast_channel *c=NULL;
01079    struct timeval now;
01080    char buf[2048];
01081    char cdrtime[256];
01082    char nf[256], wf[256], rf[256];
01083    long elapsed_seconds=0;
01084    int hour=0, min=0, sec=0;
01085    
01086    if (argc != 4)
01087       return RESULT_SHOWUSAGE;
01088    now = ast_tvnow();
01089    c = ast_get_channel_by_name_locked(argv[3]);
01090    if (!c) {
01091       ast_cli(fd, "%s is not a known channel\n", argv[3]);
01092       return RESULT_SUCCESS;
01093    }
01094    if(c->cdr) {
01095       elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01096       hour = elapsed_seconds / 3600;
01097       min = (elapsed_seconds % 3600) / 60;
01098       sec = elapsed_seconds % 60;
01099       snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
01100    } else
01101       strcpy(cdrtime, "N/A");
01102    ast_cli(fd, 
01103       " -- General --\n"
01104       "           Name: %s\n"
01105       "           Type: %s\n"
01106       "       UniqueID: %s\n"
01107       "      Caller ID: %s\n"
01108       " Caller ID Name: %s\n"
01109       "    DNID Digits: %s\n"
01110       "          State: %s (%d)\n"
01111       "          Rings: %d\n"
01112       "  NativeFormats: %s\n"
01113       "    WriteFormat: %s\n"
01114       "     ReadFormat: %s\n"
01115       " WriteTranscode: %s\n"
01116       "  ReadTranscode: %s\n"
01117       "1st File Descriptor: %d\n"
01118       "      Frames in: %d%s\n"
01119       "     Frames out: %d%s\n"
01120       " Time to Hangup: %ld\n"
01121       "   Elapsed Time: %s\n"
01122       "  Direct Bridge: %s\n"
01123       "Indirect Bridge: %s\n"
01124       " --   PBX   --\n"
01125       "        Context: %s\n"
01126       "      Extension: %s\n"
01127       "       Priority: %d\n"
01128       "     Call Group: %llu\n"
01129       "   Pickup Group: %llu\n"
01130       "    Application: %s\n"
01131       "           Data: %s\n"
01132       "    Blocking in: %s\n",
01133       c->name, c->tech->type, c->uniqueid,
01134       S_OR(c->cid.cid_num, "(N/A)"),
01135       S_OR(c->cid.cid_name, "(N/A)"),
01136       S_OR(c->cid.cid_dnid, "(N/A)"), ast_state2str(c->_state), c->_state, c->rings, 
01137       ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats), 
01138       ast_getformatname_multiple(wf, sizeof(wf), c->writeformat), 
01139       ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
01140       c->writetrans ? "Yes" : "No",
01141       c->readtrans ? "Yes" : "No",
01142       c->fds[0],
01143       c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01144       c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01145       (long)c->whentohangup,
01146       cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>", 
01147       c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
01148       ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
01149       (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
01150    
01151    if(pbx_builtin_serialize_variables(c,buf,sizeof(buf)))
01152       ast_cli(fd,"      Variables:\n%s\n",buf);
01153    if(c->cdr && ast_cdr_serialize_variables(c->cdr,buf, sizeof(buf), '=', '\n', 1))
01154       ast_cli(fd,"  CDR Variables:\n%s\n",buf);
01155    
01156    ast_channel_unlock(c);
01157    return RESULT_SUCCESS;
01158 }
01159 
01160 /*
01161  * helper function to generate CLI matches from a fixed set of values.
01162  * A NULL word is acceptable.
01163  */
01164 char *ast_cli_complete(const char *word, char *const choices[], int state)
01165 {
01166    int i, which = 0, len;
01167    len = ast_strlen_zero(word) ? 0 : strlen(word);
01168 
01169    for (i = 0; choices[i]; i++) {
01170       if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
01171          return ast_strdup(choices[i]);
01172    }
01173    return NULL;
01174 }
01175 
01176 static char *complete_show_channels_deprecated(const char *line, const char *word, int pos, int state)
01177 {
01178    static char *choices[] = { "concise", "verbose", NULL };
01179 
01180    return (pos != 2) ? NULL : ast_cli_complete(word, choices, state);
01181 }
01182 
01183 static char *complete_show_channels(const char *line, const char *word, int pos, int state)
01184 {
01185    static char *choices[] = { "concise", "verbose", NULL };
01186 
01187    return (pos != 3) ? NULL : ast_cli_complete(word, choices, state);
01188 }
01189 
01190 char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
01191 {
01192    struct ast_channel *c = NULL;
01193    int which = 0;
01194    int wordlen;
01195    char notfound = '\0';
01196    char *ret = &notfound; /* so NULL can break the loop */
01197 
01198    if (pos != rpos)
01199       return NULL;
01200 
01201    wordlen = strlen(word); 
01202 
01203    while (ret == &notfound && (c = ast_channel_walk_locked(c))) {
01204       if (!strncasecmp(word, c->name, wordlen) && ++which > state)
01205          ret = ast_strdup(c->name);
01206       ast_channel_unlock(c);
01207    }
01208    return ret == &notfound ? NULL : ret;
01209 }
01210 
01211 static char *complete_ch_3(const char *line, const char *word, int pos, int state)
01212 {
01213    return ast_complete_channels(line, word, pos, state, 2);
01214 }
01215 
01216 static char *complete_ch_4(const char *line, const char *word, int pos, int state)
01217 {
01218    return ast_complete_channels(line, word, pos, state, 3);
01219 }
01220 
01221 static char *complete_ch_5(const char *line, const char *word, int pos, int state)
01222 {
01223    return ast_complete_channels(line, word, pos, state, 4);
01224 }
01225 
01226 static char *complete_mod_2(const char *line, const char *word, int pos, int state)
01227 {
01228    return ast_module_helper(line, word, pos, state, 1, 1);
01229 }
01230 
01231 static char *complete_mod_3_nr(const char *line, const char *word, int pos, int state)
01232 {
01233    return ast_module_helper(line, word, pos, state, 2, 0);
01234 }
01235 
01236 static char *complete_mod_3(const char *line, const char *word, int pos, int state)
01237 {
01238    return ast_module_helper(line, word, pos, state, 2, 1);
01239 }
01240 
01241 static char *complete_mod_4(const char *line, const char *word, int pos, int state)
01242 {
01243    return ast_module_helper(line, word, pos, state, 3, 0);
01244 }
01245 
01246 static char *complete_fn_2(const char *line, const char *word, int pos, int state)
01247 {
01248    char *c;
01249    char filename[256];
01250 
01251    if (pos != 1)
01252       return NULL;
01253    
01254    if (word[0] == '/')
01255       ast_copy_string(filename, word, sizeof(filename));
01256    else
01257       snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
01258    
01259    c = filename_completion_function(filename, state);
01260    
01261    if (c && word[0] != '/')
01262       c += (strlen(ast_config_AST_MODULE_DIR) + 1);
01263    
01264    return c ? strdup(c) : c;
01265 }
01266 
01267 static char *complete_fn_3(const char *line, const char *word, int pos, int state)
01268 {
01269    char *c;
01270    char filename[256];
01271 
01272    if (pos != 2)
01273       return NULL;
01274    
01275    if (word[0] == '/')
01276       ast_copy_string(filename, word, sizeof(filename));
01277    else
01278       snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
01279    
01280    c = filename_completion_function(filename, state);
01281    
01282    if (c && word[0] != '/')
01283       c += (strlen(ast_config_AST_MODULE_DIR) + 1);
01284    
01285    return c ? strdup(c) : c;
01286 }
01287 
01288 static int group_show_channels(int fd, int argc, char *argv[])
01289 {
01290 #define FORMAT_STRING  "%-25s  %-20s  %-20s\n"
01291 
01292    struct ast_group_info *gi = NULL;
01293    int numchans = 0;
01294    regex_t regexbuf;
01295    int havepattern = 0;
01296 
01297    if (argc < 3 || argc > 4)
01298       return RESULT_SHOWUSAGE;
01299    
01300    if (argc == 4) {
01301       if (regcomp(&regexbuf, argv[3], REG_EXTENDED | REG_NOSUB))
01302          return RESULT_SHOWUSAGE;
01303       havepattern = 1;
01304    }
01305 
01306    ast_cli(fd, FORMAT_STRING, "Channel", "Group", "Category");
01307 
01308    ast_app_group_list_lock();
01309    
01310    gi = ast_app_group_list_head();
01311    while (gi) {
01312       if (!havepattern || !regexec(&regexbuf, gi->group, 0, NULL, 0)) {
01313          ast_cli(fd, FORMAT_STRING, gi->chan->name, gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
01314          numchans++;
01315       }
01316       gi = AST_LIST_NEXT(gi, list);
01317    }
01318    
01319    ast_app_group_list_unlock();
01320    
01321    if (havepattern)
01322       regfree(&regexbuf);
01323 
01324    ast_cli(fd, "%d active channel%s\n", numchans, (numchans != 1) ? "s" : "");
01325    return RESULT_SUCCESS;
01326 #undef FORMAT_STRING
01327 }
01328 
01329 static int handle_help(int fd, int argc, char *argv[]);
01330 
01331 static char * complete_help(const char *text, const char *word, int pos, int state)
01332 {
01333    /* skip first 4 or 5 chars, "help "*/
01334    int l = strlen(text);
01335 
01336    if (l > 5)
01337       l = 5;
01338    text += l;
01339    /* XXX watch out, should stop to the non-generator parts */
01340    return __ast_cli_generator(text, word, state, 0);
01341 }
01342 
01343 /* XXX Nothing in this array can currently be deprecated...
01344    You have to change the way find_cli works in order to remove this array
01345    I recommend doing this eventually...
01346  */
01347 static struct ast_cli_entry builtins[] = {
01348    /* Keep alphabetized, with longer matches first (example: abcd before abc) */
01349    { { "_command", "complete", NULL },
01350    handle_commandcomplete, "Command complete",
01351    commandcomplete_help },
01352 
01353    { { "_command", "nummatches", NULL },
01354    handle_commandnummatches, "Returns number of command matches",
01355    commandnummatches_help },
01356 
01357    { { "_command", "matchesarray", NULL },
01358    handle_commandmatchesarray, "Returns command matches array",
01359    commandmatchesarray_help },
01360 
01361    { { NULL }, NULL, NULL, NULL }
01362 };
01363 
01364 static struct ast_cli_entry cli_debug_channel_deprecated = {
01365    { "debug", "channel", NULL },
01366    handle_debugchan_deprecated, NULL,
01367    NULL, complete_ch_3 };
01368 
01369 static struct ast_cli_entry cli_debug_level_deprecated = {
01370    { "debug", "level", NULL },
01371    handle_debuglevel_deprecated, NULL,
01372    NULL };
01373 
01374 static struct ast_cli_entry cli_set_debug_deprecated = {
01375    { "set", "debug", NULL },
01376    handle_set_debug_deprecated, NULL,
01377    NULL, NULL, &cli_debug_level_deprecated };
01378 
01379 static struct ast_cli_entry cli_set_verbose_deprecated = {
01380    { "set", "verbose", NULL },
01381    handle_set_verbose_deprecated, NULL,
01382    NULL };
01383 
01384 static struct ast_cli_entry cli_show_channel_deprecated = {
01385    { "show", "channel", NULL },
01386    handle_showchan_deprecated, NULL,
01387    NULL, complete_ch_3 };
01388 
01389 static struct ast_cli_entry cli_show_channels_deprecated = {
01390    { "show", "channels", NULL },
01391    handle_chanlist_deprecated, NULL,
01392    NULL, complete_show_channels_deprecated };
01393 
01394 static struct ast_cli_entry cli_show_modules_deprecated = {
01395    { "show", "modules", NULL },
01396    handle_modlist, NULL,
01397    NULL };
01398 
01399 static struct ast_cli_entry cli_show_modules_like_deprecated = {
01400    { "show", "modules", "like", NULL },
01401    handle_modlist, NULL,
01402    NULL, complete_mod_4 };
01403 
01404 static struct ast_cli_entry cli_module_load_deprecated = {
01405    { "load", NULL },
01406    handle_load_deprecated, NULL,
01407    NULL, complete_fn_2 };
01408 
01409 static struct ast_cli_entry cli_module_reload_deprecated = {
01410    { "reload", NULL },
01411    handle_reload_deprecated, NULL,
01412    NULL, complete_mod_2 };
01413 
01414 static struct ast_cli_entry cli_module_unload_deprecated = {
01415    { "unload", NULL },
01416    handle_unload_deprecated, NULL,
01417    NULL, complete_mod_2 };
01418 
01419 static struct ast_cli_entry cli_show_uptime_deprecated = {
01420    { "show", "uptime", NULL },
01421    handle_showuptime_deprecated, "Show uptime information",
01422    NULL };
01423 
01424 static struct ast_cli_entry cli_cli[] = {
01425    /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
01426    { { "no", "debug", "channel", NULL },
01427    handle_nodebugchan_deprecated, NULL,
01428    NULL, complete_ch_4 },
01429 
01430    { { "core", "show", "channels", NULL },
01431    handle_chanlist, "Display information on channels",
01432    chanlist_help, complete_show_channels, &cli_show_channels_deprecated },
01433 
01434    { { "core", "show", "channel", NULL },
01435    handle_showchan, "Display information on a specific channel",
01436    showchan_help, complete_ch_4, &cli_show_channel_deprecated },
01437 
01438    { { "core", "set", "debug", "channel", NULL },
01439    handle_core_set_debug_channel, "Enable/disable debugging on a channel",
01440    debugchan_help, complete_ch_5, &cli_debug_channel_deprecated },
01441 
01442    { { "core", "set", "debug", NULL },
01443    handle_set_debug, "Set level of debug chattiness",
01444    debug_help, NULL, &cli_set_debug_deprecated },
01445 
01446    { { "core", "set", "debug", "off", NULL },
01447    handle_nodebug, "Turns off debug chattiness",
01448    nodebug_help },
01449 
01450    { { "core", "set", "verbose", NULL },
01451    handle_verbose, "Set level of verboseness",
01452    verbose_help, NULL, &cli_set_verbose_deprecated },
01453 
01454    { { "group", "show", "channels", NULL },
01455    group_show_channels, "Display active channels with group(s)",
01456    group_show_channels_help },
01457 
01458    { { "help", NULL },
01459    handle_help, "Display help list, or specific help on a command",
01460    help_help, complete_help },
01461 
01462    { { "logger", "mute", NULL },
01463    handle_logger_mute, "Toggle logging output to a console",
01464    logger_mute_help },
01465 
01466    { { "module", "show", NULL },
01467    handle_modlist, "List modules and info",
01468    modlist_help, NULL, &cli_show_modules_deprecated },
01469 
01470    { { "module", "show", "like", NULL },
01471    handle_modlist, "List modules and info",
01472    modlist_help, complete_mod_4, &cli_show_modules_like_deprecated },
01473 
01474    { { "module", "load", NULL },
01475    handle_load, "Load a module by name",
01476    load_help, complete_fn_3, &cli_module_load_deprecated },
01477 
01478    { { "module", "reload", NULL },
01479    handle_reload, "Reload configuration",
01480    reload_help, complete_mod_3, &cli_module_reload_deprecated },
01481 
01482    { { "module", "unload", NULL },
01483    handle_unload, "Unload a module by name",
01484    unload_help, complete_mod_3_nr, &cli_module_unload_deprecated },
01485 
01486    { { "core", "show", "uptime", NULL },
01487    handle_showuptime, "Show uptime information",
01488    uptime_help, NULL, &cli_show_uptime_deprecated },
01489 
01490    { { "soft", "hangup", NULL },
01491    handle_softhangup, "Request a hangup on a given channel",
01492    softhangup_help, complete_ch_3 },
01493 };
01494 
01495 /*! \brief initialize the _full_cmd string in * each of the builtins. */
01496 void ast_builtins_init(void)
01497 {
01498    struct ast_cli_entry *e;
01499 
01500    for (e = builtins; e->cmda[0] != NULL; e++) {
01501       char buf[80];
01502       ast_join(buf, sizeof(buf), e->cmda);
01503       e->_full_cmd = strdup(buf);
01504       if (!e->_full_cmd)
01505          ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
01506    }
01507 
01508    ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
01509 }
01510 
01511 /*
01512  * We have two sets of commands: builtins are stored in a
01513  * NULL-terminated array of ast_cli_entry, whereas external
01514  * commands are in a list.
01515  * When navigating, we need to keep two pointers and get
01516  * the next one in lexicographic order. For the purpose,
01517  * we use a structure.
01518  */
01519 
01520 struct cli_iterator {
01521    struct ast_cli_entry *builtins;
01522    struct ast_cli_entry *helpers;
01523 };
01524 
01525 static struct ast_cli_entry *cli_next(struct cli_iterator *i)
01526 {
01527    struct ast_cli_entry *e;
01528 
01529    if (i->builtins == NULL && i->helpers == NULL) {
01530       /* initialize */
01531       i->builtins = builtins;
01532       i->helpers = AST_LIST_FIRST(&helpers);
01533    }
01534    e = i->builtins; /* temporary */
01535    if (!e->cmda[0] || (i->helpers &&
01536           strcmp(i->helpers->_full_cmd, e->_full_cmd) < 0)) {
01537       /* Use helpers */
01538       e = i->helpers;
01539       if (e)
01540          i->helpers = AST_LIST_NEXT(e, list);
01541    } else { /* use builtin. e is already set  */
01542       (i->builtins)++;  /* move to next */
01543    }
01544    return e;
01545 }
01546 
01547 /*!
01548  * \brief locate a cli command in the 'helpers' list (which must be locked).
01549  * exact has 3 values:
01550  *      0       returns if the search key is equal or longer than the entry.
01551  *      -1      true if the mismatch is on the last word XXX not true!
01552  *      1       true only on complete, exact match.
01553  */
01554 static struct ast_cli_entry *find_cli(char *const cmds[], int match_type)
01555 {
01556    int matchlen = -1;   /* length of longest match so far */
01557    struct ast_cli_entry *cand = NULL, *e=NULL;
01558    struct cli_iterator i = { NULL, NULL};
01559 
01560    while( (e = cli_next(&i)) ) {
01561       int y;
01562       for (y = 0 ; cmds[y] && e->cmda[y]; y++) {
01563          if (strcasecmp(e->cmda[y], cmds[y]))
01564             break;
01565       }
01566       if (e->cmda[y] == NULL) {  /* no more words in candidate */
01567          if (cmds[y] == NULL) /* this is an exact match, cannot do better */
01568             break;
01569          /* here the search key is longer than the candidate */
01570          if (match_type != 0) /* but we look for almost exact match... */
01571             continue;   /* so we skip this one. */
01572          /* otherwise we like it (case 0) */
01573       } else {       /* still words in candidate */
01574          if (cmds[y] == NULL) /* search key is shorter, not good */
01575             continue;
01576          /* if we get here, both words exist but there is a mismatch */
01577          if (match_type == 0) /* not the one we look for */
01578             continue;
01579          if (match_type == 1) /* not the one we look for */
01580             continue;
01581          if (cmds[y+1] != NULL || e->cmda[y+1] != NULL)  /* not the one we look for */
01582             continue;
01583          /* we are in case match_type == -1 and mismatch on last word */
01584       }
01585       if (y > matchlen) {  /* remember the candidate */
01586          matchlen = y;
01587          cand = e;
01588       }
01589    }
01590    return e ? e : cand;
01591 }
01592 
01593 static char *find_best(char *argv[])
01594 {
01595    static char cmdline[80];
01596    int x;
01597    /* See how close we get, then print the candidate */
01598    char *myargv[AST_MAX_CMD_LEN];
01599    for (x=0;x<AST_MAX_CMD_LEN;x++)
01600       myargv[x]=NULL;
01601    AST_LIST_LOCK(&helpers);
01602    for (x=0;argv[x];x++) {
01603       myargv[x] = argv[x];
01604       if (!find_cli(myargv, -1))
01605          break;
01606    }
01607    AST_LIST_UNLOCK(&helpers);
01608    ast_join(cmdline, sizeof(cmdline), myargv);
01609    return cmdline;
01610 }
01611 
01612 static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
01613 {
01614    if (e->deprecate_cmd) {
01615       __ast_cli_unregister(e->deprecate_cmd, e);
01616    }
01617    if (e->inuse) {
01618       ast_log(LOG_WARNING, "Can't remove command that is in use\n");
01619    } else {
01620       AST_LIST_LOCK(&helpers);
01621       AST_LIST_REMOVE(&helpers, e, list);
01622       AST_LIST_UNLOCK(&helpers);
01623       free(e->_full_cmd);
01624    }
01625    return 0;
01626 }
01627 
01628 static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
01629 {
01630    struct ast_cli_entry *cur;
01631    char fulle[80] ="";
01632    int lf, ret = -1;
01633    
01634    ast_join(fulle, sizeof(fulle), e->cmda);
01635    AST_LIST_LOCK(&helpers);
01636    
01637    if (find_cli(e->cmda, 1)) {
01638       ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", fulle);
01639       goto done;
01640    }
01641    e->_full_cmd = ast_strdup(fulle);
01642    if (!e->_full_cmd)
01643       goto done;
01644 
01645    if (ed) {
01646       e->deprecated = 1;
01647       e->summary = ed->summary;
01648       e->usage = ed->usage;
01649       /* XXX If command A deprecates command B, and command B deprecates command C...
01650          Do we want to show command A or command B when telling the user to use new syntax?
01651          This currently would show command A.
01652          To show command B, you just need to always use ed->_full_cmd.
01653        */
01654       e->_deprecated_by = S_OR(ed->_deprecated_by, ed->_full_cmd);
01655    } else {
01656       e->deprecated = 0;
01657    }
01658 
01659    lf = strlen(fulle);
01660    AST_LIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
01661       int len = strlen(cur->_full_cmd);
01662       if (lf < len)
01663          len = lf;
01664       if (strncasecmp(fulle, cur->_full_cmd, len) < 0) {
01665          AST_LIST_INSERT_BEFORE_CURRENT(&helpers, e, list); 
01666          break;
01667       }
01668    }
01669    AST_LIST_TRAVERSE_SAFE_END;
01670 
01671    if (!cur)
01672       AST_LIST_INSERT_TAIL(&helpers, e, list); 
01673    ret = 0; /* success */
01674 
01675 done:
01676    AST_LIST_UNLOCK(&helpers);
01677 
01678    if (e->deprecate_cmd) {
01679       /* This command deprecates another command.  Register that one also. */
01680       __ast_cli_register(e->deprecate_cmd, e);
01681    }
01682    
01683    return ret;
01684 }
01685 
01686 /* wrapper function, so we can unregister deprecated commands recursively */
01687 int ast_cli_unregister(struct ast_cli_entry *e)
01688 {
01689    return __ast_cli_unregister(e, NULL);
01690 }
01691 
01692 /* wrapper function, so we can register deprecated commands recursively */
01693 int ast_cli_register(struct ast_cli_entry *e)
01694 {
01695    return __ast_cli_register(e, NULL);
01696 }
01697 
01698 /*
01699  * register/unregister an array of entries.
01700  */
01701 void ast_cli_register_multiple(struct ast_cli_entry *e, int len)
01702 {
01703    int i;
01704 
01705    for (i = 0; i < len; i++)
01706       ast_cli_register(e + i);
01707 }
01708 
01709 void ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
01710 {
01711    int i;
01712 
01713    for (i = 0; i < len; i++)
01714       ast_cli_unregister(e + i);
01715 }
01716 
01717 
01718 /*! \brief helper for help_workhorse and final part of
01719  * handle_help. if locked = 0 it's just help_workhorse,
01720  * otherwise assume the list is already locked and print
01721  * an error message if not found.
01722  */
01723 static int help1(int fd, char *match[], int locked)
01724 {
01725    char matchstr[80] = "";
01726    struct ast_cli_entry *e;
01727    int len = 0;
01728    int found = 0;
01729    struct cli_iterator i = { NULL, NULL};
01730 
01731    if (match) {
01732       ast_join(matchstr, sizeof(matchstr), match);
01733       len = strlen(matchstr);
01734    }
01735    if (!locked)
01736       AST_LIST_LOCK(&helpers);
01737    while ( (e = cli_next(&i)) ) {
01738       /* Hide commands that start with '_' */
01739       if (e->_full_cmd[0] == '_')
01740          continue;
01741       /* Hide commands that are marked as deprecated. */
01742       if (e->deprecated)
01743          continue;
01744       if (match && strncasecmp(matchstr, e->_full_cmd, len))
01745          continue;
01746       ast_cli(fd, "%25.25s  %s\n", e->_full_cmd, S_OR(e->summary, ""));
01747       found++;
01748    }
01749    AST_LIST_UNLOCK(&helpers);
01750    if (!locked && !found && matchstr[0])
01751       ast_cli(fd, "No such command '%s'.\n", matchstr);
01752    return 0;
01753 }
01754 
01755 static int help_workhorse(int fd, char *match[])
01756 {
01757    return help1(fd, match, 0 /* do not print errors */);
01758 }
01759 
01760 static int handle_help(int fd, int argc, char *argv[])
01761 {
01762    char fullcmd[80];
01763    struct ast_cli_entry *e;
01764 
01765    if (argc < 1)
01766       return RESULT_SHOWUSAGE;
01767    if (argc == 1)
01768       return help_workhorse(fd, NULL);
01769 
01770    AST_LIST_LOCK(&helpers);
01771    e = find_cli(argv + 1, 1); /* try exact match first */
01772    if (!e)
01773       return help1(fd, argv + 1, 1 /* locked */);
01774    if (e->usage)
01775       ast_cli(fd, "%s", e->usage);
01776    else {
01777       ast_join(fullcmd, sizeof(fullcmd), argv+1);
01778       ast_cli(fd, "No help text available for '%s'.\n", fullcmd);
01779    }
01780    AST_LIST_UNLOCK(&helpers);
01781    return RESULT_SUCCESS;
01782 }
01783 
01784 static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
01785 {
01786    char *dup, *cur;
01787    int x = 0;
01788    int quoted = 0;
01789    int escaped = 0;
01790    int whitespace = 1;
01791 
01792    *trailingwhitespace = 0;
01793    if (s == NULL) /* invalid, though! */
01794       return NULL;
01795    /* make a copy to store the parsed string */
01796    if (!(dup = ast_strdup(s)))
01797       return NULL;
01798 
01799    cur = dup;
01800    /* scan the original string copying into cur when needed */
01801    for (; *s ; s++) {
01802       if (x >= max - 1) {
01803          ast_log(LOG_WARNING, "Too many arguments, truncating at %s\n", s);
01804          break;
01805       }
01806       if (*s == '"' && !escaped) {
01807          quoted = !quoted;
01808          if (quoted && whitespace) {
01809             /* start a quoted string from previous whitespace: new argument */
01810             argv[x++] = cur;
01811             whitespace = 0;
01812          }
01813       } else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) {
01814          /* If we are not already in whitespace, and not in a quoted string or
01815             processing an escape sequence, and just entered whitespace, then
01816             finalize the previous argument and remember that we are in whitespace
01817          */
01818          if (!whitespace) {
01819             *cur++ = '\0';
01820             whitespace = 1;
01821          }
01822       } else if (*s == '\\' && !escaped) {
01823          escaped = 1;
01824       } else {
01825          if (whitespace) {
01826             /* we leave whitespace, and are not quoted. So it's a new argument */
01827             argv[x++] = cur;
01828             whitespace = 0;
01829          }
01830          *cur++ = *s;
01831          escaped = 0;
01832       }
01833    }
01834    /* Null terminate */
01835    *cur++ = '\0';
01836    /* XXX put a NULL in the last argument, because some functions that take
01837     * the array may want a null-terminated array.
01838     * argc still reflects the number of non-NULL entries.
01839     */
01840    argv[x] = NULL;
01841    *argc = x;
01842    *trailingwhitespace = whitespace;
01843    return dup;
01844 }
01845 
01846 /*! \brief Return the number of unique matches for the generator */
01847 int ast_cli_generatornummatches(const char *text, const char *word)
01848 {
01849    int matches = 0, i = 0;
01850    char *buf = NULL, *oldbuf = NULL;
01851 
01852    while ((buf = ast_cli_generator(text, word, i++))) {
01853       if (!oldbuf || strcmp(buf,oldbuf))
01854          matches++;
01855       if (oldbuf)
01856          free(oldbuf);
01857       oldbuf = buf;
01858    }
01859    if (oldbuf)
01860       free(oldbuf);
01861    return matches;
01862 }
01863 
01864 char **ast_cli_completion_matches(const char *text, const char *word)
01865 {
01866    char **match_list = NULL, *retstr, *prevstr;
01867    size_t match_list_len, max_equal, which, i;
01868    int matches = 0;
01869 
01870    /* leave entry 0 free for the longest common substring */
01871    match_list_len = 1;
01872    while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
01873       if (matches + 1 >= match_list_len) {
01874          match_list_len <<= 1;
01875          if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
01876             return NULL;
01877       }
01878       match_list[++matches] = retstr;
01879    }
01880 
01881    if (!match_list)
01882       return match_list; /* NULL */
01883 
01884    /* Find the longest substring that is common to all results
01885     * (it is a candidate for completion), and store a copy in entry 0.
01886     */
01887    prevstr = match_list[1];
01888    max_equal = strlen(prevstr);
01889    for (which = 2; which <= matches; which++) {
01890       for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
01891          continue;
01892       max_equal = i;
01893    }
01894 
01895    if (!(retstr = ast_malloc(max_equal + 1)))
01896       return NULL;
01897    
01898    ast_copy_string(retstr, match_list[1], max_equal + 1);
01899    match_list[0] = retstr;
01900 
01901    /* ensure that the array is NULL terminated */
01902    if (matches + 1 >= match_list_len) {
01903       if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list))))
01904          return NULL;
01905    }
01906    match_list[matches + 1] = NULL;
01907 
01908    return match_list;
01909 }
01910 
01911 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
01912 {
01913    char *argv[AST_MAX_ARGS];
01914    struct ast_cli_entry *e;
01915    struct cli_iterator i = { NULL, NULL };
01916    int x = 0, argindex, matchlen;
01917    int matchnum=0;
01918    char *ret = NULL;
01919    char matchstr[80] = "";
01920    int tws = 0;
01921    char *dup = parse_args(text, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws);
01922 
01923    if (!dup)   /* error */
01924       return NULL;
01925    argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
01926    /* rebuild the command, ignore tws */
01927    ast_join(matchstr, sizeof(matchstr)-1, argv);
01928    matchlen = strlen(matchstr);
01929    if (tws) {
01930       strcat(matchstr, " "); /* XXX */
01931       if (matchlen)
01932          matchlen++;
01933    }
01934    if (lock)
01935       AST_LIST_LOCK(&helpers);
01936    while( !ret && (e = cli_next(&i)) ) {
01937       int lc = strlen(e->_full_cmd);
01938       if (e->_full_cmd[0] != '_' && lc > 0 && matchlen <= lc &&
01939             !strncasecmp(matchstr, e->_full_cmd, matchlen)) {
01940          /* Found initial part, return a copy of the next word... */
01941          if (e->cmda[argindex] && ++matchnum > state)
01942             ret = strdup(e->cmda[argindex]); /* we need a malloced string */
01943       } else if (e->generator && !strncasecmp(matchstr, e->_full_cmd, lc) && matchstr[lc] < 33) {
01944          /* We have a command in its entirity within us -- theoretically only one
01945             command can have this occur */
01946          ret = e->generator(matchstr, word, argindex, state);
01947       }
01948    }
01949    if (lock)
01950       AST_LIST_UNLOCK(&helpers);
01951    free(dup);
01952    return ret;
01953 }
01954 
01955 char *ast_cli_generator(const char *text, const char *word, int state)
01956 {
01957    return __ast_cli_generator(text, word, state, 1);
01958 }
01959 
01960 int ast_cli_command(int fd, const char *s)
01961 {
01962    char *argv[AST_MAX_ARGS];
01963    struct ast_cli_entry *e;
01964    int x;
01965    char *dup;
01966    int tws;
01967    
01968    if (!(dup = parse_args(s, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws)))
01969       return -1;
01970 
01971    /* We need at least one entry, or ignore */
01972    if (x > 0) {
01973       AST_LIST_LOCK(&helpers);
01974       e = find_cli(argv, 0);
01975       if (e)
01976          e->inuse++;
01977       AST_LIST_UNLOCK(&helpers);
01978       if (e) {
01979          switch(e->handler(fd, x, argv)) {
01980          case RESULT_SHOWUSAGE:
01981             if (e->usage)
01982                ast_cli(fd, "%s", e->usage);
01983             else
01984                ast_cli(fd, "Invalid usage, but no usage information available.\n");
01985             AST_LIST_LOCK(&helpers);
01986             if (e->deprecated)
01987                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);
01988             AST_LIST_UNLOCK(&helpers);
01989             break;
01990          default:
01991             AST_LIST_LOCK(&helpers);
01992             if (e->deprecated == 1) {
01993                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);
01994                e->deprecated = 2;
01995             }
01996             AST_LIST_UNLOCK(&helpers);
01997             break;
01998          }
01999       } else 
02000          ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(argv));
02001       if (e)
02002          ast_atomic_fetchadd_int(&e->inuse, -1);
02003    }
02004    free(dup);
02005    
02006    return 0;
02007 }

Generated on Fri Aug 24 02:22:15 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1