Tue Aug 24 2010 19:41:31

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