Fri Sep 29 11:12:25 2006

Asterisk developer's documentation


asterisk.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, 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 
00020 /* Doxygenified Copyright Header */
00021 
00022 /*!
00023  * \mainpage Asterisk -- An Open Source Telephony Toolkit
00024  *
00025  * \par Developer Documentation for Asterisk
00026  * This is the main developer documentation for Asterisk. It is 
00027  * generated by running "make progdocs".
00028  * \par Additional documentation
00029  * \arg \ref DevDoc 
00030  * \arg \ref ConfigFiles
00031  *
00032  * \section copyright Copyright and author
00033  *
00034  * Copyright (C) 1999 - 2005, Digium, Inc.
00035  * Asterisk is a trade mark registered by Digium, Inc.
00036  *
00037  * \author Mark Spencer <markster@digium.com>
00038  * Also see \ref AstCREDITS
00039  *
00040  * \section license License
00041  * See http://www.asterisk.org for more information about
00042  * the Asterisk project. Please do not directly contact
00043  * any of the maintainers of this project for assistance;
00044  * the project provides a web site, mailing lists and IRC
00045  * channels for your use.
00046  *
00047  * This program is free software, distributed under the terms of
00048  * the GNU General Public License Version 2. See the LICENSE file
00049  * at the top of the source tree.
00050  *
00051  * \verbinclude LICENSE
00052  *
00053  */
00054 
00055 /*! \file
00056   \brief Top level source file for Asterisk  - the Open Source PBX. Implementation
00057   of PBX core functions and CLI interface.
00058   
00059  */
00060 
00061 #include <unistd.h>
00062 #include <stdlib.h>
00063 #include <sys/time.h>
00064 #include <fcntl.h>
00065 #include <stdio.h>
00066 #include <signal.h>
00067 #include <sched.h>
00068 #include <sys/socket.h>
00069 #include <sys/un.h>
00070 #include <sys/wait.h>
00071 #include <string.h>
00072 #include <errno.h>
00073 #include <ctype.h>
00074 #include <sys/resource.h>
00075 #include <grp.h>
00076 #include <pwd.h>
00077 #include <sys/stat.h>
00078 #include <regex.h>
00079 
00080 #ifdef linux
00081 #include <sys/prctl.h>
00082 #endif 
00083 
00084 #if  defined(__FreeBSD__) || defined( __NetBSD__ ) || defined(SOLARIS)
00085 #include <netdb.h>
00086 #if defined(SOLARIS)
00087 extern int daemon(int, int);  /* defined in libresolv of all places */
00088 #endif
00089 #endif
00090 
00091 #include "asterisk.h"
00092 
00093 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 40798 $")
00094 
00095 #include "asterisk/logger.h"
00096 #include "asterisk/options.h"
00097 #include "asterisk/cli.h"
00098 #include "asterisk/channel.h"
00099 #include "asterisk/ulaw.h"
00100 #include "asterisk/alaw.h"
00101 #include "asterisk/callerid.h"
00102 #include "asterisk/module.h"
00103 #include "asterisk/image.h"
00104 #include "asterisk/tdd.h"
00105 #include "asterisk/term.h"
00106 #include "asterisk/manager.h"
00107 #include "asterisk/cdr.h"
00108 #include "asterisk/pbx.h"
00109 #include "asterisk/enum.h"
00110 #include "asterisk/rtp.h"
00111 #include "asterisk/app.h"
00112 #include "asterisk/lock.h"
00113 #include "asterisk/utils.h"
00114 #include "asterisk/file.h"
00115 #include "asterisk/io.h"
00116 #include "asterisk/lock.h"
00117 #include "editline/histedit.h"
00118 #include "asterisk/config.h"
00119 #include "asterisk/version.h"
00120 #include "asterisk/linkedlists.h"
00121 #include "asterisk/devicestate.h"
00122 #include "asterisk/compat.h"
00123 
00124 #include "asterisk/doxyref.h"    /* Doxygen documentation */
00125 
00126 #include "defaults.h"
00127 
00128 #ifndef AF_LOCAL
00129 #define AF_LOCAL AF_UNIX
00130 #define PF_LOCAL PF_UNIX
00131 #endif
00132 
00133 #define AST_MAX_CONNECTS 128
00134 #define NUM_MSGS 64
00135 
00136 /*! \brief Welcome message when starting a CLI interface */
00137 #define WELCOME_MESSAGE \
00138    ast_verbose("Asterisk " ASTERISK_VERSION ", Copyright (C) 1999 - 2006 Digium, Inc. and others.\n"); \
00139    ast_verbose("Created by Mark Spencer <markster@digium.com>\n"); \
00140    ast_verbose("Asterisk comes with ABSOLUTELY NO WARRANTY; type 'show warranty' for details.\n"); \
00141    ast_verbose("This is free software, with components licensed under the GNU General Public\n"); \
00142    ast_verbose("License version 2 and other licenses; you are welcome to redistribute it under\n"); \
00143    ast_verbose("certain conditions. Type 'show license' for details.\n"); \
00144    ast_verbose("=========================================================================\n")
00145 
00146 /*! \defgroup main_options 
00147  \brief Main configuration options from \ref Config_ast "asterisk.conf" or 
00148   the operating system command line when starting Asterisk 
00149   Some of them can be changed in the CLI 
00150  */
00151 /*! @{ */
00152 int option_verbose=0;         /*!< Verbosity level */
00153 int option_debug=0;        /*!< Debug level */
00154 int option_exec_includes=0;      /*!< Allow \#exec in config files? */
00155 int option_nofork=0;       /*!< Do not fork */
00156 int option_quiet=0;        /*!< Keep quiet */
00157 int option_console=0;         /*!< Console mode, no background */
00158 int option_daemonize=0;    /*!< Daemonize. Ever if -v or -d */
00159 int option_highpriority=0;    /*!< Run in realtime Linux priority */
00160 int option_remote=0;       /*!< Remote CLI */
00161 int option_exec=0;         /*!< */
00162 int option_initcrypto=0;      /*!< Initialize crypto keys for RSA auth */
00163 int option_nocolor;        /*!< Don't use termcap colors */
00164 int option_dumpcore = 0;         /*!< Dump core when failing */
00165 int option_cache_record_files = 0;     /*!< Cache sound files */
00166 int option_timestamp = 0;        /*!< Timestamp in logging */
00167 int option_overrideconfig = 0;         /*!< */
00168 int option_reconnect = 0;        /*!< */
00169 int option_transcode_slin = 1;         /*!< */
00170 int option_maxcalls = 0;         /*!< */
00171 double option_maxload = 0.0;        /*!< Max load avg on system */
00172 int option_dontwarn = 0;         /*!< */
00173 int option_priority_jumping = 1;    /*!< Enable priority jumping as result value for apps */
00174 int option_transmit_silence_during_record = 0;  /*!< Transmit silence during record() app */
00175 
00176 /*! @} */
00177 
00178 int fully_booted = 0;
00179 char record_cache_dir[AST_CACHE_DIR_LEN] = AST_TMP_DIR;
00180 char debug_filename[AST_FILENAME_MAX] = "";
00181 
00182 static int ast_socket = -1;      /*!< UNIX Socket for allowing remote control */
00183 static int ast_consock = -1;     /*!< UNIX Socket for controlling another asterisk */
00184 int ast_mainpid;
00185 struct console {
00186    int fd;           /*!< File descriptor */
00187    int p[2];         /*!< Pipe */
00188    pthread_t t;         /*!< Thread of handler */
00189 };
00190 
00191 static struct ast_atexit {
00192    void (*func)(void);
00193    struct ast_atexit *next;
00194 } *atexits = NULL;
00195 
00196 AST_MUTEX_DEFINE_STATIC(atexitslock);
00197 
00198 time_t ast_startuptime;
00199 time_t ast_lastreloadtime;
00200 
00201 static History *el_hist = NULL;
00202 static EditLine *el = NULL;
00203 static char *remotehostname;
00204 
00205 struct console consoles[AST_MAX_CONNECTS];
00206 
00207 char defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE;
00208 
00209 static int ast_el_add_history(char *);
00210 static int ast_el_read_history(char *);
00211 static int ast_el_write_history(char *);
00212 
00213 char ast_config_AST_CONFIG_DIR[AST_CONFIG_MAX_PATH];
00214 char ast_config_AST_CONFIG_FILE[AST_CONFIG_MAX_PATH];
00215 char ast_config_AST_MODULE_DIR[AST_CONFIG_MAX_PATH];
00216 char ast_config_AST_SPOOL_DIR[AST_CONFIG_MAX_PATH];
00217 char ast_config_AST_MONITOR_DIR[AST_CONFIG_MAX_PATH];
00218 char ast_config_AST_VAR_DIR[AST_CONFIG_MAX_PATH];
00219 char ast_config_AST_DATA_DIR[AST_CONFIG_MAX_PATH];
00220 char ast_config_AST_LOG_DIR[AST_CONFIG_MAX_PATH];
00221 char ast_config_AST_AGI_DIR[AST_CONFIG_MAX_PATH];
00222 char ast_config_AST_DB[AST_CONFIG_MAX_PATH];
00223 char ast_config_AST_KEY_DIR[AST_CONFIG_MAX_PATH];
00224 char ast_config_AST_PID[AST_CONFIG_MAX_PATH];
00225 char ast_config_AST_SOCKET[AST_CONFIG_MAX_PATH];
00226 char ast_config_AST_RUN_DIR[AST_CONFIG_MAX_PATH];
00227 char ast_config_AST_RUN_USER[AST_CONFIG_MAX_PATH];
00228 char ast_config_AST_RUN_GROUP[AST_CONFIG_MAX_PATH];
00229 char ast_config_AST_CTL_PERMISSIONS[AST_CONFIG_MAX_PATH];
00230 char ast_config_AST_CTL_OWNER[AST_CONFIG_MAX_PATH] = "\0";
00231 char ast_config_AST_CTL_GROUP[AST_CONFIG_MAX_PATH] = "\0";
00232 char ast_config_AST_CTL[AST_CONFIG_MAX_PATH] = "asterisk.ctl";
00233 
00234 static char *_argv[256];
00235 static int shuttingdown = 0;
00236 static int restartnow = 0;
00237 static pthread_t consolethread = AST_PTHREADT_NULL;
00238 
00239 #if !defined(LOW_MEMORY)
00240 struct file_version {
00241    AST_LIST_ENTRY(file_version) list;
00242    const char *file;
00243    char *version;
00244 };
00245 
00246 static AST_LIST_HEAD_STATIC(file_versions, file_version);
00247 
00248 void ast_register_file_version(const char *file, const char *version)
00249 {
00250    struct file_version *new;
00251    char *work;
00252    size_t version_length;
00253 
00254    work = ast_strdupa(version);
00255    work = ast_strip(ast_strip_quoted(work, "$", "$"));
00256    version_length = strlen(work) + 1;
00257 
00258    new = calloc(1, sizeof(*new) + version_length);
00259    if (!new)
00260       return;
00261 
00262    new->file = file;
00263    new->version = (char *) new + sizeof(*new);
00264    memcpy(new->version, work, version_length);
00265    AST_LIST_LOCK(&file_versions);
00266    AST_LIST_INSERT_HEAD(&file_versions, new, list);
00267    AST_LIST_UNLOCK(&file_versions);
00268 }
00269 
00270 void ast_unregister_file_version(const char *file)
00271 {
00272    struct file_version *find;
00273 
00274    AST_LIST_LOCK(&file_versions);
00275    AST_LIST_TRAVERSE_SAFE_BEGIN(&file_versions, find, list) {
00276       if (!strcasecmp(find->file, file)) {
00277          AST_LIST_REMOVE_CURRENT(&file_versions, list);
00278          break;
00279       }
00280    }
00281    AST_LIST_TRAVERSE_SAFE_END;
00282    AST_LIST_UNLOCK(&file_versions);
00283    if (find)
00284       free(find);
00285 }
00286 
00287 static char show_version_files_help[] = 
00288 "Usage: show version files [like <pattern>]\n"
00289 "       Shows the revision numbers of the files used to build this copy of Asterisk.\n"
00290 "       Optional regular expression pattern is used to filter the file list.\n";
00291 
00292 /*! CLI command to list module versions */
00293 static int handle_show_version_files(int fd, int argc, char *argv[])
00294 {
00295 #define FORMAT "%-25.25s %-40.40s\n"
00296    struct file_version *iterator;
00297    regex_t regexbuf;
00298    int havepattern = 0;
00299    int havename = 0;
00300    int count_files = 0;
00301 
00302    switch (argc) {
00303    case 5:
00304       if (!strcasecmp(argv[3], "like")) {
00305          if (regcomp(&regexbuf, argv[4], REG_EXTENDED | REG_NOSUB))
00306             return RESULT_SHOWUSAGE;
00307          havepattern = 1;
00308       } else
00309          return RESULT_SHOWUSAGE;
00310       break;
00311    case 4:
00312       havename = 1;
00313       break;
00314    case 3:
00315       break;
00316    default:
00317       return RESULT_SHOWUSAGE;
00318    }
00319 
00320    ast_cli(fd, FORMAT, "File", "Revision");
00321    ast_cli(fd, FORMAT, "----", "--------");
00322    AST_LIST_LOCK(&file_versions);
00323    AST_LIST_TRAVERSE(&file_versions, iterator, list) {
00324       if (havename && strcasecmp(iterator->file, argv[3]))
00325          continue;
00326 
00327       if (havepattern && regexec(&regexbuf, iterator->file, 0, NULL, 0))
00328          continue;
00329 
00330       ast_cli(fd, FORMAT, iterator->file, iterator->version);
00331       count_files++;
00332       if (havename)
00333          break;
00334    }
00335    AST_LIST_UNLOCK(&file_versions);
00336    if (!havename) {
00337       ast_cli(fd, "%d files listed.\n", count_files);
00338    }
00339 
00340    if (havepattern)
00341       regfree(&regexbuf);
00342 
00343    return RESULT_SUCCESS;
00344 #undef FORMAT
00345 }
00346 
00347 static char *complete_show_version_files(char *line, char *word, int pos, int state)
00348 {
00349    struct file_version *find;
00350    int which = 0;
00351    char *ret = NULL;
00352    int matchlen = strlen(word);
00353 
00354    if (pos != 3)
00355       return NULL;
00356 
00357    AST_LIST_LOCK(&file_versions);
00358    AST_LIST_TRAVERSE(&file_versions, find, list) {
00359       if (!strncasecmp(word, find->file, matchlen)) {
00360          if (++which > state) {
00361             ret = strdup(find->file);
00362             break;
00363          }
00364       }
00365    }
00366    AST_LIST_UNLOCK(&file_versions);
00367 
00368    return ret;
00369 }
00370 #endif /* ! LOW_MEMORY */
00371 
00372 int ast_register_atexit(void (*func)(void))
00373 {
00374    int res = -1;
00375    struct ast_atexit *ae;
00376    ast_unregister_atexit(func);
00377    ae = malloc(sizeof(struct ast_atexit));
00378    ast_mutex_lock(&atexitslock);
00379    if (ae) {
00380       memset(ae, 0, sizeof(struct ast_atexit));
00381       ae->next = atexits;
00382       ae->func = func;
00383       atexits = ae;
00384       res = 0;
00385    }
00386    ast_mutex_unlock(&atexitslock);
00387    return res;
00388 }
00389 
00390 void ast_unregister_atexit(void (*func)(void))
00391 {
00392    struct ast_atexit *ae, *prev = NULL;
00393    ast_mutex_lock(&atexitslock);
00394    ae = atexits;
00395    while(ae) {
00396       if (ae->func == func) {
00397          if (prev)
00398             prev->next = ae->next;
00399          else
00400             atexits = ae->next;
00401          break;
00402       }
00403       prev = ae;
00404       ae = ae->next;
00405    }
00406    ast_mutex_unlock(&atexitslock);
00407 }
00408 
00409 static int fdprint(int fd, const char *s)
00410 {
00411    return write(fd, s, strlen(s) + 1);
00412 }
00413 
00414 /*! NULL handler so we can collect the child exit status */
00415 static void null_sig_handler(int signal)
00416 {
00417 
00418 }
00419 
00420 AST_MUTEX_DEFINE_STATIC(safe_system_lock);
00421 static unsigned int safe_system_level = 0;
00422 static void *safe_system_prev_handler;
00423 
00424 int ast_safe_system(const char *s)
00425 {
00426    pid_t pid;
00427    int x;
00428    int res;
00429    struct rusage rusage;
00430    int status;
00431    unsigned int level;
00432 
00433    /* keep track of how many ast_safe_system() functions
00434       are running at this moment
00435    */
00436    ast_mutex_lock(&safe_system_lock);
00437    level = safe_system_level++;
00438 
00439    /* only replace the handler if it has not already been done */
00440    if (level == 0)
00441       safe_system_prev_handler = signal(SIGCHLD, null_sig_handler);
00442 
00443    ast_mutex_unlock(&safe_system_lock);
00444 
00445    pid = fork();
00446 
00447    if (pid == 0) {
00448       if (option_highpriority)
00449          ast_set_priority(0);
00450       /* Close file descriptors and launch system command */
00451       for (x = STDERR_FILENO + 1; x < 4096; x++)
00452          close(x);
00453       execl("/bin/sh", "/bin/sh", "-c", s, NULL);
00454       exit(1);
00455    } else if (pid > 0) {
00456       for(;;) {
00457          res = wait4(pid, &status, 0, &rusage);
00458          if (res > -1) {
00459             res = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
00460             break;
00461          } else if (errno != EINTR) 
00462             break;
00463       }
00464    } else {
00465       ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
00466       res = -1;
00467    }
00468 
00469    ast_mutex_lock(&safe_system_lock);
00470    level = --safe_system_level;
00471 
00472    /* only restore the handler if we are the last one */
00473    if (level == 0)
00474       signal(SIGCHLD, safe_system_prev_handler);
00475 
00476    ast_mutex_unlock(&safe_system_lock);
00477 
00478    return res;
00479 }
00480 
00481 /*!
00482  * write the string to all attached console clients
00483  */
00484 static void ast_network_puts(const char *string)
00485 {
00486    int x;
00487    for (x=0;x<AST_MAX_CONNECTS; x++) {
00488       if (consoles[x].fd > -1) 
00489          fdprint(consoles[x].p[1], string);
00490    }
00491 }
00492 
00493 /*!
00494  * write the string to the console, and all attached
00495  * console clients
00496  */
00497 void ast_console_puts(const char *string)
00498 {
00499    fputs(string, stdout);
00500    fflush(stdout);
00501    ast_network_puts(string);
00502 }
00503 
00504 static void network_verboser(const char *s, int pos, int replace, int complete)
00505    /* ARGUSED */
00506 {
00507    if (replace) {
00508       char *t = alloca(strlen(s) + 2);
00509       if (t) {
00510          sprintf(t, "\r%s", s);
00511          if (complete)
00512             ast_network_puts(t);
00513       } else {
00514          ast_log(LOG_ERROR, "Out of memory\n");
00515          ast_network_puts(s);
00516       }
00517    } else {
00518       if (complete)
00519          ast_network_puts(s);
00520    }
00521 }
00522 
00523 static pthread_t lthread;
00524 
00525 static void *netconsole(void *vconsole)
00526 {
00527    struct console *con = vconsole;
00528    char hostname[MAXHOSTNAMELEN]="";
00529    char tmp[512];
00530    int res;
00531    struct pollfd fds[2];
00532    
00533    if (gethostname(hostname, sizeof(hostname)-1))
00534       ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
00535    snprintf(tmp, sizeof(tmp), "%s/%d/%s\n", hostname, ast_mainpid, ASTERISK_VERSION);
00536    fdprint(con->fd, tmp);
00537    for(;;) {
00538       fds[0].fd = con->fd;
00539       fds[0].events = POLLIN;
00540       fds[0].revents = 0;
00541       fds[1].fd = con->p[0];
00542       fds[1].events = POLLIN;
00543       fds[1].revents = 0;
00544 
00545       res = poll(fds, 2, -1);
00546       if (res < 0) {
00547          if (errno != EINTR)
00548             ast_log(LOG_WARNING, "poll returned < 0: %s\n", strerror(errno));
00549          continue;
00550       }
00551       if (fds[0].revents) {
00552          res = read(con->fd, tmp, sizeof(tmp));
00553          if (res < 1) {
00554             break;
00555          }
00556          tmp[res] = 0;
00557          ast_cli_command(con->fd, tmp);
00558       }
00559       if (fds[1].revents) {
00560          res = read(con->p[0], tmp, sizeof(tmp));
00561          if (res < 1) {
00562             ast_log(LOG_ERROR, "read returned %d\n", res);
00563             break;
00564          }
00565          res = write(con->fd, tmp, res);
00566          if (res < 1)
00567             break;
00568       }
00569    }
00570    if (option_verbose > 2) 
00571       ast_verbose(VERBOSE_PREFIX_3 "Remote UNIX connection disconnected\n");
00572    close(con->fd);
00573    close(con->p[0]);
00574    close(con->p[1]);
00575    con->fd = -1;
00576    
00577    return NULL;
00578 }
00579 
00580 static void *listener(void *unused)
00581 {
00582    struct sockaddr_un sunaddr;
00583    int s;
00584    socklen_t len;
00585    int x;
00586    int flags;
00587    struct pollfd fds[1];
00588    pthread_attr_t attr;
00589    pthread_attr_init(&attr);
00590    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00591    for(;;) {
00592       if (ast_socket < 0)
00593          return NULL;
00594       fds[0].fd = ast_socket;
00595       fds[0].events= POLLIN;
00596       s = poll(fds, 1, -1);
00597       pthread_testcancel();
00598       if (s < 0) {
00599          if (errno != EINTR)
00600             ast_log(LOG_WARNING, "poll returned error: %s\n", strerror(errno));
00601          continue;
00602       }
00603       len = sizeof(sunaddr);
00604       s = accept(ast_socket, (struct sockaddr *)&sunaddr, &len);
00605       if (s < 0) {
00606          if (errno != EINTR)
00607             ast_log(LOG_WARNING, "Accept returned %d: %s\n", s, strerror(errno));
00608       } else {
00609          for (x=0;x<AST_MAX_CONNECTS;x++) {
00610             if (consoles[x].fd < 0) {
00611                if (socketpair(AF_LOCAL, SOCK_STREAM, 0, consoles[x].p)) {
00612                   ast_log(LOG_ERROR, "Unable to create pipe: %s\n", strerror(errno));
00613                   consoles[x].fd = -1;
00614                   fdprint(s, "Server failed to create pipe\n");
00615                   close(s);
00616                   break;
00617                }
00618                flags = fcntl(consoles[x].p[1], F_GETFL);
00619                fcntl(consoles[x].p[1], F_SETFL, flags | O_NONBLOCK);
00620                consoles[x].fd = s;
00621                if (ast_pthread_create(&consoles[x].t, &attr, netconsole, &consoles[x])) {
00622                   ast_log(LOG_ERROR, "Unable to spawn thread to handle connection: %s\n", strerror(errno));
00623                   close(consoles[x].p[0]);
00624                   close(consoles[x].p[1]);
00625                   consoles[x].fd = -1;
00626                   fdprint(s, "Server failed to spawn thread\n");
00627                   close(s);
00628                }
00629                break;
00630             }
00631          }
00632          if (x >= AST_MAX_CONNECTS) {
00633             fdprint(s, "No more connections allowed\n");
00634             ast_log(LOG_WARNING, "No more connections allowed\n");
00635             close(s);
00636          } else if (consoles[x].fd > -1) {
00637             if (option_verbose > 2) 
00638                ast_verbose(VERBOSE_PREFIX_3 "Remote UNIX connection\n");
00639          }
00640       }
00641    }
00642    return NULL;
00643 }
00644 
00645 static int ast_makesocket(void)
00646 {
00647    struct sockaddr_un sunaddr;
00648    int res;
00649    int x;
00650    uid_t uid = -1;
00651    gid_t gid = -1;
00652 
00653    for (x = 0; x < AST_MAX_CONNECTS; x++) 
00654       consoles[x].fd = -1;
00655    unlink(ast_config_AST_SOCKET);
00656    ast_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
00657    if (ast_socket < 0) {
00658       ast_log(LOG_WARNING, "Unable to create control socket: %s\n", strerror(errno));
00659       return -1;
00660    }     
00661    memset(&sunaddr, 0, sizeof(sunaddr));
00662    sunaddr.sun_family = AF_LOCAL;
00663    ast_copy_string(sunaddr.sun_path, ast_config_AST_SOCKET, sizeof(sunaddr.sun_path));
00664    res = bind(ast_socket, (struct sockaddr *)&sunaddr, sizeof(sunaddr));
00665    if (res) {
00666       ast_log(LOG_WARNING, "Unable to bind socket to %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
00667       close(ast_socket);
00668       ast_socket = -1;
00669       return -1;
00670    }
00671    res = listen(ast_socket, 2);
00672    if (res < 0) {
00673       ast_log(LOG_WARNING, "Unable to listen on socket %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
00674       close(ast_socket);
00675       ast_socket = -1;
00676       return -1;
00677    }
00678    ast_register_verbose(network_verboser);
00679    ast_pthread_create(&lthread, NULL, listener, NULL);
00680 
00681    if (!ast_strlen_zero(ast_config_AST_CTL_OWNER)) {
00682       struct passwd *pw;
00683       if ((pw = getpwnam(ast_config_AST_CTL_OWNER)) == NULL) {
00684          ast_log(LOG_WARNING, "Unable to find uid of user %s\n", ast_config_AST_CTL_OWNER);
00685       } else {
00686          uid = pw->pw_uid;
00687       }
00688    }
00689       
00690    if (!ast_strlen_zero(ast_config_AST_CTL_GROUP)) {
00691       struct group *grp;
00692       if ((grp = getgrnam(ast_config_AST_CTL_GROUP)) == NULL) {
00693          ast_log(LOG_WARNING, "Unable to find gid of group %s\n", ast_config_AST_CTL_GROUP);
00694       } else {
00695          gid = grp->gr_gid;
00696       }
00697    }
00698 
00699    if (chown(ast_config_AST_SOCKET, uid, gid) < 0)
00700       ast_log(LOG_WARNING, "Unable to change ownership of %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
00701 
00702    if (!ast_strlen_zero(ast_config_AST_CTL_PERMISSIONS)) {
00703       int p1;
00704       mode_t p;
00705       sscanf(ast_config_AST_CTL_PERMISSIONS, "%o", &p1);
00706       p = p1;
00707       if ((chmod(ast_config_AST_SOCKET, p)) < 0)
00708          ast_log(LOG_WARNING, "Unable to change file permissions of %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
00709    }
00710 
00711    return 0;
00712 }
00713 
00714 static int ast_tryconnect(void)
00715 {
00716    struct sockaddr_un sunaddr;
00717    int res;
00718    ast_consock = socket(PF_LOCAL, SOCK_STREAM, 0);
00719    if (ast_consock < 0) {
00720       ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
00721       return 0;
00722    }
00723    memset(&sunaddr, 0, sizeof(sunaddr));
00724    sunaddr.sun_family = AF_LOCAL;
00725    ast_copy_string(sunaddr.sun_path, (char *)ast_config_AST_SOCKET, sizeof(sunaddr.sun_path));
00726    res = connect(ast_consock, (struct sockaddr *)&sunaddr, sizeof(sunaddr));
00727    if (res) {
00728       close(ast_consock);
00729       ast_consock = -1;
00730       return 0;
00731    } else
00732       return 1;
00733 }
00734 
00735 /*! Urgent handler
00736  Called by soft_hangup to interrupt the poll, read, or other
00737  system call.  We don't actually need to do anything though.  
00738  Remember: Cannot EVER ast_log from within a signal handler 
00739  */
00740 static void urg_handler(int num)
00741 {
00742    signal(num, urg_handler);
00743    return;
00744 }
00745 
00746 static void hup_handler(int num)
00747 {
00748    if (option_verbose > 1) 
00749       printf("Received HUP signal -- Reloading configs\n");
00750    if (restartnow)
00751       execvp(_argv[0], _argv);
00752    /* XXX This could deadlock XXX */
00753    ast_module_reload(NULL);
00754    signal(num, hup_handler);
00755 }
00756 
00757 static void child_handler(int sig)
00758 {
00759    /* Must not ever ast_log or ast_verbose within signal handler */
00760    int n, status;
00761 
00762    /*
00763     * Reap all dead children -- not just one
00764     */
00765    for (n = 0; wait4(-1, &status, WNOHANG, NULL) > 0; n++)
00766       ;
00767    if (n == 0 && option_debug)   
00768       printf("Huh?  Child handler, but nobody there?\n");
00769    signal(sig, child_handler);
00770 }
00771 
00772 /*! Set an X-term or screen title */
00773 static void set_title(char *text)
00774 {
00775    if (getenv("TERM") && strstr(getenv("TERM"), "xterm"))
00776       fprintf(stdout, "\033]2;%s\007", text);
00777 }
00778 
00779 static void set_icon(char *text)
00780 {
00781    if (getenv("TERM") && strstr(getenv("TERM"), "xterm"))
00782       fprintf(stdout, "\033]1;%s\007", text);
00783 }
00784 
00785 /*! We set ourselves to a high priority, that we might pre-empt everything
00786    else.  If your PBX has heavy activity on it, this is a good thing.  */
00787 int ast_set_priority(int pri)
00788 {
00789    struct sched_param sched;
00790    memset(&sched, 0, sizeof(sched));
00791 #ifdef __linux__
00792    if (pri) {  
00793       sched.sched_priority = 10;
00794       if (sched_setscheduler(0, SCHED_RR, &sched)) {
00795          ast_log(LOG_WARNING, "Unable to set high priority\n");
00796          return -1;
00797       } else
00798          if (option_verbose)
00799             ast_verbose("Set to realtime thread\n");
00800    } else {
00801       sched.sched_priority = 0;
00802       if (sched_setscheduler(0, SCHED_OTHER, &sched)) {
00803          ast_log(LOG_WARNING, "Unable to set normal priority\n");
00804          return -1;
00805       }
00806    }
00807 #else
00808    if (pri) {
00809       if (setpriority(PRIO_PROCESS, 0, -10) == -1) {
00810          ast_log(LOG_WARNING, "Unable to set high priority\n");
00811          return -1;
00812       } else
00813          if (option_verbose)
00814             ast_verbose("Set to high priority\n");
00815    } else {
00816       if (setpriority(PRIO_PROCESS, 0, 0) == -1) {
00817          ast_log(LOG_WARNING, "Unable to set normal priority\n");
00818          return -1;
00819       }
00820    }
00821 #endif
00822    return 0;
00823 }
00824 
00825 static void ast_run_atexits(void)
00826 {
00827    struct ast_atexit *ae;
00828    ast_mutex_lock(&atexitslock);
00829    ae = atexits;
00830    while(ae) {
00831       if (ae->func) 
00832          ae->func();
00833       ae = ae->next;
00834    }
00835    ast_mutex_unlock(&atexitslock);
00836 }
00837 
00838 static void quit_handler(int num, int nice, int safeshutdown, int restart)
00839 {
00840    char filename[80] = "";
00841    time_t s,e;
00842    int x;
00843    /* Try to get as many CDRs as possible submitted to the backend engines (if in batch mode) */
00844    ast_cdr_engine_term();
00845    if (safeshutdown) {
00846       shuttingdown = 1;
00847       if (!nice) {
00848          /* Begin shutdown routine, hanging up active channels */
00849          ast_begin_shutdown(1);
00850          if (option_verbose && option_console)
00851             ast_verbose("Beginning asterisk %s....\n", restart ? "restart" : "shutdown");
00852          time(&s);
00853          for(;;) {
00854             time(&e);
00855             /* Wait up to 15 seconds for all channels to go away */
00856             if ((e - s) > 15)
00857                break;
00858             if (!ast_active_channels())
00859                break;
00860             if (!shuttingdown)
00861                break;
00862             /* Sleep 1/10 of a second */
00863             usleep(100000);
00864          }
00865       } else {
00866          if (nice < 2)
00867             ast_begin_shutdown(0);
00868          if (option_verbose && option_console)
00869             ast_verbose("Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt");
00870          for(;;) {
00871             if (!ast_active_channels())
00872                break;
00873             if (!shuttingdown)
00874                break;
00875             sleep(1);
00876          }
00877       }
00878 
00879       if (!shuttingdown) {
00880          if (option_verbose && option_console)
00881             ast_verbose("Asterisk %s cancelled.\n", restart ? "restart" : "shutdown");
00882          return;
00883       }
00884    }
00885    if (option_console || option_remote) {
00886       if (getenv("HOME")) 
00887          snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
00888       if (!ast_strlen_zero(filename))
00889          ast_el_write_history(filename);
00890       if (el != NULL)
00891          el_end(el);
00892       if (el_hist != NULL)
00893          history_end(el_hist);
00894    }
00895    if (option_verbose)
00896       ast_verbose("Executing last minute cleanups\n");
00897    ast_run_atexits();
00898    /* Called on exit */
00899    if (option_verbose && option_console)
00900       ast_verbose("Asterisk %s ending (%d).\n", ast_active_channels() ? "uncleanly" : "cleanly", num);
00901    else if (option_debug)
00902       ast_log(LOG_DEBUG, "Asterisk ending (%d).\n", num);
00903    manager_event(EVENT_FLAG_SYSTEM, "Shutdown", "Shutdown: %s\r\nRestart: %s\r\n", ast_active_channels() ? "Uncleanly" : "Cleanly", restart ? "True" : "False");
00904    if (ast_socket > -1) {
00905       pthread_cancel(lthread);
00906       close(ast_socket);
00907       ast_socket = -1;
00908       unlink(ast_config_AST_SOCKET);
00909    }
00910    if (ast_consock > -1)
00911       close(ast_consock);
00912    if (!option_remote) unlink((char *)ast_config_AST_PID);
00913    printf(term_quit());
00914    if (restart) {
00915       if (option_verbose || option_console)
00916          ast_verbose("Preparing for Asterisk restart...\n");
00917       /* Mark all FD's for closing on exec */
00918       for (x=3;x<32768;x++) {
00919          fcntl(x, F_SETFD, FD_CLOEXEC);
00920       }
00921       if (option_verbose || option_console)
00922          ast_verbose("Restarting Asterisk NOW...\n");
00923       restartnow = 1;
00924 
00925       /* close logger */
00926       close_logger();
00927 
00928       /* If there is a consolethread running send it a SIGHUP 
00929          so it can execvp, otherwise we can do it ourselves */
00930       if ((consolethread != AST_PTHREADT_NULL) && (consolethread != pthread_self())) {
00931          pthread_kill(consolethread, SIGHUP);
00932          /* Give the signal handler some time to complete */
00933          sleep(2);
00934       } else
00935          execvp(_argv[0], _argv);
00936    
00937    } else {
00938       /* close logger */
00939       close_logger();
00940    }
00941    exit(0);
00942 }
00943 
00944 static void __quit_handler(int num)
00945 {
00946    quit_handler(num, 0, 1, 0);
00947 }
00948 
00949 static const char *fix_header(char *outbuf, int maxout, const char *s, char *cmp)
00950 {
00951    const char *c;
00952    if (!strncmp(s, cmp, strlen(cmp))) {
00953       c = s + strlen(cmp);
00954       term_color(outbuf, cmp, COLOR_GRAY, 0, maxout);
00955       return c;
00956    }
00957    return NULL;
00958 }
00959 
00960 static void console_verboser(const char *s, int pos, int replace, int complete)
00961 {
00962    char tmp[80];
00963    const char *c=NULL;
00964    /* Return to the beginning of the line */
00965    if (!pos) {
00966       fprintf(stdout, "\r");
00967       if ((c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_4)) ||
00968          (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_3)) ||
00969          (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_2)) ||
00970          (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_1)))
00971          fputs(tmp, stdout);
00972    }
00973    if (c)
00974       fputs(c + pos,stdout);
00975    else
00976       fputs(s + pos,stdout);
00977    fflush(stdout);
00978    if (complete) {
00979       /* Wake up a poll()ing console */
00980       if (option_console && consolethread != AST_PTHREADT_NULL)
00981          pthread_kill(consolethread, SIGURG);
00982    }
00983 }
00984 
00985 static int ast_all_zeros(char *s)
00986 {
00987    while(*s) {
00988       if (*s > 32)
00989          return 0;
00990       s++;  
00991    }
00992    return 1;
00993 }
00994 
00995 static void consolehandler(char *s)
00996 {
00997    printf(term_end());
00998    fflush(stdout);
00999 
01000    /* Called when readline data is available */
01001    if (!ast_all_zeros(s))
01002       ast_el_add_history(s);
01003    /* The real handler for bang */
01004    if (s[0] == '!') {
01005       if (s[1])
01006          ast_safe_system(s+1);
01007       else
01008          ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
01009    } else 
01010       ast_cli_command(STDOUT_FILENO, s);
01011 }
01012 
01013 static int remoteconsolehandler(char *s)
01014 {
01015    int ret = 0;
01016 
01017    /* Called when readline data is available */
01018    if (!ast_all_zeros(s))
01019       ast_el_add_history(s);
01020    /* The real handler for bang */
01021    if (s[0] == '!') {
01022       if (s[1])
01023          ast_safe_system(s+1);
01024       else
01025          ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
01026       ret = 1;
01027    }
01028    if ((strncasecmp(s, "quit", 4) == 0 || strncasecmp(s, "exit", 4) == 0) &&
01029        (s[4] == '\0' || isspace(s[4]))) {
01030       quit_handler(0, 0, 0, 0);
01031       ret = 1;
01032    }
01033 
01034    return ret;
01035 }
01036 
01037 static char abort_halt_help[] = 
01038 "Usage: abort shutdown\n"
01039 "       Causes Asterisk to abort an executing shutdown or restart, and resume normal\n"
01040 "       call operations.\n";
01041 
01042 static char shutdown_now_help[] = 
01043 "Usage: stop now\n"
01044 "       Shuts down a running Asterisk immediately, hanging up all active calls .\n";
01045 
01046 static char shutdown_gracefully_help[] = 
01047 "Usage: stop gracefully\n"
01048 "       Causes Asterisk to not accept new calls, and exit when all\n"
01049 "       active calls have terminated normally.\n";
01050 
01051 static char shutdown_when_convenient_help[] = 
01052 "Usage: stop when convenient\n"
01053 "       Causes Asterisk to perform a shutdown when all active calls have ended.\n";
01054 
01055 static char restart_now_help[] = 
01056 "Usage: restart now\n"
01057 "       Causes Asterisk to hangup all calls and exec() itself performing a cold\n"
01058 "       restart.\n";
01059 
01060 static char restart_gracefully_help[] = 
01061 "Usage: restart gracefully\n"
01062 "       Causes Asterisk to stop accepting new calls and exec() itself performing a cold\n"
01063 "       restart when all active calls have ended.\n";
01064 
01065 static char restart_when_convenient_help[] = 
01066 "Usage: restart when convenient\n"
01067 "       Causes Asterisk to perform a cold restart when all active calls have ended.\n";
01068 
01069 static char bang_help[] =
01070 "Usage: !<command>\n"
01071 "       Executes a given shell command\n";
01072 
01073 static char show_warranty_help[] =
01074 "Usage: show warranty\n"
01075 "  Shows the warranty (if any) for this copy of Asterisk.\n";
01076 
01077 static char show_license_help[] =
01078 "Usage: show license\n"
01079 "  Shows the license(s) for this copy of Asterisk.\n";
01080 
01081 #if 0
01082 static int handle_quit(int fd, int argc, char *argv[])
01083 {
01084    if (argc != 1)
01085       return RESULT_SHOWUSAGE;
01086    quit_handler(0, 0, 1, 0);
01087    return RESULT_SUCCESS;
01088 }
01089 #endif
01090 
01091 static int handle_shutdown_now(int fd, int argc, char *argv[])
01092 {
01093    if (argc != 2)
01094       return RESULT_SHOWUSAGE;
01095    quit_handler(0, 0 /* Not nice */, 1 /* safely */, 0 /* not restart */);
01096    return RESULT_SUCCESS;
01097 }
01098 
01099 static int handle_shutdown_gracefully(int fd, int argc, char *argv[])
01100 {
01101    if (argc != 2)
01102       return RESULT_SHOWUSAGE;
01103    quit_handler(0, 1 /* nicely */, 1 /* safely */, 0 /* no restart */);
01104    return RESULT_SUCCESS;
01105 }
01106 
01107 static int handle_shutdown_when_convenient(int fd, int argc, char *argv[])
01108 {
01109    if (argc != 3)
01110       return RESULT_SHOWUSAGE;
01111    quit_handler(0, 2 /* really nicely */, 1 /* safely */, 0 /* don't restart */);
01112    return RESULT_SUCCESS;
01113 }
01114 
01115 static int handle_restart_now(int fd, int argc, char *argv[])
01116 {
01117    if (argc != 2)
01118       return RESULT_SHOWUSAGE;
01119    quit_handler(0, 0 /* not nicely */, 1 /* safely */, 1 /* restart */);
01120    return RESULT_SUCCESS;
01121 }
01122 
01123 static int handle_restart_gracefully(int fd, int argc, char *argv[])
01124 {
01125    if (argc != 2)
01126       return RESULT_SHOWUSAGE;
01127    quit_handler(0, 1 /* nicely */, 1 /* safely */, 1 /* restart */);
01128    return RESULT_SUCCESS;
01129 }
01130 
01131 static int handle_restart_when_convenient(int fd, int argc, char *argv[])
01132 {
01133    if (argc != 3)
01134       return RESULT_SHOWUSAGE;
01135    quit_handler(0, 2 /* really nicely */, 1 /* safely */, 1 /* restart */);
01136    return RESULT_SUCCESS;
01137 }
01138 
01139 static int handle_abort_halt(int fd, int argc, char *argv[])
01140 {
01141    if (argc != 2)
01142       return RESULT_SHOWUSAGE;
01143    ast_cancel_shutdown();
01144    shuttingdown = 0;
01145    return RESULT_SUCCESS;
01146 }
01147 
01148 static int handle_bang(int fd, int argc, char *argv[])
01149 {
01150    return RESULT_SUCCESS;
01151 }
01152 static const char *warranty_lines[] = {
01153    "\n",
01154    "            NO WARRANTY\n",
01155    "\n",
01156    "BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n",
01157    "FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\n",
01158    "OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n",
01159    "PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n",
01160    "OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n",
01161    "MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\n",
01162    "TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\n",
01163    "PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n",
01164    "REPAIR OR CORRECTION.\n",
01165    "\n",
01166    "IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n",
01167    "WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n",
01168    "REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n",
01169    "INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n",
01170    "OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n",
01171    "TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n",
01172    "YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n",
01173    "PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n",
01174    "POSSIBILITY OF SUCH DAMAGES.\n",
01175 };
01176 
01177 static int show_warranty(int fd, int argc, char *argv[])
01178 {
01179    int x;
01180 
01181    for (x = 0; x < sizeof(warranty_lines) / sizeof(warranty_lines[0]); x++)
01182       ast_cli(fd, (char *) warranty_lines[x]);
01183 
01184    return RESULT_SUCCESS;
01185 }
01186 
01187 static const char *license_lines[] = {
01188    "\n",
01189    "This program is free software; you can redistribute it and/or modify\n",
01190    "it under the terms of the GNU General Public License version 2 as\n",
01191    "published by the Free Software Foundation.\n",
01192    "\n",
01193    "This program also contains components licensed under other licenses.\n",
01194    "They include:\n",
01195    "\n",
01196    "This program is distributed in the hope that it will be useful,\n",
01197    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n",
01198    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n",
01199    "GNU General Public License for more details.\n",
01200    "\n",
01201    "You should have received a copy of the GNU General Public License\n",
01202    "along with this program; if not, write to the Free Software\n",
01203    "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n",
01204 };
01205 
01206 static int show_license(int fd, int argc, char *argv[])
01207 {
01208    int x;
01209 
01210    for (x = 0; x < sizeof(license_lines) / sizeof(license_lines[0]); x++)
01211       ast_cli(fd, (char *) license_lines[x]);
01212 
01213    return RESULT_SUCCESS;
01214 }
01215 
01216 #define ASTERISK_PROMPT "*CLI> "
01217 
01218 #define ASTERISK_PROMPT2 "%s*CLI> "
01219 
01220 static struct ast_cli_entry core_cli[] = {
01221    { { "abort", "halt", NULL }, handle_abort_halt,
01222      "Cancel a running halt", abort_halt_help },
01223    { { "stop", "now", NULL }, handle_shutdown_now,
01224      "Shut down Asterisk immediately", shutdown_now_help },
01225    { { "stop", "gracefully", NULL }, handle_shutdown_gracefully,
01226      "Gracefully shut down Asterisk", shutdown_gracefully_help },
01227    { { "stop", "when","convenient", NULL }, handle_shutdown_when_convenient,
01228      "Shut down Asterisk at empty call volume", shutdown_when_convenient_help },
01229    { { "restart", "now", NULL }, handle_restart_now,
01230      "Restart Asterisk immediately", restart_now_help },
01231    { { "restart", "gracefully", NULL }, handle_restart_gracefully,
01232      "Restart Asterisk gracefully", restart_gracefully_help },
01233    { { "restart", "when", "convenient", NULL }, handle_restart_when_convenient,
01234      "Restart Asterisk at empty call volume", restart_when_convenient_help },
01235    { { "show", "warranty", NULL }, show_warranty,
01236      "Show the warranty (if any) for this copy of Asterisk", show_warranty_help },
01237    { { "show", "license", NULL }, show_license,
01238      "Show the license(s) for this copy of Asterisk", show_license_help },
01239    { { "!", NULL }, handle_bang,
01240      "Execute a shell command", bang_help },
01241 #if !defined(LOW_MEMORY)
01242    { { "show", "version", "files", NULL }, handle_show_version_files,
01243      "Show versions of files used to build Asterisk", show_version_files_help, complete_show_version_files },
01244 #endif /* ! LOW_MEMORY */
01245 };
01246 
01247 static int ast_el_read_char(EditLine *el, char *cp)
01248 {
01249    int num_read=0;
01250    int lastpos=0;
01251    struct pollfd fds[2];
01252    int res;
01253    int max;
01254    char buf[512];
01255 
01256    for (;;) {
01257       max = 1;
01258       fds[0].fd = ast_consock;
01259       fds[0].events = POLLIN;
01260       if (!option_exec) {
01261          fds[1].fd = STDIN_FILENO;
01262          fds[1].events = POLLIN;
01263          max++;
01264       }
01265       res = poll(fds, max, -1);
01266       if (res < 0) {
01267          if (errno == EINTR)
01268             continue;
01269          ast_log(LOG_ERROR, "poll failed: %s\n", strerror(errno));
01270          break;
01271       }
01272 
01273       if (!option_exec && fds[1].revents) {
01274          num_read = read(STDIN_FILENO, cp, 1);
01275          if (num_read < 1) {
01276             break;
01277          } else 
01278             return (num_read);
01279       }
01280       if (fds[0].revents) {
01281          res = read(ast_consock, buf, sizeof(buf) - 1);
01282          /* if the remote side disappears exit */
01283          if (res < 1) {
01284             fprintf(stderr, "\nDisconnected from Asterisk server\n");
01285             if (!option_reconnect) {
01286                quit_handler(0, 0, 0, 0);
01287             } else {
01288                int tries;
01289                int reconnects_per_second = 20;
01290                fprintf(stderr, "Attempting to reconnect for 30 seconds\n");
01291                for (tries=0;tries<30 * reconnects_per_second;tries++) {
01292                   if (ast_tryconnect()) {
01293                      fprintf(stderr, "Reconnect succeeded after %.3f seconds\n", 1.0 / reconnects_per_second * tries);
01294                      printf(term_quit());
01295                      WELCOME_MESSAGE;
01296                      break;
01297                   } else {
01298                      usleep(1000000 / reconnects_per_second);
01299                   }
01300                }
01301                if (tries >= 30 * reconnects_per_second) {
01302                   fprintf(stderr, "Failed to reconnect for 30 seconds.  Quitting.\n");
01303                   quit_handler(0, 0, 0, 0);
01304                }
01305             }
01306          }
01307 
01308          buf[res] = '\0';
01309 
01310          if (!option_exec && !lastpos)
01311             write(STDOUT_FILENO, "\r", 1);
01312          write(STDOUT_FILENO, buf, res);
01313          if ((buf[res-1] == '\n') || (buf[res-2] == '\n')) {
01314             *cp = CC_REFRESH;
01315             return(1);
01316          } else {
01317             lastpos = 1;
01318          }
01319       }
01320    }
01321 
01322    *cp = '\0';
01323    return (0);
01324 }
01325 
01326 static char *cli_prompt(EditLine *el)
01327 {
01328    static char prompt[200];
01329    char *pfmt;
01330    int color_used=0;
01331    char term_code[20];
01332 
01333    if ((pfmt = getenv("ASTERISK_PROMPT"))) {
01334       char *t = pfmt, *p = prompt;
01335       memset(prompt, 0, sizeof(prompt));
01336       while (*t != '\0' && *p < sizeof(prompt)) {
01337          if (*t == '%') {
01338             char hostname[MAXHOSTNAMELEN]="";
01339             int i;
01340             time_t ts;
01341             struct tm tm;
01342 #ifdef linux
01343             FILE *LOADAVG;
01344 #endif
01345             int fgcolor = COLOR_WHITE, bgcolor = COLOR_BLACK;
01346 
01347             t++;
01348             switch (*t) {
01349                case 'C': /* color */
01350                   t++;
01351                   if (sscanf(t, "%d;%d%n", &fgcolor, &bgcolor, &i) == 2) {
01352                      strncat(p, term_color_code(term_code, fgcolor, bgcolor, sizeof(term_code)),sizeof(prompt) - strlen(prompt) - 1);
01353                      t += i - 1;
01354                   } else if (sscanf(t, "%d%n", &fgcolor, &i) == 1) {
01355                      strncat(p, term_color_code(term_code, fgcolor, 0, sizeof(term_code)),sizeof(prompt) - strlen(prompt) - 1);
01356                      t += i - 1;
01357                   }
01358 
01359                   /* If the color has been reset correctly, then there's no need to reset it later */
01360                   if ((fgcolor == COLOR_WHITE) && (bgcolor == COLOR_BLACK)) {
01361                      color_used = 0;
01362                   } else {
01363                      color_used = 1;
01364                   }
01365                   break;
01366                case 'd': /* date */
01367                   memset(&tm, 0, sizeof(struct tm));
01368                   time(&ts);
01369                   if (localtime_r(&ts, &tm)) {
01370                      strftime(p, sizeof(prompt) - strlen(prompt), "%Y-%m-%d", &tm);
01371                   }
01372                   break;
01373                case 'h': /* hostname */
01374                   if (!gethostname(hostname, sizeof(hostname) - 1)) {
01375                      strncat(p, hostname, sizeof(prompt) - strlen(prompt) - 1);
01376                   } else {
01377                      strncat(p, "localhost", sizeof(prompt) - strlen(prompt) - 1);
01378                   }
01379                   break;
01380                case 'H': /* short hostname */
01381                   if (!gethostname(hostname, sizeof(hostname) - 1)) {
01382                      for (i=0;i<sizeof(hostname);i++) {
01383                         if (hostname[i] == '.') {
01384                            hostname[i] = '\0';
01385                            break;
01386                         }
01387                      }
01388                      strncat(p, hostname, sizeof(prompt) - strlen(prompt) - 1);
01389                   } else {
01390                      strncat(p, "localhost", sizeof(prompt) - strlen(prompt) - 1);
01391                   }
01392                   break;
01393 #ifdef linux
01394                case 'l': /* load avg */
01395                   t++;
01396                   if ((LOADAVG = fopen("/proc/loadavg", "r"))) {
01397                      float avg1, avg2, avg3;
01398                      int actproc, totproc, npid, which;
01399                      fscanf(LOADAVG, "%f %f %f %d/%d %d",
01400                         &avg1, &avg2, &avg3, &actproc, &totproc, &npid);
01401                      if (sscanf(t, "%d", &which) == 1) {
01402                         switch (which) {
01403                            case 1:
01404                               snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg1);
01405                               break;
01406                            case 2:
01407                               snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg2);
01408                               break;
01409                            case 3:
01410                               snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg3);
01411                               break;
01412                            case 4:
01413                               snprintf(p, sizeof(prompt) - strlen(prompt), "%d/%d", actproc, totproc);
01414                               break;
01415                            case 5:
01416                               snprintf(p, sizeof(prompt) - strlen(prompt), "%d", npid);
01417                               break;
01418                         }
01419                      }
01420                   }
01421                   break;
01422 #endif
01423                case 't': /* time */
01424                   memset(&tm, 0, sizeof(struct tm));
01425                   time(&ts);
01426                   if (localtime_r(&ts, &tm)) {
01427                      strftime(p, sizeof(prompt) - strlen(prompt), "%H:%M:%S", &tm);
01428                   }
01429                   break;
01430                case '#': /* process console or remote? */
01431                   if (! option_remote) {
01432                      strncat(p, "#", sizeof(prompt) - strlen(prompt) - 1);
01433                   } else {
01434                      strncat(p, ">", sizeof(prompt) - strlen(prompt) - 1);
01435                   }
01436                   break;
01437                case '%': /* literal % */
01438                   strncat(p, "%", sizeof(prompt) - strlen(prompt) - 1);
01439                   break;
01440                case '\0': /* % is last character - prevent bug */
01441                   t--;
01442                   break;
01443             }
01444             while (*p != '\0') {
01445                p++;
01446             }
01447             t++;
01448          } else {
01449             *p = *t;
01450             p++;
01451             t++;
01452          }
01453       }
01454       if (color_used) {
01455          /* Force colors back to normal at end */
01456          term_color_code(term_code, COLOR_WHITE, COLOR_BLACK, sizeof(term_code));
01457          if (strlen(term_code) > sizeof(prompt) - strlen(prompt)) {
01458             strncat(prompt + sizeof(prompt) - strlen(term_code) - 1, term_code, strlen(term_code));
01459          } else {
01460             strncat(p, term_code, sizeof(term_code));
01461          }
01462       }
01463    } else if (remotehostname)
01464       snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT2, remotehostname);
01465    else
01466       snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT);
01467 
01468    return(prompt);   
01469 }
01470 
01471 static char **ast_el_strtoarr(char *buf)
01472 {
01473    char **match_list = NULL, *retstr;
01474    size_t match_list_len;
01475    int matches = 0;
01476 
01477    match_list_len = 1;
01478    while ( (retstr = strsep(&buf, " ")) != NULL) {
01479 
01480       if (!strcmp(retstr, AST_CLI_COMPLETE_EOF))
01481          break;
01482       if (matches + 1 >= match_list_len) {
01483          match_list_len <<= 1;
01484          match_list = realloc(match_list, match_list_len * sizeof(char *));
01485       }
01486 
01487       match_list[matches++] = strdup(retstr);
01488    }
01489 
01490    if (!match_list)
01491       return (char **) NULL;
01492 
01493    if (matches>= match_list_len)
01494       match_list = realloc(match_list, (match_list_len + 1) * sizeof(char *));
01495 
01496    match_list[matches] = (char *) NULL;
01497 
01498    return match_list;
01499 }
01500 
01501 static int ast_el_sort_compare(const void *i1, const void *i2)
01502 {
01503    char *s1, *s2;
01504 
01505    s1 = ((char **)i1)[0];
01506    s2 = ((char **)i2)[0];
01507 
01508    return strcasecmp(s1, s2);
01509 }
01510 
01511 static int ast_cli_display_match_list(char **matches, int len, int max)
01512 {
01513    int i, idx, limit, count;
01514    int screenwidth = 0;
01515    int numoutput = 0, numoutputline = 0;
01516 
01517    screenwidth = ast_get_termcols(STDOUT_FILENO);
01518 
01519    /* find out how many entries can be put on one line, with two spaces between strings */
01520    limit = screenwidth / (max + 2);
01521    if (limit == 0)
01522       limit = 1;
01523 
01524    /* how many lines of output */
01525    count = len / limit;
01526    if (count * limit < len)
01527       count++;
01528 
01529    idx = 1;
01530 
01531    qsort(&matches[0], (size_t)(len + 1), sizeof(char *), ast_el_sort_compare);
01532 
01533    for (; count > 0; count--) {
01534       numoutputline = 0;
01535       for (i=0; i < limit && matches[idx]; i++, idx++) {
01536 
01537          /* Don't print dupes */
01538          if ( (matches[idx+1] != NULL && strcmp(matches[idx], matches[idx+1]) == 0 ) ) {
01539             i--;
01540             free(matches[idx]);
01541             matches[idx] = NULL;
01542             continue;
01543          }
01544 
01545          numoutput++;
01546          numoutputline++;
01547          fprintf(stdout, "%-*s  ", max, matches[idx]);
01548          free(matches[idx]);
01549          matches[idx] = NULL;
01550       }
01551       if (numoutputline > 0)
01552          fprintf(stdout, "\n");
01553    }
01554 
01555    return numoutput;
01556 }
01557 
01558 
01559 static char *cli_complete(EditLine *el, int ch)
01560 {
01561    int len=0;
01562    char *ptr;
01563    int nummatches = 0;
01564    char **matches;
01565    int retval = CC_ERROR;
01566    char buf[2048];
01567    int res;
01568 
01569    LineInfo *lf = (LineInfo *)el_line(el);
01570 
01571    *(char *)lf->cursor = '\0';
01572    ptr = (char *)lf->cursor;
01573    if (ptr) {
01574       while (ptr > lf->buffer) {
01575          if (isspace(*ptr)) {
01576             ptr++;
01577             break;
01578          }
01579          ptr--;
01580       }
01581    }
01582 
01583    len = lf->cursor - ptr;
01584 
01585    if (option_remote) {
01586       snprintf(buf, sizeof(buf),"_COMMAND NUMMATCHES \"%s\" \"%s\"", lf->buffer, ptr); 
01587       fdprint(ast_consock, buf);
01588       res = read(ast_consock, buf, sizeof(buf));
01589       buf[res] = '\0';
01590       nummatches = atoi(buf);
01591 
01592       if (nummatches > 0) {
01593          char *mbuf;
01594          int mlen = 0, maxmbuf = 2048;
01595          /* Start with a 2048 byte buffer */
01596          mbuf = malloc(maxmbuf);
01597          if (!mbuf)
01598             return (char *)(CC_ERROR);
01599          snprintf(buf, sizeof(buf),"_COMMAND MATCHESARRAY \"%s\" \"%s\"", lf->buffer, ptr); 
01600          fdprint(ast_consock, buf);
01601          res = 0;
01602          mbuf[0] = '\0';
01603          while (!strstr(mbuf, AST_CLI_COMPLETE_EOF) && res != -1) {
01604             if (mlen + 1024 > maxmbuf) {
01605                /* Every step increment buffer 1024 bytes */
01606                maxmbuf += 1024;
01607                mbuf = realloc(mbuf, maxmbuf);
01608                if (!mbuf)
01609                   return (char *)(CC_ERROR);
01610             }
01611             /* Only read 1024 bytes at a time */
01612             res = read(ast_consock, mbuf + mlen, 1024);
01613             if (res > 0)
01614                mlen += res;
01615          }
01616          mbuf[mlen] = '\0';
01617 
01618          matches = ast_el_strtoarr(mbuf);
01619          free(mbuf);
01620       } else
01621          matches = (char **) NULL;
01622 
01623 
01624    } else {
01625 
01626       nummatches = ast_cli_generatornummatches((char *)lf->buffer,ptr);
01627       matches = ast_cli_completion_matches((char *)lf->buffer,ptr);
01628    }
01629 
01630    if (matches) {
01631       int i;
01632       int matches_num, maxlen, match_len;
01633 
01634       if (matches[0][0] != '\0') {
01635          el_deletestr(el, (int) len);
01636          el_insertstr(el, matches[0]);
01637          retval = CC_REFRESH;
01638       }
01639 
01640       if (nummatches == 1) {
01641          /* Found an exact match */
01642          el_insertstr(el, " ");
01643          retval = CC_REFRESH;
01644       } else {
01645          /* Must be more than one match */
01646          for (i=1, maxlen=0; matches[i]; i++) {
01647             match_len = strlen(matches[i]);
01648             if (match_len > maxlen)
01649                maxlen = match_len;
01650          }
01651          matches_num = i - 1;
01652          if (matches_num >1) {
01653             fprintf(stdout, "\n");
01654             ast_cli_display_match_list(matches, nummatches, maxlen);
01655             retval = CC_REDISPLAY;
01656          } else { 
01657             el_insertstr(el," ");
01658             retval = CC_REFRESH;
01659          }
01660       }
01661    free(matches);
01662    }
01663 
01664    return (char *)(long)retval;
01665 }
01666 
01667 static int ast_el_initialize(void)
01668 {
01669    HistEvent ev;
01670    char *editor = getenv("AST_EDITOR");
01671 
01672    if (el != NULL)
01673       el_end(el);
01674    if (el_hist != NULL)
01675       history_end(el_hist);
01676 
01677    el = el_init("asterisk", stdin, stdout, stderr);
01678    el_set(el, EL_PROMPT, cli_prompt);
01679 
01680    el_set(el, EL_EDITMODE, 1);      
01681    el_set(el, EL_EDITOR, editor ? editor : "emacs");     
01682    el_hist = history_init();
01683    if (!el || !el_hist)
01684       return -1;
01685 
01686    /* setup history with 100 entries */
01687    history(el_hist, &ev, H_SETSIZE, 100);
01688 
01689    el_set(el, EL_HIST, history, el_hist);
01690 
01691    el_set(el, EL_ADDFN, "ed-complete", "Complete argument", cli_complete);
01692    /* Bind <tab> to command completion */
01693    el_set(el, EL_BIND, "^I", "ed-complete", NULL);
01694    /* Bind ? to command completion */
01695    el_set(el, EL_BIND, "?", "ed-complete", NULL);
01696    /* Bind ^D to redisplay */
01697    el_set(el, EL_BIND, "^D", "ed-redisplay", NULL);
01698 
01699    return 0;
01700 }
01701 
01702 static int ast_el_add_history(char *buf)
01703 {
01704    HistEvent ev;
01705 
01706    if (el_hist == NULL || el == NULL)
01707       ast_el_initialize();
01708    if (strlen(buf) > 256)
01709       return 0;
01710    return (history(el_hist, &ev, H_ENTER, buf));
01711 }
01712 
01713 static int ast_el_write_history(char *filename)
01714 {
01715    HistEvent ev;
01716 
01717    if (el_hist == NULL || el == NULL)
01718       ast_el_initialize();
01719 
01720    return (history(el_hist, &ev, H_SAVE, filename));
01721 }
01722 
01723 static int ast_el_read_history(char *filename)
01724 {
01725    char buf[256];
01726    FILE *f;
01727    int ret = -1;
01728 
01729    if (el_hist == NULL || el == NULL)
01730       ast_el_initialize();
01731 
01732    if ((f = fopen(filename, "r")) == NULL)
01733       return ret;
01734 
01735    while (!feof(f)) {
01736       fgets(buf, sizeof(buf), f);
01737       if (!strcmp(buf, "_HiStOrY_V2_\n"))
01738          continue;
01739       if (ast_all_zeros(buf))
01740          continue;
01741       if ((ret = ast_el_add_history(buf)) == -1)
01742          break;
01743    }
01744    fclose(f);
01745 
01746    return ret;
01747 }
01748 
01749 static void ast_remotecontrol(char * data)
01750 {
01751    char buf[80];
01752    int res;
01753    char filename[80] = "";
01754    char *hostname;
01755    char *cpid;
01756    char *version;
01757    int pid;
01758    char tmp[80];
01759    char *stringp=NULL;
01760 
01761    char *ebuf;
01762    int num = 0;
01763 
01764    read(ast_consock, buf, sizeof(buf));
01765    if (data)
01766       write(ast_consock, data, strlen(data) + 1);
01767    stringp=buf;
01768    hostname = strsep(&stringp, "/");
01769    cpid = strsep(&stringp, "/");
01770    version = strsep(&stringp, "\n");
01771    if (!version)
01772       version = "<Version Unknown>";
01773    stringp=hostname;
01774    strsep(&stringp, ".");
01775    if (cpid)
01776       pid = atoi(cpid);
01777    else
01778       pid = -1;
01779    snprintf(tmp, sizeof(tmp), "set verbose atleast %d", option_verbose);
01780    fdprint(ast_consock, tmp);
01781    snprintf(tmp, sizeof(tmp), "set debug atleast %d", option_debug);
01782    fdprint(ast_consock, tmp);
01783    ast_verbose("Connected to Asterisk %s currently running on %s (pid = %d)\n", version, hostname, pid);
01784    remotehostname = hostname;
01785    if (getenv("HOME")) 
01786       snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
01787    if (el_hist == NULL || el == NULL)
01788       ast_el_initialize();
01789 
01790    el_set(el, EL_GETCFN, ast_el_read_char);
01791 
01792    if (!ast_strlen_zero(filename))
01793       ast_el_read_history(filename);
01794 
01795    if (option_exec && data) {  /* hack to print output then exit if asterisk -rx is used */
01796       char tempchar;
01797       struct pollfd fds;
01798       fds.fd = ast_consock;
01799       fds.events = POLLIN;
01800       fds.revents = 0;
01801       while (poll(&fds, 1, 100) > 0)
01802          ast_el_read_char(el, &tempchar);
01803       return;
01804    }
01805    for(;;) {
01806       ebuf = (char *)el_gets(el, &num);
01807 
01808       if (!ast_strlen_zero(ebuf)) {
01809          if (ebuf[strlen(ebuf)-1] == '\n')
01810             ebuf[strlen(ebuf)-1] = '\0';
01811          if (!remoteconsolehandler(ebuf)) {
01812             res = write(ast_consock, ebuf, strlen(ebuf) + 1);
01813             if (res < 1) {
01814                ast_log(LOG_WARNING, "Unable to write: %s\n", strerror(errno));
01815                break;
01816             }
01817          }
01818       }
01819    }
01820    printf("\nDisconnected from Asterisk server\n");
01821 }
01822 
01823 static int show_version(void)
01824 {
01825    printf("Asterisk " ASTERISK_VERSION "\n");
01826    return 0;
01827 }
01828 
01829 static int show_cli_help(void) {
01830    printf("Asterisk " ASTERISK_VERSION ", Copyright (C) 1999 - 2005, Digium, Inc. and others.\n");
01831    printf("Usage: asterisk [OPTIONS]\n");
01832    printf("Valid Options:\n");
01833    printf("   -V              Display version number and exit\n");
01834    printf("   -C <configfile> Use an alternate configuration file\n");
01835    printf("   -G <group>      Run as a group other than the caller\n");
01836    printf("   -U <user>       Run as a user other than the caller\n");
01837    printf("   -c              Provide console CLI\n");
01838    printf("   -F              Force Fork even if -v or -d were given\n");
01839    printf("   -d              Enable extra debugging\n");
01840    printf("   -f              Do not fork\n");
01841    printf("   -g              Dump core in case of a crash\n");
01842    printf("   -h              This help screen\n");
01843    printf("   -i              Initialize crypto keys at startup\n");
01844    printf("   -n              Disable console colorization\n");
01845    printf("   -p              Run as pseudo-realtime thread\n");
01846    printf("   -q              Quiet mode (suppress output)\n");
01847    printf("   -r              Connect to Asterisk on this machine\n");
01848    printf("   -R              Connect to Asterisk, and attempt to reconnect if disconnected\n");
01849    printf("   -t              Record soundfiles in /var/tmp and move them where they belong after they are done.\n");
01850    printf("   -T              Display the time in [Mmm dd hh:mm:ss] format for each line of output to the CLI.\n");
01851    printf("   -v              Increase verbosity (multiple v's = more verbose)\n");
01852    printf("   -x <cmd>        Execute command <cmd> (only valid with -r)\n");
01853    printf("\n");
01854    return 0;
01855 }
01856 
01857 static void ast_readconfig(void) {
01858    struct ast_config *cfg;
01859    struct ast_variable *v;
01860    char *config = AST_CONFIG_FILE;
01861 
01862    if (option_overrideconfig == 1) {
01863       cfg = ast_config_load(ast_config_AST_CONFIG_FILE);
01864       if (!cfg)
01865          ast_log(LOG_WARNING, "Unable to open specified master config file '%s', using built-in defaults\n", ast_config_AST_CONFIG_FILE);
01866    } else {
01867       cfg = ast_config_load(config);
01868    }
01869 
01870    /* init with buildtime config */
01871    ast_copy_string(ast_config_AST_CONFIG_DIR, AST_CONFIG_DIR, sizeof(ast_config_AST_CONFIG_DIR));
01872    ast_copy_string(ast_config_AST_SPOOL_DIR, AST_SPOOL_DIR, sizeof(ast_config_AST_SPOOL_DIR));
01873    ast_copy_string(ast_config_AST_MODULE_DIR, AST_MODULE_DIR, sizeof(ast_config_AST_MODULE_DIR));
01874    snprintf(ast_config_AST_MONITOR_DIR, sizeof(ast_config_AST_MONITOR_DIR) - 1, "%s/monitor", ast_config_AST_SPOOL_DIR);
01875    ast_copy_string(ast_config_AST_VAR_DIR, AST_VAR_DIR, sizeof(ast_config_AST_VAR_DIR));
01876    ast_copy_string(ast_config_AST_DATA_DIR, AST_DATA_DIR, sizeof(ast_config_AST_DATA_DIR));
01877    ast_copy_string(ast_config_AST_LOG_DIR, AST_LOG_DIR, sizeof(ast_config_AST_LOG_DIR));
01878    ast_copy_string(ast_config_AST_AGI_DIR, AST_AGI_DIR, sizeof(ast_config_AST_AGI_DIR));
01879    ast_copy_string(ast_config_AST_DB, AST_DB, sizeof(ast_config_AST_DB));
01880    ast_copy_string(ast_config_AST_KEY_DIR, AST_KEY_DIR, sizeof(ast_config_AST_KEY_DIR));
01881    ast_copy_string(ast_config_AST_PID, AST_PID, sizeof(ast_config_AST_PID));
01882    ast_copy_string(ast_config_AST_SOCKET, AST_SOCKET, sizeof(ast_config_AST_SOCKET));
01883    ast_copy_string(ast_config_AST_RUN_DIR, AST_RUN_DIR, sizeof(ast_config_AST_RUN_DIR));
01884 
01885    /* no asterisk.conf? no problem, use buildtime config! */
01886    if (!cfg) {
01887       return;
01888    }
01889    v = ast_variable_browse(cfg, "files");
01890    while (v) {
01891       if (!strcasecmp(v->name, "astctlpermissions")) {
01892          ast_copy_string(ast_config_AST_CTL_PERMISSIONS, v->value, sizeof(ast_config_AST_CTL_PERMISSIONS));
01893       } else if (!strcasecmp(v->name, "astctlowner")) {
01894          ast_copy_string(ast_config_AST_CTL_OWNER, v->value, sizeof(ast_config_AST_CTL_OWNER));
01895       } else if (!strcasecmp(v->name, "astctlgroup")) {
01896          ast_copy_string(ast_config_AST_CTL_GROUP, v->value, sizeof(ast_config_AST_CTL_GROUP));
01897       } else if (!strcasecmp(v->name, "astctl")) {
01898          ast_copy_string(ast_config_AST_CTL, v->value, sizeof(ast_config_AST_CTL));
01899       }
01900       v = v->next;
01901    }
01902    v = ast_variable_browse(cfg, "directories");
01903    while(v) {
01904       if (!strcasecmp(v->name, "astetcdir")) {
01905          ast_copy_string(ast_config_AST_CONFIG_DIR, v->value, sizeof(ast_config_AST_CONFIG_DIR));
01906       } else if (!strcasecmp(v->name, "astspooldir")) {
01907          ast_copy_string(ast_config_AST_SPOOL_DIR, v->value, sizeof(ast_config_AST_SPOOL_DIR));
01908          snprintf(ast_config_AST_MONITOR_DIR, sizeof(ast_config_AST_MONITOR_DIR) - 1, "%s/monitor", v->value);
01909       } else if (!strcasecmp(v->name, "astvarlibdir")) {
01910          ast_copy_string(ast_config_AST_VAR_DIR, v->value, sizeof(ast_config_AST_VAR_DIR));
01911          snprintf(ast_config_AST_DB, sizeof(ast_config_AST_DB), "%s/astdb", v->value);
01912          snprintf(ast_config_AST_KEY_DIR, sizeof(ast_config_AST_KEY_DIR), "%s/keys", v->value);
01913       } else if (!strcasecmp(v->name, "astdatadir")) {
01914          ast_copy_string(ast_config_AST_DATA_DIR, v->value, sizeof(ast_config_AST_DATA_DIR));
01915       } else if (!strcasecmp(v->name, "astlogdir")) {
01916          ast_copy_string(ast_config_AST_LOG_DIR, v->value, sizeof(ast_config_AST_LOG_DIR));
01917       } else if (!strcasecmp(v->name, "astagidir")) {
01918          ast_copy_string(ast_config_AST_AGI_DIR, v->value, sizeof(ast_config_AST_AGI_DIR));
01919       } else if (!strcasecmp(v->name, "astrundir")) {
01920          snprintf(ast_config_AST_PID, sizeof(ast_config_AST_PID), "%s/%s", v->value, "asterisk.pid");
01921          snprintf(ast_config_AST_SOCKET, sizeof(ast_config_AST_SOCKET), "%s/%s", v->value, ast_config_AST_CTL);
01922          ast_copy_string(ast_config_AST_RUN_DIR, v->value, sizeof(ast_config_AST_RUN_DIR));
01923       } else if (!strcasecmp(v->name, "astmoddir")) {
01924          ast_copy_string(ast_config_AST_MODULE_DIR, v->value, sizeof(ast_config_AST_MODULE_DIR));
01925       }
01926       v = v->next;
01927    }
01928    v = ast_variable_browse(cfg, "options");
01929    while(v) {
01930       /* verbose level (-v at startup) */
01931       if (!strcasecmp(v->name, "verbose")) {
01932          option_verbose = atoi(v->value);
01933       /* whether or not to force timestamping. (-T at startup) */
01934       } else if (!strcasecmp(v->name, "timestamp")) {
01935          option_timestamp = ast_true(v->value);
01936       /* whether or not to support #exec in config files */
01937       } else if (!strcasecmp(v->name, "execincludes")) {
01938          option_exec_includes = ast_true(v->value);
01939       /* debug level (-d at startup) */
01940       } else if (!strcasecmp(v->name, "debug")) {
01941          option_debug = 0;
01942          if (sscanf(v->value, "%d", &option_debug) != 1) {
01943             option_debug = ast_true(v->value);
01944          }
01945       /* Disable forking (-f at startup) */
01946       } else if (!strcasecmp(v->name, "nofork")) {
01947          option_nofork = ast_true(v->value);
01948       /* Run quietly (-q at startup ) */
01949       } else if (!strcasecmp(v->name, "quiet")) {
01950          option_quiet = ast_true(v->value);
01951       /* Run as console (-c at startup, implies nofork) */
01952       } else if (!strcasecmp(v->name, "console")) {
01953          option_console = ast_true(v->value);
01954       /* Run with highg priority if the O/S permits (-p at startup) */
01955       } else if (!strcasecmp(v->name, "highpriority")) {
01956          option_highpriority = ast_true(v->value);
01957       /* Initialize RSA auth keys (IAX2) (-i at startup) */
01958       } else if (!strcasecmp(v->name, "initcrypto")) {
01959          option_initcrypto = ast_true(v->value);
01960       /* Disable ANSI colors for console (-c at startup) */
01961       } else if (!strcasecmp(v->name, "nocolor")) {
01962          option_nocolor = ast_true(v->value);
01963       /* Disable some usage warnings for picky people :p */
01964       } else if (!strcasecmp(v->name, "dontwarn")) {
01965          option_dontwarn = ast_true(v->value);
01966       /* Dump core in case of crash (-g) */
01967       } else if (!strcasecmp(v->name, "dumpcore")) {
01968          option_dumpcore = ast_true(v->value);
01969       /* Cache recorded sound files to another directory during recording */
01970       } else if (!strcasecmp(v->name, "cache_record_files")) {
01971          option_cache_record_files = ast_true(v->value);
01972       /* Specify cache directory */
01973       }  else if (!strcasecmp(v->name, "record_cache_dir")) {
01974          ast_copy_string(record_cache_dir, v->value, AST_CACHE_DIR_LEN);
01975       /* Build transcode paths via SLINEAR, instead of directly */
01976       } else if (!strcasecmp(v->name, "transcode_via_sln")) {
01977          option_transcode_slin = ast_true(v->value);
01978       /* Transmit SLINEAR silence while a channel is being recorded */
01979       } else if (!strcasecmp(v->name, "transmit_silence_during_record")) {
01980          option_transmit_silence_during_record = ast_true(v->value);
01981       } else if (!strcasecmp(v->name, "maxcalls")) {
01982          if ((sscanf(v->value, "%d", &option_maxcalls) != 1) || (option_maxcalls < 0)) {
01983             option_maxcalls = 0;
01984          }
01985       } else if (!strcasecmp(v->name, "maxload")) {
01986          double test[1];
01987 
01988          if (getloadavg(test, 1) == -1) {
01989             ast_log(LOG_ERROR, "Cannot obtain load average on this system. 'maxload' option disabled.\n");
01990             option_maxload = 0.0;
01991          } else if ((sscanf(v->value, "%lf", &option_maxload) != 1) || (option_maxload < 0.0)) {
01992             option_maxload = 0.0;
01993          }
01994       /* What user to run as */
01995       } else if (!strcasecmp(v->name, "runuser")) {
01996          ast_copy_string(ast_config_AST_RUN_USER, v->value, sizeof(ast_config_AST_RUN_USER));
01997       /* What group to run as */
01998       } else if (!strcasecmp(v->name, "rungroup")) {
01999          ast_copy_string(ast_config_AST_RUN_GROUP, v->value, sizeof(ast_config_AST_RUN_GROUP));
02000       }
02001       v = v->next;
02002    }
02003    ast_config_destroy(cfg);
02004 }
02005 
02006 int main(int argc, char *argv[])
02007 {
02008    int c;
02009    char filename[80] = "";
02010    char hostname[MAXHOSTNAMELEN]="";
02011    char tmp[80];
02012    char * xarg = NULL;
02013    int x;
02014    FILE *f;
02015    sigset_t sigs;
02016    int num;
02017    int is_child_of_nonroot=0;
02018    char *buf;
02019    char *runuser=NULL, *rungroup=NULL;
02020 
02021    /* Remember original args for restart */
02022    if (argc > sizeof(_argv) / sizeof(_argv[0]) - 1) {
02023       fprintf(stderr, "Truncating argument size to %d\n", (int)(sizeof(_argv) / sizeof(_argv[0])) - 1);
02024       argc = sizeof(_argv) / sizeof(_argv[0]) - 1;
02025    }
02026    for (x=0;x<argc;x++)
02027       _argv[x] = argv[x];
02028    _argv[x] = NULL;
02029 
02030    /* if the progname is rasterisk consider it a remote console */
02031    if (argv[0] && (strstr(argv[0], "rasterisk")) != NULL) {
02032       option_remote++;
02033       option_nofork++;
02034    }
02035    if (gethostname(hostname, sizeof(hostname)-1))
02036       ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
02037    ast_mainpid = getpid();
02038    ast_ulaw_init();
02039    ast_alaw_init();
02040    callerid_init();
02041    ast_utils_init();
02042    tdd_init();
02043    /* When Asterisk restarts after it has dropped the root privileges,
02044     * it can't issue setuid(), setgid(), setgroups() or set_priority() 
02045     * */
02046    if (getenv("ASTERISK_ALREADY_NONROOT"))
02047       is_child_of_nonroot=1;
02048    if (getenv("HOME")) 
02049       snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
02050    /* Check if we're root */
02051    /*
02052    if (geteuid()) {
02053       ast_log(LOG_ERROR, "Must be run as root\n");
02054       exit(1);
02055    }
02056    */
02057    /* Check for options */
02058    while((c=getopt(argc, argv, "tThfdvVqprRgcFinx:U:G:C:L:M:")) != -1) {
02059       switch(c) {
02060       case 'd':
02061          option_debug++;
02062          option_nofork++;
02063          break;
02064       case 'c':
02065          option_console++;
02066          option_nofork++;
02067          break;
02068       case 'F':
02069          option_daemonize++;
02070          break;
02071       case 'f':
02072          option_nofork++;
02073          break;
02074       case 'n':
02075          option_nocolor++;
02076          break;
02077       case 'r':
02078          option_remote++;
02079          option_nofork++;
02080          break;
02081       case 'R':
02082          option_remote++;
02083          option_nofork++;
02084          option_reconnect++;
02085          break;
02086       case 'p':
02087          option_highpriority++;
02088          break;
02089       case 'v':
02090          option_verbose++;
02091          option_nofork++;
02092          break;
02093       case 'M':
02094          if ((sscanf(optarg, "%d", &option_maxcalls) != 1) || (option_maxcalls < 0))
02095             option_maxcalls = 0;
02096          break;
02097       case 'L':
02098          if ((sscanf(optarg, "%lf", &option_maxload) != 1) || (option_maxload < 0.0))
02099             option_maxload = 0.0;
02100          break;
02101       case 'q':
02102          option_quiet++;
02103          break;
02104       case 't':
02105          option_cache_record_files++;
02106          break;
02107       case 'T':
02108          option_timestamp++;
02109          break;
02110       case 'x':
02111          option_exec++;
02112          xarg = optarg;
02113          break;
02114       case 'C':
02115          ast_copy_string((char *)ast_config_AST_CONFIG_FILE,optarg,sizeof(ast_config_AST_CONFIG_FILE));
02116          option_overrideconfig++;
02117          break;
02118       case 'i':
02119          option_initcrypto++;
02120          break;
02121       case'g':
02122          option_dumpcore++;
02123          break;
02124       case 'h':
02125          show_cli_help();
02126          exit(0);
02127       case 'V':
02128          show_version();
02129          exit(0);
02130       case 'U':
02131          runuser = optarg;
02132          break;
02133       case 'G':
02134          rungroup = optarg;
02135          break;
02136       case '?':
02137          exit(1);
02138       }
02139    }
02140 
02141    /* For remote connections, change the name of the remote connection.
02142     * We do this for the benefit of init scripts (which need to know if/when
02143     * the main asterisk process has died yet). */
02144    if (option_remote) {
02145       strcpy(argv[0], "rasterisk");
02146       for (x = 1; x < argc; x++) {
02147          argv[x] = argv[0] + 10;
02148       }
02149    }
02150 
02151    if (option_console && !option_verbose) 
02152       ast_verbose("[ Reading Master Configuration ]");
02153    ast_readconfig();
02154 
02155    if (option_dumpcore) {
02156       struct rlimit l;
02157       memset(&l, 0, sizeof(l));
02158       l.rlim_cur = RLIM_INFINITY;
02159       l.rlim_max = RLIM_INFINITY;
02160       if (setrlimit(RLIMIT_CORE, &l)) {
02161          ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s\n", strerror(errno));
02162       }
02163    }
02164 
02165    if ((!rungroup) && !ast_strlen_zero(ast_config_AST_RUN_GROUP))
02166       rungroup = ast_config_AST_RUN_GROUP;
02167    if ((!runuser) && !ast_strlen_zero(ast_config_AST_RUN_USER))
02168       runuser = ast_config_AST_RUN_USER;
02169 #ifndef __CYGWIN__
02170 
02171    if (!is_child_of_nonroot) 
02172       ast_set_priority(option_highpriority);
02173 
02174    if (!is_child_of_nonroot && rungroup) {
02175       struct group *gr;
02176       gr = getgrnam(rungroup);
02177       if (!gr) {
02178          ast_log(LOG_WARNING, "No such group '%s'!\n", rungroup);
02179          exit(1);
02180       }
02181       if (setgid(gr->gr_gid)) {
02182          ast_log(LOG_WARNING, "Unable to setgid to %d (%s)\n", (int)gr->gr_gid, rungroup);
02183          exit(1);
02184       }
02185       if (setgroups(0, NULL)) {
02186          ast_log(LOG_WARNING, "Unable to drop unneeded groups\n");
02187          exit(1);
02188       }
02189       if (option_verbose)
02190          ast_verbose("Running as group '%s'\n", rungroup);
02191    }
02192 
02193    if (!is_child_of_nonroot && runuser) {
02194       struct passwd *pw;
02195       pw = getpwnam(runuser);
02196       if (!pw) {
02197          ast_log(LOG_WARNING, "No such user '%s'!\n", runuser);
02198          exit(1);
02199       }
02200       if (!rungroup) {
02201          if (setgid(pw->pw_gid)) {
02202             ast_log(LOG_WARNING, "Unable to setgid to %d!\n", (int)pw->pw_gid);
02203             exit(1);
02204          }
02205          if (initgroups(pw->pw_name, pw->pw_gid)) {
02206             ast_log(LOG_WARNING, "Unable to init groups for '%s'\n", runuser);
02207             exit(1);
02208          }
02209       }
02210       if (!rungroup && initgroups(runuser, pw->pw_gid)) {
02211          ast_log(LOG_WARNING, "Unable to initialize supplementary group list for %s\n", runuser);
02212          exit(1);
02213       }
02214       if (setuid(pw->pw_uid)) {
02215          ast_log(LOG_WARNING, "Unable to setuid to %d (%s)\n", (int)pw->pw_uid, runuser);
02216          exit(1);
02217       }
02218       setenv("ASTERISK_ALREADY_NONROOT","yes",1);
02219       if (option_verbose)
02220          ast_verbose("Running as user '%s'\n", runuser);
02221    }
02222 
02223 #endif /* __CYGWIN__ */
02224 
02225 #ifdef linux
02226 
02227    if (geteuid() && option_dumpcore) {
02228       if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
02229          ast_log(LOG_WARNING, "Unable to set the process for core dumps after changing to a non-root user. %s\n", strerror(errno));
02230       }  
02231    }
02232 
02233 #endif
02234 
02235    term_init();
02236    printf(term_end());
02237    fflush(stdout);
02238 
02239    if (option_console && !option_verbose) 
02240       ast_verbose("[ Initializing Custom Configuration Options ]");
02241    /* custom config setup */
02242    register_config_cli();
02243    read_config_maps();
02244    
02245 
02246    if (option_console) {
02247       if (el_hist == NULL || el == NULL)
02248          ast_el_initialize();
02249 
02250       if (!ast_strlen_zero(filename))
02251          ast_el_read_history(filename);
02252    }
02253 
02254    if (ast_tryconnect()) {
02255       /* One is already running */
02256       if (option_remote) {
02257          if (option_exec) {
02258             ast_remotecontrol(xarg);
02259             quit_handler(0, 0, 0, 0);
02260             exit(0);
02261          }
02262          printf(term_quit());
02263          ast_register_verbose(console_verboser);
02264          WELCOME_MESSAGE;
02265          ast_remotecontrol(NULL);
02266          quit_handler(0, 0, 0, 0);
02267          exit(0);
02268       } else {
02269          ast_log(LOG_ERROR, "Asterisk already running on %s.  Use 'asterisk -r' to connect.\n", (char *)ast_config_AST_SOCKET);
02270          printf(term_quit());
02271          exit(1);
02272       }
02273    } else if (option_remote || option_exec) {
02274       ast_log(LOG_ERROR, "Unable to connect to remote asterisk (does %s exist?)\n",ast_config_AST_SOCKET);
02275       printf(term_quit());
02276       exit(1);
02277    }
02278    /* Blindly write pid file since we couldn't connect */
02279    unlink((char *)ast_config_AST_PID);
02280    f = fopen((char *)ast_config_AST_PID, "w");
02281    if (f) {
02282       fprintf(f, "%d\n", (int)getpid());
02283       fclose(f);
02284    } else
02285       ast_log(LOG_WARNING, "Unable to open pid file '%s': %s\n", (char *)ast_config_AST_PID, strerror(errno));
02286 
02287    if (option_daemonize ||
02288          (!option_verbose && !option_debug && !option_nofork && !option_console)
02289    ) {
02290       daemon(0,0);
02291       ast_mainpid = getpid();
02292       /* Blindly re-write pid file since we are forking */
02293       unlink((char *)ast_config_AST_PID);
02294       f = fopen((char *)ast_config_AST_PID, "w");
02295       if (f) {
02296          fprintf(f, "%d\n", ast_mainpid);
02297          fclose(f);
02298       } else
02299          ast_log(LOG_WARNING, "Unable to open pid file '%s': %s\n", (char *)ast_config_AST_PID, strerror(errno));
02300       ast_mainpid = getpid();
02301    }
02302 
02303    /* Test recursive mutex locking. */
02304    if (test_for_thread_safety())
02305       ast_verbose("Warning! Asterisk is not thread safe.\n");
02306 
02307    ast_makesocket();
02308    sigemptyset(&sigs);
02309    sigaddset(&sigs, SIGHUP);
02310    sigaddset(&sigs, SIGTERM);
02311    sigaddset(&sigs, SIGINT);
02312    sigaddset(&sigs, SIGPIPE);
02313    sigaddset(&sigs, SIGWINCH);
02314    pthread_sigmask(SIG_BLOCK, &sigs, NULL);
02315    if (option_console || option_verbose || option_remote)
02316       ast_register_verbose(console_verboser);
02317    /* Print a welcome message if desired */
02318    if (option_verbose || option_console) {
02319       WELCOME_MESSAGE;
02320    }
02321    if (option_console && !option_verbose) 
02322       ast_verbose("[ Booting...");
02323 
02324    signal(SIGURG, urg_handler);
02325    signal(SIGINT, __quit_handler);
02326    signal(SIGTERM, __quit_handler);
02327    signal(SIGHUP, hup_handler);
02328    signal(SIGCHLD, child_handler);
02329    signal(SIGPIPE, SIG_IGN);
02330 
02331    /* ensure that the random number generators are seeded with a different value every time
02332       Asterisk is started
02333    */
02334    srand((unsigned int) getpid() + (unsigned int) time(NULL));
02335    srandom((unsigned int) getpid() + (unsigned int) time(NULL));
02336 
02337    if (init_logger()) {
02338       printf(term_quit());
02339       exit(1);
02340    }
02341    if (dnsmgr_init()) {
02342       printf(term_quit());
02343       exit(1);
02344    }
02345    /* load 'preload' modules, required for access to Realtime-mapped configuration files */
02346    if (load_modules(1)) {
02347       printf(term_quit());
02348       exit(1);
02349    }
02350    ast_channels_init();
02351    if (init_manager()) {
02352       printf(term_quit());
02353       exit(1);
02354    }
02355    if (ast_cdr_engine_init()) {
02356       printf(term_quit());
02357       exit(1);
02358    }
02359    if (ast_device_state_engine_init()) {
02360       printf(term_quit());
02361       exit(1);
02362    }
02363    ast_rtp_init();
02364    if (ast_image_init()) {
02365       printf(term_quit());
02366       exit(1);
02367    }
02368    if (ast_file_init()) {
02369       printf(term_quit());
02370       exit(1);
02371    }
02372    if (load_pbx()) {
02373       printf(term_quit());
02374       exit(1);
02375    }
02376    if (init_framer()) {
02377       printf(term_quit());
02378       exit(1);
02379    }
02380    if (astdb_init()) {
02381       printf(term_quit());
02382       exit(1);
02383    }
02384    if (ast_enum_init()) {
02385       printf(term_quit());
02386       exit(1);
02387    }
02388    if (load_modules(0)) {
02389       printf(term_quit());
02390       exit(1);
02391    }
02392 
02393    dnsmgr_start_refresh();
02394 
02395 #if 0
02396    /* This should no longer be necessary */
02397    /* sync cust config and reload some internals in case a custom config handler binded to them */
02398    read_ast_cust_config();
02399    reload_logger(0);
02400    reload_manager();
02401    ast_enum_reload();
02402    ast_rtp_reload();
02403 #endif
02404 
02405 
02406    /* We might have the option of showing a console, but for now just
02407       do nothing... */
02408    if (option_console && !option_verbose)
02409       ast_verbose(" ]\n");
02410    if (option_verbose || option_console)
02411       ast_verbose(term_color(tmp, "Asterisk Ready.\n", COLOR_BRWHITE, COLOR_BLACK, sizeof(tmp)));
02412    if (option_nofork)
02413       consolethread = pthread_self();
02414    fully_booted = 1;
02415    pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
02416 #ifdef __AST_DEBUG_MALLOC
02417    __ast_mm_init();
02418 #endif   
02419    time(&ast_startuptime);
02420    ast_cli_register_multiple(core_cli, sizeof(core_cli) / sizeof(core_cli[0]));
02421    if (option_console) {
02422       /* Console stuff now... */
02423       /* Register our quit function */
02424       char title[256];
02425       set_icon("Asterisk");
02426       snprintf(title, sizeof(title), "Asterisk Console on '%s' (pid %d)", hostname, ast_mainpid);
02427       set_title(title);
02428 
02429       for (;;) {
02430          buf = (char *)el_gets(el, &num);
02431          if (buf) {
02432             if (buf[strlen(buf)-1] == '\n')
02433                buf[strlen(buf)-1] = '\0';
02434 
02435             consolehandler((char *)buf);
02436          } else {
02437             if (write(STDOUT_FILENO, "\nUse EXIT or QUIT to exit the asterisk console\n",
02438                  strlen("\nUse EXIT or QUIT to exit the asterisk console\n")) < 0) {
02439                /* Whoa, stdout disappeared from under us... Make /dev/null's */
02440                int fd;
02441                fd = open("/dev/null", O_RDWR);
02442                if (fd > -1) {
02443                   dup2(fd, STDOUT_FILENO);
02444                   dup2(fd, STDIN_FILENO);
02445                } else
02446                   ast_log(LOG_WARNING, "Failed to open /dev/null to recover from dead console.  Bad things will happen!\n");
02447                break;
02448             }
02449          }
02450       }
02451 
02452    }
02453    /* Do nothing */
02454    for(;;)  {  /* apparently needed for the MACos */
02455       struct pollfd p = { -1 /* no descriptor */, 0, 0 };
02456       poll(&p, 0, -1);
02457    }
02458    return 0;
02459 }

Generated on Fri Sep 29 11:12:25 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7