Mon Sep 20 2010 00:23:08

Asterisk developer's documentation


logger.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 Asterisk Logger
00022  * 
00023  * Logging routines
00024  *
00025  * \author Mark Spencer <markster@digium.com>
00026  */
00027 
00028 /*
00029  * define _ASTERISK_LOGGER_H to prevent the inclusion of logger.h;
00030  * it redefines LOG_* which we need to define syslog_level_map.
00031  * later, we force the inclusion of logger.h again.
00032  */
00033 #define _ASTERISK_LOGGER_H
00034 #include "asterisk.h"
00035 
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 256822 $")
00037 
00038 /*
00039  * WARNING: additional #include directives should NOT be placed here, they 
00040  * should be placed AFTER '#undef _ASTERISK_LOGGER_H' below
00041  */
00042 #include "asterisk/_private.h"
00043 #include "asterisk/paths.h"   /* use ast_config_AST_LOG_DIR */
00044 #include <signal.h>
00045 #include <time.h>
00046 #include <sys/stat.h>
00047 #include <fcntl.h>
00048 #ifdef HAVE_BKTR
00049 #include <execinfo.h>
00050 #define MAX_BACKTRACE_FRAMES 20
00051 #endif
00052 
00053 #define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values,
00054               from <syslog.h> which is included by logger.h */
00055 #include <syslog.h>
00056 
00057 static int syslog_level_map[] = {
00058    LOG_DEBUG,
00059    LOG_INFO,    /* arbitrary equivalent of LOG_EVENT */
00060    LOG_NOTICE,
00061    LOG_WARNING,
00062    LOG_ERR,
00063    LOG_DEBUG,
00064    LOG_DEBUG
00065 };
00066 
00067 #define SYSLOG_NLEVELS sizeof(syslog_level_map) / sizeof(int)
00068 
00069 #undef _ASTERISK_LOGGER_H  /* now include logger.h */
00070 #include "asterisk/logger.h"
00071 #include "asterisk/lock.h"
00072 #include "asterisk/channel.h"
00073 #include "asterisk/config.h"
00074 #include "asterisk/term.h"
00075 #include "asterisk/cli.h"
00076 #include "asterisk/utils.h"
00077 #include "asterisk/manager.h"
00078 #include "asterisk/threadstorage.h"
00079 #include "asterisk/strings.h"
00080 #include "asterisk/pbx.h"
00081 #include "asterisk/app.h"
00082 
00083 #if defined(__linux__) && !defined(__NR_gettid)
00084 #include <asm/unistd.h>
00085 #endif
00086 
00087 #if defined(__linux__) && defined(__NR_gettid)
00088 #define GETTID() syscall(__NR_gettid)
00089 #else
00090 #define GETTID() getpid()
00091 #endif
00092 
00093 static char dateformat[256] = "%b %e %T";    /* Original Asterisk Format */
00094 
00095 static char queue_log_name[256] = QUEUELOG;
00096 static char exec_after_rotate[256] = "";
00097 
00098 static int filesize_reload_needed;
00099 static int global_logmask = -1;
00100 
00101 enum rotatestrategy {
00102    SEQUENTIAL = 1 << 0,     /* Original method - create a new file, in order */
00103    ROTATE = 1 << 1,         /* Rotate all files, such that the oldest file has the highest suffix */
00104    TIMESTAMP = 1 << 2,      /* Append the epoch timestamp onto the end of the archived file */
00105 } rotatestrategy = SEQUENTIAL;
00106 
00107 static struct {
00108    unsigned int queue_log:1;
00109    unsigned int event_log:1;
00110 } logfiles = { 1, 1 };
00111 
00112 static char hostname[MAXHOSTNAMELEN];
00113 
00114 enum logtypes {
00115    LOGTYPE_SYSLOG,
00116    LOGTYPE_FILE,
00117    LOGTYPE_CONSOLE,
00118 };
00119 
00120 struct logchannel {
00121    int logmask;         /* What to log to this channel */
00122    int disabled;        /* If this channel is disabled or not */
00123    int facility;        /* syslog facility */
00124    enum logtypes type;     /* Type of log channel */
00125    FILE *fileptr;       /* logfile logging file pointer */
00126    char filename[256];     /* Filename */
00127    AST_LIST_ENTRY(logchannel) list;
00128 };
00129 
00130 static AST_RWLIST_HEAD_STATIC(logchannels, logchannel);
00131 
00132 enum logmsgtypes {
00133    LOGMSG_NORMAL = 0,
00134    LOGMSG_VERBOSE,
00135 };
00136 
00137 struct logmsg {
00138    enum logmsgtypes type;
00139    char date[256];
00140    int level;
00141    char file[80];
00142    int line;
00143    char function[80];
00144    long process_id;
00145    AST_LIST_ENTRY(logmsg) list;
00146    char str[0];
00147 };
00148 
00149 static AST_LIST_HEAD_STATIC(logmsgs, logmsg);
00150 static pthread_t logthread = AST_PTHREADT_NULL;
00151 static ast_cond_t logcond;
00152 static int close_logger_thread = 0;
00153 
00154 static FILE *eventlog;
00155 static FILE *qlog;
00156 
00157 /*! \brief Logging channels used in the Asterisk logging system */
00158 static char *levels[] = {
00159    "DEBUG",
00160    "EVENT",
00161    "NOTICE",
00162    "WARNING",
00163    "ERROR",
00164    "VERBOSE",
00165    "DTMF"
00166 };
00167 
00168 /*! \brief Colors used in the console for logging */
00169 static int colors[] = {
00170    COLOR_BRGREEN,
00171    COLOR_BRBLUE,
00172    COLOR_YELLOW,
00173    COLOR_BRRED,
00174    COLOR_RED,
00175    COLOR_GREEN,
00176    COLOR_BRGREEN
00177 };
00178 
00179 AST_THREADSTORAGE(verbose_buf);
00180 #define VERBOSE_BUF_INIT_SIZE   256
00181 
00182 AST_THREADSTORAGE(log_buf);
00183 #define LOG_BUF_INIT_SIZE       256
00184 
00185 static int make_components(const char *s, int lineno)
00186 {
00187    char *w;
00188    int res = 0;
00189    char *stringp = ast_strdupa(s);
00190 
00191    while ((w = strsep(&stringp, ","))) {
00192       w = ast_skip_blanks(w);
00193       if (!strcasecmp(w, "error")) 
00194          res |= (1 << __LOG_ERROR);
00195       else if (!strcasecmp(w, "warning"))
00196          res |= (1 << __LOG_WARNING);
00197       else if (!strcasecmp(w, "notice"))
00198          res |= (1 << __LOG_NOTICE);
00199       else if (!strcasecmp(w, "event"))
00200          res |= (1 << __LOG_EVENT);
00201       else if (!strcasecmp(w, "debug"))
00202          res |= (1 << __LOG_DEBUG);
00203       else if (!strcasecmp(w, "verbose"))
00204          res |= (1 << __LOG_VERBOSE);
00205       else if (!strcasecmp(w, "dtmf"))
00206          res |= (1 << __LOG_DTMF);
00207       else {
00208          fprintf(stderr, "Logfile Warning: Unknown keyword '%s' at line %d of logger.conf\n", w, lineno);
00209       }
00210    }
00211 
00212    return res;
00213 }
00214 
00215 static struct logchannel *make_logchannel(const char *channel, const char *components, int lineno)
00216 {
00217    struct logchannel *chan;
00218    char *facility;
00219 #ifndef SOLARIS
00220    CODE *cptr;
00221 #endif
00222 
00223    if (ast_strlen_zero(channel) || !(chan = ast_calloc(1, sizeof(*chan))))
00224       return NULL;
00225 
00226    if (!strcasecmp(channel, "console")) {
00227       chan->type = LOGTYPE_CONSOLE;
00228    } else if (!strncasecmp(channel, "syslog", 6)) {
00229       /*
00230       * syntax is:
00231       *  syslog.facility => level,level,level
00232       */
00233       facility = strchr(channel, '.');
00234       if (!facility++ || !facility) {
00235          facility = "local0";
00236       }
00237 
00238 #ifndef SOLARIS
00239       /*
00240       * Walk through the list of facilitynames (defined in sys/syslog.h)
00241       * to see if we can find the one we have been given
00242       */
00243       chan->facility = -1;
00244       cptr = facilitynames;
00245       while (cptr->c_name) {
00246          if (!strcasecmp(facility, cptr->c_name)) {
00247             chan->facility = cptr->c_val;
00248             break;
00249          }
00250          cptr++;
00251       }
00252 #else
00253       chan->facility = -1;
00254       if (!strcasecmp(facility, "kern")) 
00255          chan->facility = LOG_KERN;
00256       else if (!strcasecmp(facility, "USER")) 
00257          chan->facility = LOG_USER;
00258       else if (!strcasecmp(facility, "MAIL")) 
00259          chan->facility = LOG_MAIL;
00260       else if (!strcasecmp(facility, "DAEMON")) 
00261          chan->facility = LOG_DAEMON;
00262       else if (!strcasecmp(facility, "AUTH")) 
00263          chan->facility = LOG_AUTH;
00264       else if (!strcasecmp(facility, "SYSLOG")) 
00265          chan->facility = LOG_SYSLOG;
00266       else if (!strcasecmp(facility, "LPR")) 
00267          chan->facility = LOG_LPR;
00268       else if (!strcasecmp(facility, "NEWS")) 
00269          chan->facility = LOG_NEWS;
00270       else if (!strcasecmp(facility, "UUCP")) 
00271          chan->facility = LOG_UUCP;
00272       else if (!strcasecmp(facility, "CRON")) 
00273          chan->facility = LOG_CRON;
00274       else if (!strcasecmp(facility, "LOCAL0")) 
00275          chan->facility = LOG_LOCAL0;
00276       else if (!strcasecmp(facility, "LOCAL1")) 
00277          chan->facility = LOG_LOCAL1;
00278       else if (!strcasecmp(facility, "LOCAL2")) 
00279          chan->facility = LOG_LOCAL2;
00280       else if (!strcasecmp(facility, "LOCAL3")) 
00281          chan->facility = LOG_LOCAL3;
00282       else if (!strcasecmp(facility, "LOCAL4")) 
00283          chan->facility = LOG_LOCAL4;
00284       else if (!strcasecmp(facility, "LOCAL5")) 
00285          chan->facility = LOG_LOCAL5;
00286       else if (!strcasecmp(facility, "LOCAL6")) 
00287          chan->facility = LOG_LOCAL6;
00288       else if (!strcasecmp(facility, "LOCAL7")) 
00289          chan->facility = LOG_LOCAL7;
00290 #endif /* Solaris */
00291 
00292       if (0 > chan->facility) {
00293          fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
00294          ast_free(chan);
00295          return NULL;
00296       }
00297 
00298       chan->type = LOGTYPE_SYSLOG;
00299       snprintf(chan->filename, sizeof(chan->filename), "%s", channel);
00300       openlog("asterisk", LOG_PID, chan->facility);
00301    } else {
00302       if (!ast_strlen_zero(hostname)) {
00303          snprintf(chan->filename, sizeof(chan->filename), "%s/%s.%s",
00304              channel[0] != '/' ? ast_config_AST_LOG_DIR : "", channel, hostname);
00305       } else {
00306          snprintf(chan->filename, sizeof(chan->filename), "%s/%s",
00307              channel[0] != '/' ? ast_config_AST_LOG_DIR : "", channel);
00308       }
00309       chan->fileptr = fopen(chan->filename, "a");
00310       if (!chan->fileptr) {
00311          /* Can't log here, since we're called with a lock */
00312          fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", chan->filename, strerror(errno));
00313       } 
00314       chan->type = LOGTYPE_FILE;
00315    }
00316    chan->logmask = make_components(components, lineno);
00317    return chan;
00318 }
00319 
00320 static void init_logger_chain(int locked)
00321 {
00322    struct logchannel *chan;
00323    struct ast_config *cfg;
00324    struct ast_variable *var;
00325    const char *s;
00326    struct ast_flags config_flags = { 0 };
00327 
00328    if (!(cfg = ast_config_load2("logger.conf", "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID)
00329       return;
00330 
00331    /* delete our list of log channels */
00332    if (!locked)
00333       AST_RWLIST_WRLOCK(&logchannels);
00334    while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list)))
00335       ast_free(chan);
00336    if (!locked)
00337       AST_RWLIST_UNLOCK(&logchannels);
00338    
00339    global_logmask = 0;
00340    errno = 0;
00341    /* close syslog */
00342    closelog();
00343    
00344    /* If no config file, we're fine, set default options. */
00345    if (!cfg) {
00346       if (errno)
00347          fprintf(stderr, "Unable to open logger.conf: %s; default settings will be used.\n", strerror(errno));
00348       else
00349          fprintf(stderr, "Errors detected in logger.conf: see above; default settings will be used.\n");
00350       if (!(chan = ast_calloc(1, sizeof(*chan))))
00351          return;
00352       chan->type = LOGTYPE_CONSOLE;
00353       chan->logmask = 28; /*warning,notice,error */
00354       if (!locked)
00355          AST_RWLIST_WRLOCK(&logchannels);
00356       AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
00357       if (!locked)
00358          AST_RWLIST_UNLOCK(&logchannels);
00359       global_logmask |= chan->logmask;
00360       return;
00361    }
00362    
00363    if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
00364       if (ast_true(s)) {
00365          if (gethostname(hostname, sizeof(hostname) - 1)) {
00366             ast_copy_string(hostname, "unknown", sizeof(hostname));
00367             fprintf(stderr, "What box has no hostname???\n");
00368          }
00369       } else
00370          hostname[0] = '\0';
00371    } else
00372       hostname[0] = '\0';
00373    if ((s = ast_variable_retrieve(cfg, "general", "dateformat")))
00374       ast_copy_string(dateformat, s, sizeof(dateformat));
00375    else
00376       ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
00377    if ((s = ast_variable_retrieve(cfg, "general", "queue_log")))
00378       logfiles.queue_log = ast_true(s);
00379    if ((s = ast_variable_retrieve(cfg, "general", "event_log")))
00380       logfiles.event_log = ast_true(s);
00381    if ((s = ast_variable_retrieve(cfg, "general", "queue_log_name")))
00382       ast_copy_string(queue_log_name, s, sizeof(queue_log_name));
00383    if ((s = ast_variable_retrieve(cfg, "general", "exec_after_rotate")))
00384       ast_copy_string(exec_after_rotate, s, sizeof(exec_after_rotate));
00385    if ((s = ast_variable_retrieve(cfg, "general", "rotatestrategy"))) {
00386       if (strcasecmp(s, "timestamp") == 0)
00387          rotatestrategy = TIMESTAMP;
00388       else if (strcasecmp(s, "rotate") == 0)
00389          rotatestrategy = ROTATE;
00390       else if (strcasecmp(s, "sequential") == 0)
00391          rotatestrategy = SEQUENTIAL;
00392       else
00393          fprintf(stderr, "Unknown rotatestrategy: %s\n", s);
00394    } else {
00395       if ((s = ast_variable_retrieve(cfg, "general", "rotatetimestamp"))) {
00396          rotatestrategy = ast_true(s) ? TIMESTAMP : SEQUENTIAL;
00397          fprintf(stderr, "rotatetimestamp option has been deprecated.  Please use rotatestrategy instead.\n");
00398       }
00399    }
00400 
00401    if (!locked)
00402       AST_RWLIST_WRLOCK(&logchannels);
00403    var = ast_variable_browse(cfg, "logfiles");
00404    for (; var; var = var->next) {
00405       if (!(chan = make_logchannel(var->name, var->value, var->lineno)))
00406          continue;
00407       AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
00408       global_logmask |= chan->logmask;
00409    }
00410    if (!locked)
00411       AST_RWLIST_UNLOCK(&logchannels);
00412 
00413    ast_config_destroy(cfg);
00414 }
00415 
00416 void ast_child_verbose(int level, const char *fmt, ...)
00417 {
00418    char *msg = NULL, *emsg = NULL, *sptr, *eptr;
00419    va_list ap, aq;
00420    int size;
00421 
00422    /* Don't bother, if the level isn't that high */
00423    if (option_verbose < level) {
00424       return;
00425    }
00426 
00427    va_start(ap, fmt);
00428    va_copy(aq, ap);
00429    if ((size = vsnprintf(msg, 0, fmt, ap)) < 0) {
00430       va_end(ap);
00431       va_end(aq);
00432       return;
00433    }
00434    va_end(ap);
00435 
00436    if (!(msg = ast_malloc(size + 1))) {
00437       va_end(aq);
00438       return;
00439    }
00440 
00441    vsnprintf(msg, size + 1, fmt, aq);
00442    va_end(aq);
00443 
00444    if (!(emsg = ast_malloc(size * 2 + 1))) {
00445       ast_free(msg);
00446       return;
00447    }
00448 
00449    for (sptr = msg, eptr = emsg; ; sptr++) {
00450       if (*sptr == '"') {
00451          *eptr++ = '\\';
00452       }
00453       *eptr++ = *sptr;
00454       if (*sptr == '\0') {
00455          break;
00456       }
00457    }
00458    ast_free(msg);
00459 
00460    fprintf(stdout, "verbose \"%s\" %d\n", emsg, level);
00461    fflush(stdout);
00462    ast_free(emsg);
00463 }
00464 
00465 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
00466 {
00467    va_list ap;
00468    char qlog_msg[8192];
00469    int qlog_len;
00470    char time_str[16];
00471 
00472    if (ast_check_realtime("queue_log")) {
00473       va_start(ap, fmt);
00474       vsnprintf(qlog_msg, sizeof(qlog_msg), fmt, ap);
00475       va_end(ap);
00476       snprintf(time_str, sizeof(time_str), "%ld", (long)time(NULL));
00477       ast_store_realtime("queue_log", "time", time_str, 
00478                   "callid", callid, 
00479                   "queuename", queuename, 
00480                   "agent", agent, 
00481                   "event", event,
00482                   "data", qlog_msg,
00483                   SENTINEL);
00484    } else {
00485       if (qlog) {
00486          va_start(ap, fmt);
00487          qlog_len = snprintf(qlog_msg, sizeof(qlog_msg), "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
00488          vsnprintf(qlog_msg + qlog_len, sizeof(qlog_msg) - qlog_len, fmt, ap);
00489          va_end(ap);
00490       }
00491       AST_RWLIST_RDLOCK(&logchannels);
00492       if (qlog) {
00493          fprintf(qlog, "%s\n", qlog_msg);
00494          fflush(qlog);
00495       }
00496       AST_RWLIST_UNLOCK(&logchannels);
00497    }
00498 }
00499 
00500 static int rotate_file(const char *filename)
00501 {
00502    char old[PATH_MAX];
00503    char new[PATH_MAX];
00504    int x, y, which, found, res = 0, fd;
00505    char *suffixes[4] = { "", ".gz", ".bz2", ".Z" };
00506 
00507    switch (rotatestrategy) {
00508    case SEQUENTIAL:
00509       for (x = 0; ; x++) {
00510          snprintf(new, sizeof(new), "%s.%d", filename, x);
00511          fd = open(new, O_RDONLY);
00512          if (fd > -1)
00513             close(fd);
00514          else
00515             break;
00516       }
00517       if (rename(filename, new)) {
00518          fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
00519          res = -1;
00520       }
00521       break;
00522    case TIMESTAMP:
00523       snprintf(new, sizeof(new), "%s.%ld", filename, (long)time(NULL));
00524       if (rename(filename, new)) {
00525          fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
00526          res = -1;
00527       }
00528       break;
00529    case ROTATE:
00530       /* Find the next empty slot, including a possible suffix */
00531       for (x = 0; ; x++) {
00532          found = 0;
00533          for (which = 0; which < ARRAY_LEN(suffixes); which++) {
00534             snprintf(new, sizeof(new), "%s.%d%s", filename, x, suffixes[which]);
00535             fd = open(new, O_RDONLY);
00536             if (fd > -1) {
00537                close(fd);
00538                found = 1;
00539                break;
00540             }
00541          }
00542          if (!found) {
00543             break;
00544          }
00545       }
00546 
00547       /* Found an empty slot */
00548       for (y = x; y > 0; y--) {
00549          for (which = 0; which < ARRAY_LEN(suffixes); which++) {
00550             snprintf(old, sizeof(old), "%s.%d%s", filename, y - 1, suffixes[which]);
00551             fd = open(old, O_RDONLY);
00552             if (fd > -1) {
00553                /* Found the right suffix */
00554                close(fd);
00555                snprintf(new, sizeof(new), "%s.%d%s", filename, y, suffixes[which]);
00556                if (rename(old, new)) {
00557                   fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
00558                   res = -1;
00559                }
00560                break;
00561             }
00562          }
00563       }
00564 
00565       /* Finally, rename the current file */
00566       snprintf(new, sizeof(new), "%s.0", filename);
00567       if (rename(filename, new)) {
00568          fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
00569          res = -1;
00570       }
00571    }
00572 
00573    if (!ast_strlen_zero(exec_after_rotate)) {
00574       struct ast_channel *c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Logger/rotate");
00575       char buf[512];
00576       pbx_builtin_setvar_helper(c, "filename", filename);
00577       pbx_substitute_variables_helper(c, exec_after_rotate, buf, sizeof(buf));
00578       if (ast_safe_system(buf) == -1) {
00579          ast_log(LOG_WARNING, "error executing '%s'\n", buf);
00580       }
00581       ast_channel_free(c);
00582    }
00583    return res;
00584 }
00585 
00586 static int reload_logger(int rotate)
00587 {
00588    char old[PATH_MAX] = "";
00589    int event_rotate = rotate, queue_rotate = rotate;
00590    struct logchannel *f;
00591    int res = 0;
00592    struct stat st;
00593 
00594    AST_RWLIST_WRLOCK(&logchannels);
00595 
00596    if (eventlog) {
00597       if (rotate < 0) {
00598          /* Check filesize - this one typically doesn't need an auto-rotate */
00599          snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, EVENTLOG);
00600          if (stat(old, &st) != 0 || st.st_size > 0x40000000) { /* Arbitrarily, 1 GB */
00601             fclose(eventlog);
00602             eventlog = NULL;
00603          } else
00604             event_rotate = 0;
00605       } else {
00606          fclose(eventlog);
00607          eventlog = NULL;
00608       }
00609    } else
00610       event_rotate = 0;
00611 
00612    if (qlog) {
00613       if (rotate < 0) {
00614          /* Check filesize - this one typically doesn't need an auto-rotate */
00615          snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
00616          if (stat(old, &st) != 0 || st.st_size > 0x40000000) { /* Arbitrarily, 1 GB */
00617             fclose(qlog);
00618             qlog = NULL;
00619          } else
00620             queue_rotate = 0;
00621       } else {
00622          fclose(qlog);
00623          qlog = NULL;
00624       }
00625    } else 
00626       queue_rotate = 0;
00627 
00628    ast_mkdir(ast_config_AST_LOG_DIR, 0777);
00629 
00630    AST_RWLIST_TRAVERSE(&logchannels, f, list) {
00631       if (f->disabled) {
00632          f->disabled = 0;  /* Re-enable logging at reload */
00633          manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
00634       }
00635       if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
00636          fclose(f->fileptr);  /* Close file */
00637          f->fileptr = NULL;
00638          if (rotate)
00639             rotate_file(f->filename);
00640       }
00641    }
00642 
00643    filesize_reload_needed = 0;
00644 
00645    init_logger_chain(1 /* locked */);
00646 
00647    if (logfiles.event_log) {
00648       snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, EVENTLOG);
00649       if (event_rotate)
00650          rotate_file(old);
00651 
00652       eventlog = fopen(old, "a");
00653       if (eventlog) {
00654          ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
00655          ast_verb(1, "Asterisk Event Logger restarted\n");
00656       } else {
00657          ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
00658          res = -1;
00659       }
00660    }
00661 
00662    if (logfiles.queue_log) {
00663       snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
00664       if (queue_rotate)
00665          rotate_file(old);
00666 
00667       qlog = fopen(old, "a");
00668       if (qlog) {
00669          AST_RWLIST_UNLOCK(&logchannels);
00670          ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
00671          AST_RWLIST_WRLOCK(&logchannels);
00672          ast_log(LOG_EVENT, "Restarted Asterisk Queue Logger\n");
00673          ast_verb(1, "Asterisk Queue Logger restarted\n");
00674       } else {
00675          ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
00676          res = -1;
00677       }
00678    }
00679 
00680    AST_RWLIST_UNLOCK(&logchannels);
00681 
00682    return res;
00683 }
00684 
00685 /*! \brief Reload the logger module without rotating log files (also used from loader.c during
00686    a full Asterisk reload) */
00687 int logger_reload(void)
00688 {
00689    if(reload_logger(0))
00690       return RESULT_FAILURE;
00691    return RESULT_SUCCESS;
00692 }
00693 
00694 static char *handle_logger_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00695 {
00696    switch (cmd) {
00697    case CLI_INIT:
00698       e->command = "logger reload";
00699       e->usage = 
00700          "Usage: logger reload\n"
00701          "       Reloads the logger subsystem state.  Use after restarting syslogd(8) if you are using syslog logging.\n";
00702       return NULL;
00703    case CLI_GENERATE:
00704       return NULL;
00705    }
00706    if (reload_logger(0)) {
00707       ast_cli(a->fd, "Failed to reload the logger\n");
00708       return CLI_FAILURE;
00709    }
00710    return CLI_SUCCESS;
00711 }
00712 
00713 static char *handle_logger_rotate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00714 {
00715    switch (cmd) {
00716    case CLI_INIT:
00717       e->command = "logger rotate";
00718       e->usage = 
00719          "Usage: logger rotate\n"
00720          "       Rotates and Reopens the log files.\n";
00721       return NULL;
00722    case CLI_GENERATE:
00723       return NULL;   
00724    }
00725    if (reload_logger(1)) {
00726       ast_cli(a->fd, "Failed to reload the logger and rotate log files\n");
00727       return CLI_FAILURE;
00728    } 
00729    return CLI_SUCCESS;
00730 }
00731 
00732 static char *handle_logger_set_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00733 {
00734    int x;
00735    int state;
00736    int level = -1;
00737 
00738    switch (cmd) {
00739    case CLI_INIT:
00740       e->command = "logger set level {DEBUG|NOTICE|WARNING|ERROR|VERBOSE|DTMF} {on|off}";
00741       e->usage = 
00742          "Usage: logger set level {DEBUG|NOTICE|WARNING|ERROR|VERBOSE|DTMF} {on|off}\n"
00743          "       Set a specific log level to enabled/disabled for this console.\n";
00744       return NULL;
00745    case CLI_GENERATE:
00746       return NULL;
00747    }
00748 
00749    if (a->argc < 5)
00750       return CLI_SHOWUSAGE;
00751 
00752    for (x = 0; x <= NUMLOGLEVELS; x++) {
00753       if (!strcasecmp(a->argv[3], levels[x])) {
00754          level = x;
00755          break;
00756       }
00757    }
00758 
00759    state = ast_true(a->argv[4]) ? 1 : 0;
00760 
00761    if (level != -1) {
00762       ast_console_toggle_loglevel(a->fd, level, state);
00763       ast_cli(a->fd, "Logger status for '%s' has been set to '%s'.\n", levels[level], state ? "on" : "off");
00764    } else
00765       return CLI_SHOWUSAGE;
00766 
00767    return CLI_SUCCESS;
00768 }
00769 
00770 /*! \brief CLI command to show logging system configuration */
00771 static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00772 {
00773 #define FORMATL   "%-35.35s %-8.8s %-9.9s "
00774    struct logchannel *chan;
00775    switch (cmd) {
00776    case CLI_INIT:
00777       e->command = "logger show channels";
00778       e->usage = 
00779          "Usage: logger show channels\n"
00780          "       List configured logger channels.\n";
00781       return NULL;
00782    case CLI_GENERATE:
00783       return NULL;   
00784    }
00785    ast_cli(a->fd, FORMATL, "Channel", "Type", "Status");
00786    ast_cli(a->fd, "Configuration\n");
00787    ast_cli(a->fd, FORMATL, "-------", "----", "------");
00788    ast_cli(a->fd, "-------------\n");
00789    AST_RWLIST_RDLOCK(&logchannels);
00790    AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
00791       ast_cli(a->fd, FORMATL, chan->filename, chan->type == LOGTYPE_CONSOLE ? "Console" : (chan->type == LOGTYPE_SYSLOG ? "Syslog" : "File"),
00792          chan->disabled ? "Disabled" : "Enabled");
00793       ast_cli(a->fd, " - ");
00794       if (chan->logmask & (1 << __LOG_DEBUG)) 
00795          ast_cli(a->fd, "Debug ");
00796       if (chan->logmask & (1 << __LOG_DTMF)) 
00797          ast_cli(a->fd, "DTMF ");
00798       if (chan->logmask & (1 << __LOG_VERBOSE)) 
00799          ast_cli(a->fd, "Verbose ");
00800       if (chan->logmask & (1 << __LOG_WARNING)) 
00801          ast_cli(a->fd, "Warning ");
00802       if (chan->logmask & (1 << __LOG_NOTICE)) 
00803          ast_cli(a->fd, "Notice ");
00804       if (chan->logmask & (1 << __LOG_ERROR)) 
00805          ast_cli(a->fd, "Error ");
00806       if (chan->logmask & (1 << __LOG_EVENT)) 
00807          ast_cli(a->fd, "Event ");
00808       ast_cli(a->fd, "\n");
00809    }
00810    AST_RWLIST_UNLOCK(&logchannels);
00811    ast_cli(a->fd, "\n");
00812       
00813    return CLI_SUCCESS;
00814 }
00815 
00816 struct verb {
00817    void (*verboser)(const char *string);
00818    AST_LIST_ENTRY(verb) list;
00819 };
00820 
00821 static AST_RWLIST_HEAD_STATIC(verbosers, verb);
00822 
00823 static struct ast_cli_entry cli_logger[] = {
00824    AST_CLI_DEFINE(handle_logger_show_channels, "List configured log channels"),
00825    AST_CLI_DEFINE(handle_logger_reload, "Reopens the log files"),
00826    AST_CLI_DEFINE(handle_logger_rotate, "Rotates and reopens the log files"),
00827    AST_CLI_DEFINE(handle_logger_set_level, "Enables/Disables a specific logging level for this console")
00828 };
00829 
00830 static int handle_SIGXFSZ(int sig) 
00831 {
00832    /* Indicate need to reload */
00833    filesize_reload_needed = 1;
00834    return 0;
00835 }
00836 
00837 static void ast_log_vsyslog(int level, const char *file, int line, const char *function, char *str, long pid)
00838 {
00839    char buf[BUFSIZ];
00840 
00841    if (level >= SYSLOG_NLEVELS) {
00842       /* we are locked here, so cannot ast_log() */
00843       fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level);
00844       return;
00845    }
00846 
00847    if (level == __LOG_VERBOSE) {
00848       snprintf(buf, sizeof(buf), "VERBOSE[%ld]: %s", pid, str);
00849       level = __LOG_DEBUG;
00850    } else if (level == __LOG_DTMF) {
00851       snprintf(buf, sizeof(buf), "DTMF[%ld]: %s", pid, str);
00852       level = __LOG_DEBUG;
00853    } else {
00854       snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: %s",
00855           levels[level], pid, file, line, function, str);
00856    }
00857 
00858    term_strip(buf, buf, strlen(buf) + 1);
00859    syslog(syslog_level_map[level], "%s", buf);
00860 }
00861 
00862 /*! \brief Print a normal log message to the channels */
00863 static void logger_print_normal(struct logmsg *logmsg)
00864 {
00865    struct logchannel *chan = NULL;
00866    char buf[BUFSIZ];
00867 
00868    AST_RWLIST_RDLOCK(&logchannels);
00869 
00870    if (logfiles.event_log && logmsg->level == __LOG_EVENT) {
00871       fprintf(eventlog, "%s asterisk[%ld]: %s", logmsg->date, (long)getpid(), logmsg->str);
00872       fflush(eventlog);
00873       AST_RWLIST_UNLOCK(&logchannels);
00874       return;
00875    }
00876 
00877    if (!AST_RWLIST_EMPTY(&logchannels)) {
00878       AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
00879          /* If the channel is disabled, then move on to the next one */
00880          if (chan->disabled)
00881             continue;
00882          /* Check syslog channels */
00883          if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << logmsg->level))) {
00884             ast_log_vsyslog(logmsg->level, logmsg->file, logmsg->line, logmsg->function, logmsg->str, logmsg->process_id);
00885          /* Console channels */
00886          } else if (chan->type == LOGTYPE_CONSOLE && (chan->logmask & (1 << logmsg->level))) {
00887             char linestr[128];
00888             char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
00889 
00890             /* If the level is verbose, then skip it */
00891             if (logmsg->level == __LOG_VERBOSE)
00892                continue;
00893 
00894             /* Turn the numerical line number into a string */
00895             snprintf(linestr, sizeof(linestr), "%d", logmsg->line);
00896             /* Build string to print out */
00897             snprintf(buf, sizeof(buf), "[%s] %s[%ld]: %s:%s %s: %s",
00898                 logmsg->date,
00899                 term_color(tmp1, levels[logmsg->level], colors[logmsg->level], 0, sizeof(tmp1)),
00900                 logmsg->process_id,
00901                 term_color(tmp2, logmsg->file, COLOR_BRWHITE, 0, sizeof(tmp2)),
00902                 term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
00903                 term_color(tmp4, logmsg->function, COLOR_BRWHITE, 0, sizeof(tmp4)),
00904                 logmsg->str);
00905             /* Print out */
00906             ast_console_puts_mutable(buf, logmsg->level);
00907          /* File channels */
00908          } else if (chan->type == LOGTYPE_FILE && (chan->logmask & (1 << logmsg->level))) {
00909             int res = 0;
00910 
00911             /* If no file pointer exists, skip it */
00912             if (!chan->fileptr) {
00913                continue;
00914             }
00915 
00916             /* Print out to the file */
00917             res = fprintf(chan->fileptr, "[%s] %s[%ld] %s: %s",
00918                      logmsg->date, levels[logmsg->level], logmsg->process_id, logmsg->file, term_strip(buf, logmsg->str, BUFSIZ));
00919             if (res <= 0 && !ast_strlen_zero(logmsg->str)) {
00920                fprintf(stderr, "**** Asterisk Logging Error: ***********\n");
00921                if (errno == ENOMEM || errno == ENOSPC)
00922                   fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename);
00923                else
00924                   fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno));
00925                manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno));
00926                chan->disabled = 1;
00927             } else if (res > 0) {
00928                fflush(chan->fileptr);
00929             }
00930          }
00931       }
00932    } else if (logmsg->level != __LOG_VERBOSE) {
00933       fputs(logmsg->str, stdout);
00934    }
00935 
00936    AST_RWLIST_UNLOCK(&logchannels);
00937 
00938    /* If we need to reload because of the file size, then do so */
00939    if (filesize_reload_needed) {
00940       reload_logger(-1);
00941       ast_log(LOG_EVENT, "Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
00942       ast_verb(1, "Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
00943    }
00944 
00945    return;
00946 }
00947 
00948 /*! \brief Print a verbose message to the verbosers */
00949 static void logger_print_verbose(struct logmsg *logmsg)
00950 {
00951    struct verb *v = NULL;
00952 
00953    /* Iterate through the list of verbosers and pass them the log message string */
00954    AST_RWLIST_RDLOCK(&verbosers);
00955    AST_RWLIST_TRAVERSE(&verbosers, v, list)
00956       v->verboser(logmsg->str);
00957    AST_RWLIST_UNLOCK(&verbosers);
00958 
00959    return;
00960 }
00961 
00962 /*! \brief Actual logging thread */
00963 static void *logger_thread(void *data)
00964 {
00965    struct logmsg *next = NULL, *msg = NULL;
00966 
00967    for (;;) {
00968       /* We lock the message list, and see if any message exists... if not we wait on the condition to be signalled */
00969       AST_LIST_LOCK(&logmsgs);
00970       if (AST_LIST_EMPTY(&logmsgs)) {
00971          if (close_logger_thread) {
00972             break;
00973          } else {
00974             ast_cond_wait(&logcond, &logmsgs.lock);
00975          }
00976       }
00977       next = AST_LIST_FIRST(&logmsgs);
00978       AST_LIST_HEAD_INIT_NOLOCK(&logmsgs);
00979       AST_LIST_UNLOCK(&logmsgs);
00980 
00981       /* Otherwise go through and process each message in the order added */
00982       while ((msg = next)) {
00983          /* Get the next entry now so that we can free our current structure later */
00984          next = AST_LIST_NEXT(msg, list);
00985 
00986          /* Depending on the type, send it to the proper function */
00987          if (msg->type == LOGMSG_NORMAL)
00988             logger_print_normal(msg);
00989          else if (msg->type == LOGMSG_VERBOSE)
00990             logger_print_verbose(msg);
00991 
00992          /* Free the data since we are done */
00993          ast_free(msg);
00994       }
00995 
00996       /* If we should stop, then stop */
00997       if (close_logger_thread)
00998          break;
00999    }
01000 
01001    return NULL;
01002 }
01003 
01004 int init_logger(void)
01005 {
01006    char tmp[256];
01007    int res = 0;
01008 
01009    /* auto rotate if sig SIGXFSZ comes a-knockin */
01010    (void) signal(SIGXFSZ, (void *) handle_SIGXFSZ);
01011 
01012    /* start logger thread */
01013    ast_cond_init(&logcond, NULL);
01014    if (ast_pthread_create(&logthread, NULL, logger_thread, NULL) < 0) {
01015       ast_cond_destroy(&logcond);
01016       return -1;
01017    }
01018 
01019    /* register the logger cli commands */
01020    ast_cli_register_multiple(cli_logger, ARRAY_LEN(cli_logger));
01021 
01022    ast_mkdir(ast_config_AST_LOG_DIR, 0777);
01023   
01024    /* create log channels */
01025    init_logger_chain(0 /* locked */);
01026 
01027    /* create the eventlog */
01028    if (logfiles.event_log) {
01029       snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, EVENTLOG);
01030       eventlog = fopen(tmp, "a");
01031       if (eventlog) {
01032          ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
01033          ast_verb(1, "Asterisk Event Logger Started %s\n", tmp);
01034       } else {
01035          ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
01036          res = -1;
01037       }
01038    }
01039 
01040    if (logfiles.queue_log) {
01041       snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
01042       qlog = fopen(tmp, "a");
01043       ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
01044    }
01045    return res;
01046 }
01047 
01048 void close_logger(void)
01049 {
01050    struct logchannel *f = NULL;
01051 
01052    /* Stop logger thread */
01053    AST_LIST_LOCK(&logmsgs);
01054    close_logger_thread = 1;
01055    ast_cond_signal(&logcond);
01056    AST_LIST_UNLOCK(&logmsgs);
01057 
01058    if (logthread != AST_PTHREADT_NULL)
01059       pthread_join(logthread, NULL);
01060 
01061    AST_RWLIST_WRLOCK(&logchannels);
01062 
01063    if (eventlog) {
01064       fclose(eventlog);
01065       eventlog = NULL;
01066    }
01067 
01068    if (qlog) {
01069       fclose(qlog);
01070       qlog = NULL;
01071    }
01072 
01073    AST_RWLIST_TRAVERSE(&logchannels, f, list) {
01074       if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
01075          fclose(f->fileptr);
01076          f->fileptr = NULL;
01077       }
01078    }
01079 
01080    closelog(); /* syslog */
01081 
01082    AST_RWLIST_UNLOCK(&logchannels);
01083 
01084    return;
01085 }
01086 
01087 /*!
01088  * \brief send log messages to syslog and/or the console
01089  */
01090 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
01091 {
01092    struct logmsg *logmsg = NULL;
01093    struct ast_str *buf = NULL;
01094    struct ast_tm tm;
01095    struct timeval now = ast_tvnow();
01096    int res = 0;
01097    va_list ap;
01098 
01099    if (!(buf = ast_str_thread_get(&log_buf, LOG_BUF_INIT_SIZE)))
01100       return;
01101 
01102    if (AST_RWLIST_EMPTY(&logchannels)) {
01103       /*
01104        * we don't have the logger chain configured yet,
01105        * so just log to stdout
01106        */
01107       if (level != __LOG_VERBOSE) {
01108          int result;
01109          va_start(ap, fmt);
01110          result = ast_str_set_va(&buf, BUFSIZ, fmt, ap); /* XXX BUFSIZ ? */
01111          va_end(ap);
01112          if (result != AST_DYNSTR_BUILD_FAILED) {
01113             term_filter_escapes(ast_str_buffer(buf));
01114             fputs(ast_str_buffer(buf), stdout);
01115          }
01116       }
01117       return;
01118    }
01119    
01120    /* don't display LOG_DEBUG messages unless option_verbose _or_ option_debug
01121       are non-zero; LOG_DEBUG messages can still be displayed if option_debug
01122       is zero, if option_verbose is non-zero (this allows for 'level zero'
01123       LOG_DEBUG messages to be displayed, if the logmask on any channel
01124       allows it)
01125    */
01126    if (!option_verbose && !option_debug && (level == __LOG_DEBUG))
01127       return;
01128 
01129    /* Ignore anything that never gets logged anywhere */
01130    if (!(global_logmask & (1 << level)))
01131       return;
01132    
01133    /* Build string */
01134    va_start(ap, fmt);
01135    res = ast_str_set_va(&buf, BUFSIZ, fmt, ap);
01136    va_end(ap);
01137 
01138    /* If the build failed, then abort and free this structure */
01139    if (res == AST_DYNSTR_BUILD_FAILED)
01140       return;
01141 
01142    /* Create a new logging message */
01143    if (!(logmsg = ast_calloc(1, sizeof(*logmsg) + res + 1)))
01144       return;
01145 
01146    /* Copy string over */
01147    strcpy(logmsg->str, ast_str_buffer(buf));
01148 
01149    /* Set type to be normal */
01150    logmsg->type = LOGMSG_NORMAL;
01151 
01152    /* Create our date/time */
01153    ast_localtime(&now, &tm, NULL);
01154    ast_strftime(logmsg->date, sizeof(logmsg->date), dateformat, &tm);
01155 
01156    /* Copy over data */
01157    logmsg->level = level;
01158    logmsg->line = line;
01159    ast_copy_string(logmsg->file, file, sizeof(logmsg->file));
01160    ast_copy_string(logmsg->function, function, sizeof(logmsg->function));
01161    logmsg->process_id = (long) GETTID();
01162 
01163    /* If the logger thread is active, append it to the tail end of the list - otherwise skip that step */
01164    if (logthread != AST_PTHREADT_NULL) {
01165       AST_LIST_LOCK(&logmsgs);
01166       AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
01167       ast_cond_signal(&logcond);
01168       AST_LIST_UNLOCK(&logmsgs);
01169    } else {
01170       logger_print_normal(logmsg);
01171       ast_free(logmsg);
01172    }
01173 
01174    return;
01175 }
01176 
01177 #ifdef HAVE_BKTR
01178 
01179 struct ast_bt *ast_bt_create(void) 
01180 {
01181    struct ast_bt *bt = ast_calloc(1, sizeof(*bt));
01182    if (!bt) {
01183       ast_log(LOG_ERROR, "Unable to allocate memory for backtrace structure!\n");
01184       return NULL;
01185    }
01186 
01187    bt->alloced = 1;
01188 
01189    ast_bt_get_addresses(bt);
01190 
01191    return bt;
01192 }
01193 
01194 int ast_bt_get_addresses(struct ast_bt *bt)
01195 {
01196    bt->num_frames = backtrace(bt->addresses, AST_MAX_BT_FRAMES);
01197 
01198    return 0;
01199 }
01200 
01201 void *ast_bt_destroy(struct ast_bt *bt)
01202 {
01203    if (bt->alloced) {
01204       ast_free(bt);
01205    }
01206 
01207    return NULL;
01208 }
01209 
01210 #endif /* HAVE_BKTR */
01211 
01212 void ast_backtrace(void)
01213 {
01214 #ifdef HAVE_BKTR
01215    struct ast_bt *bt;
01216    int i = 0;
01217    char **strings;
01218 
01219    if (!(bt = ast_bt_create())) {
01220       ast_log(LOG_WARNING, "Unable to allocate space for backtrace structure\n");
01221       return;
01222    }
01223 
01224    if ((strings = backtrace_symbols(bt->addresses, bt->num_frames))) {
01225       ast_debug(1, "Got %d backtrace record%c\n", bt->num_frames, bt->num_frames != 1 ? 's' : ' ');
01226       for (i = 0; i < bt->num_frames; i++) {
01227          ast_log(LOG_DEBUG, "#%d: [%p] %s\n", i, bt->addresses[i], strings[i]);
01228       }
01229       free(strings);
01230    } else {
01231       ast_debug(1, "Could not allocate memory for backtrace\n");
01232    }
01233    ast_bt_destroy(bt);
01234 #else
01235    ast_log(LOG_WARNING, "Must run configure with '--with-execinfo' for stack backtraces.\n");
01236 #endif
01237 }
01238 
01239 void __ast_verbose_ap(const char *file, int line, const char *func, const char *fmt, va_list ap)
01240 {
01241    struct logmsg *logmsg = NULL;
01242    struct ast_str *buf = NULL;
01243    int res = 0;
01244 
01245    if (!(buf = ast_str_thread_get(&verbose_buf, VERBOSE_BUF_INIT_SIZE)))
01246       return;
01247 
01248    if (ast_opt_timestamp) {
01249       struct timeval now;
01250       struct ast_tm tm;
01251       char date[40];
01252       char *datefmt;
01253 
01254       now = ast_tvnow();
01255       ast_localtime(&now, &tm, NULL);
01256       ast_strftime(date, sizeof(date), dateformat, &tm);
01257       datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1);
01258       sprintf(datefmt, "%c[%s] %s", 127, date, fmt);
01259       fmt = datefmt;
01260    } else {
01261       char *tmp = alloca(strlen(fmt) + 2);
01262       sprintf(tmp, "%c%s", 127, fmt);
01263       fmt = tmp;
01264    }
01265 
01266    /* Build string */
01267    res = ast_str_set_va(&buf, 0, fmt, ap);
01268 
01269    /* If the build failed then we can drop this allocated message */
01270    if (res == AST_DYNSTR_BUILD_FAILED)
01271       return;
01272 
01273    if (!(logmsg = ast_calloc(1, sizeof(*logmsg) + res + 1)))
01274       return;
01275 
01276    strcpy(logmsg->str, ast_str_buffer(buf));
01277 
01278    ast_log(__LOG_VERBOSE, file, line, func, "%s", logmsg->str + 1);
01279 
01280    /* Set type */
01281    logmsg->type = LOGMSG_VERBOSE;
01282    
01283    /* Add to the list and poke the thread if possible */
01284    if (logthread != AST_PTHREADT_NULL) {
01285       AST_LIST_LOCK(&logmsgs);
01286       AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
01287       ast_cond_signal(&logcond);
01288       AST_LIST_UNLOCK(&logmsgs);
01289    } else {
01290       logger_print_verbose(logmsg);
01291       ast_free(logmsg);
01292    }
01293 }
01294 
01295 void __ast_verbose(const char *file, int line, const char *func, const char *fmt, ...)
01296 {
01297    va_list ap;
01298    va_start(ap, fmt);
01299    __ast_verbose_ap(file, line, func, fmt, ap);
01300    va_end(ap);
01301 }
01302 
01303 /* No new code should use this directly, but we have the ABI for backwards compat */
01304 #undef ast_verbose
01305 void __attribute__((format(printf, 1,2))) ast_verbose(const char *fmt, ...);
01306 void ast_verbose(const char *fmt, ...)
01307 {
01308    va_list ap;
01309    va_start(ap, fmt);
01310    __ast_verbose_ap("", 0, "", fmt, ap);
01311    va_end(ap);
01312 }
01313 
01314 int ast_register_verbose(void (*v)(const char *string)) 
01315 {
01316    struct verb *verb;
01317 
01318    if (!(verb = ast_malloc(sizeof(*verb))))
01319       return -1;
01320 
01321    verb->verboser = v;
01322 
01323    AST_RWLIST_WRLOCK(&verbosers);
01324    AST_RWLIST_INSERT_HEAD(&verbosers, verb, list);
01325    AST_RWLIST_UNLOCK(&verbosers);
01326    
01327    return 0;
01328 }
01329 
01330 int ast_unregister_verbose(void (*v)(const char *string))
01331 {
01332    struct verb *cur;
01333 
01334    AST_RWLIST_WRLOCK(&verbosers);
01335    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&verbosers, cur, list) {
01336       if (cur->verboser == v) {
01337          AST_RWLIST_REMOVE_CURRENT(list);
01338          ast_free(cur);
01339          break;
01340       }
01341    }
01342    AST_RWLIST_TRAVERSE_SAFE_END;
01343    AST_RWLIST_UNLOCK(&verbosers);
01344    
01345    return cur ? 0 : -1;
01346 }