Thu Apr 28 2011 17:15:23

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