Sat Apr 26 2014 22:01:38

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 /*** MODULEINFO
00029    <support_level>core</support_level>
00030  ***/
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 407456 $")
00035 
00036 /* When we include logger.h again it will trample on some stuff in syslog.h, but
00037  * nothing we care about in here. */
00038 #include <syslog.h>
00039 
00040 #include "asterisk/_private.h"
00041 #include "asterisk/paths.h"   /* use ast_config_AST_LOG_DIR */
00042 #include "asterisk/logger.h"
00043 #include "asterisk/lock.h"
00044 #include "asterisk/channel.h"
00045 #include "asterisk/config.h"
00046 #include "asterisk/term.h"
00047 #include "asterisk/cli.h"
00048 #include "asterisk/utils.h"
00049 #include "asterisk/manager.h"
00050 #include "asterisk/astobj2.h"
00051 #include "asterisk/threadstorage.h"
00052 #include "asterisk/strings.h"
00053 #include "asterisk/pbx.h"
00054 #include "asterisk/app.h"
00055 #include "asterisk/syslog.h"
00056 #include "asterisk/buildinfo.h"
00057 #include "asterisk/ast_version.h"
00058 
00059 #include <signal.h>
00060 #include <time.h>
00061 #include <sys/stat.h>
00062 #include <fcntl.h>
00063 #ifdef HAVE_BKTR
00064 #include <execinfo.h>
00065 #define MAX_BACKTRACE_FRAMES 20
00066 #  if defined(HAVE_DLADDR) && defined(HAVE_BFD) && defined(BETTER_BACKTRACES)
00067 #    include <dlfcn.h>
00068 #    include <bfd.h>
00069 #  endif
00070 #endif
00071 
00072 /*** DOCUMENTATION
00073  ***/
00074 
00075 static char dateformat[256] = "%b %e %T";    /* Original Asterisk Format */
00076 
00077 static char queue_log_name[256] = QUEUELOG;
00078 static char exec_after_rotate[256] = "";
00079 
00080 static int filesize_reload_needed;
00081 static unsigned int global_logmask = 0xFFFF;
00082 static int queuelog_init;
00083 static int logger_initialized;
00084 static volatile int next_unique_callid; /* Used to assign unique call_ids to calls */
00085 static int display_callids;
00086 static void unique_callid_cleanup(void *data);
00087 
00088 struct ast_callid {
00089     int call_identifier; /* Numerical value of the call displayed in the logs */
00090 };
00091 
00092 AST_THREADSTORAGE_CUSTOM(unique_callid, NULL, unique_callid_cleanup);
00093 
00094 static enum rotatestrategy {
00095    SEQUENTIAL = 1 << 0,     /* Original method - create a new file, in order */
00096    ROTATE = 1 << 1,         /* Rotate all files, such that the oldest file has the highest suffix */
00097    TIMESTAMP = 1 << 2,      /* Append the epoch timestamp onto the end of the archived file */
00098 } rotatestrategy = SEQUENTIAL;
00099 
00100 static struct {
00101    unsigned int queue_log:1;
00102    unsigned int queue_log_to_file:1;
00103    unsigned int queue_adaptive_realtime:1;
00104 } logfiles = { 1 };
00105 
00106 static char hostname[MAXHOSTNAMELEN];
00107 
00108 enum logtypes {
00109    LOGTYPE_SYSLOG,
00110    LOGTYPE_FILE,
00111    LOGTYPE_CONSOLE,
00112 };
00113 
00114 struct logchannel {
00115    /*! What to log to this channel */
00116    unsigned int logmask;
00117    /*! If this channel is disabled or not */
00118    int disabled;
00119    /*! syslog facility */
00120    int facility;
00121    /*! Verbosity level. (-1 if use option_verbose for the level.) */
00122    int verbosity;
00123    /*! Type of log channel */
00124    enum logtypes type;
00125    /*! logfile logging file pointer */
00126    FILE *fileptr;
00127    /*! Filename */
00128    char filename[PATH_MAX];
00129    /*! field for linking to list */
00130    AST_LIST_ENTRY(logchannel) list;
00131    /*! Line number from configuration file */
00132    int lineno;
00133    /*! Components (levels) from last config load */
00134    char components[0];
00135 };
00136 
00137 static AST_RWLIST_HEAD_STATIC(logchannels, logchannel);
00138 
00139 enum logmsgtypes {
00140    LOGMSG_NORMAL = 0,
00141    LOGMSG_VERBOSE,
00142 };
00143 
00144 struct logmsg {
00145    enum logmsgtypes type;
00146    int level;
00147    int line;
00148    int lwp;
00149    struct ast_callid *callid;
00150    AST_DECLARE_STRING_FIELDS(
00151       AST_STRING_FIELD(date);
00152       AST_STRING_FIELD(file);
00153       AST_STRING_FIELD(function);
00154       AST_STRING_FIELD(message);
00155       AST_STRING_FIELD(level_name);
00156    );
00157    AST_LIST_ENTRY(logmsg) list;
00158 };
00159 
00160 static void logmsg_free(struct logmsg *msg)
00161 {
00162    if (msg->callid) {
00163       ast_callid_unref(msg->callid);
00164    }
00165    ast_free(msg);
00166 }
00167 
00168 static AST_LIST_HEAD_STATIC(logmsgs, logmsg);
00169 static pthread_t logthread = AST_PTHREADT_NULL;
00170 static ast_cond_t logcond;
00171 static int close_logger_thread = 0;
00172 
00173 static FILE *qlog;
00174 
00175 /*! \brief Logging channels used in the Asterisk logging system
00176  *
00177  * The first 16 levels are reserved for system usage, and the remaining
00178  * levels are reserved for usage by dynamic levels registered via
00179  * ast_logger_register_level.
00180  */
00181 
00182 /* Modifications to this array are protected by the rwlock in the
00183  * logchannels list.
00184  */
00185 
00186 static char *levels[NUMLOGLEVELS] = {
00187    "DEBUG",
00188    "---EVENT---",    /* no longer used */
00189    "NOTICE",
00190    "WARNING",
00191    "ERROR",
00192    "VERBOSE",
00193    "DTMF",
00194 };
00195 
00196 /*! \brief Colors used in the console for logging */
00197 static const int colors[NUMLOGLEVELS] = {
00198    COLOR_BRGREEN,
00199    COLOR_BRBLUE,     /* no longer used */
00200    COLOR_YELLOW,
00201    COLOR_BRRED,
00202    COLOR_RED,
00203    COLOR_GREEN,
00204    COLOR_BRGREEN,
00205    0,
00206    0,
00207    0,
00208    0,
00209    0,
00210    0,
00211    0,
00212    0,
00213    0,
00214    COLOR_BRBLUE,
00215    COLOR_BRBLUE,
00216    COLOR_BRBLUE,
00217    COLOR_BRBLUE,
00218    COLOR_BRBLUE,
00219    COLOR_BRBLUE,
00220    COLOR_BRBLUE,
00221    COLOR_BRBLUE,
00222    COLOR_BRBLUE,
00223    COLOR_BRBLUE,
00224    COLOR_BRBLUE,
00225    COLOR_BRBLUE,
00226    COLOR_BRBLUE,
00227    COLOR_BRBLUE,
00228    COLOR_BRBLUE,
00229    COLOR_BRBLUE,
00230 };
00231 
00232 AST_THREADSTORAGE(verbose_buf);
00233 #define VERBOSE_BUF_INIT_SIZE   256
00234 
00235 AST_THREADSTORAGE(log_buf);
00236 #define LOG_BUF_INIT_SIZE       256
00237 
00238 static void logger_queue_init(void);
00239 
00240 static void make_components(struct logchannel *chan)
00241 {
00242    char *w;
00243    unsigned int logmask = 0;
00244    char *stringp = ast_strdupa(chan->components);
00245    unsigned int x;
00246    int verb_level;
00247 
00248    /* Default to using option_verbose as the verbosity level of the logging channel.  */
00249    verb_level = -1;
00250 
00251    while ((w = strsep(&stringp, ","))) {
00252       w = ast_strip(w);
00253       if (ast_strlen_zero(w)) {
00254          continue;
00255       }
00256       if (!strcmp(w, "*")) {
00257          logmask = 0xFFFFFFFF;
00258       } else if (!strncasecmp(w, "verbose(", 8)) {
00259          if (levels[__LOG_VERBOSE] && sscanf(w + 8, "%30u)", &verb_level) == 1) {
00260             logmask |= (1 << __LOG_VERBOSE);
00261          }
00262       } else {
00263          for (x = 0; x < ARRAY_LEN(levels); ++x) {
00264             if (levels[x] && !strcasecmp(w, levels[x])) {
00265                logmask |= (1 << x);
00266                break;
00267             }
00268          }
00269       }
00270    }
00271    if (chan->type == LOGTYPE_CONSOLE) {
00272       /*
00273        * Force to use the root console verbose level so if the
00274        * user specified any verbose level then it does not interfere
00275        * with calculating the ast_verb_sys_level value.
00276        */
00277       chan->verbosity = -1;
00278    } else {
00279       chan->verbosity = verb_level;
00280    }
00281    chan->logmask = logmask;
00282 }
00283 
00284 static struct logchannel *make_logchannel(const char *channel, const char *components, int lineno)
00285 {
00286    struct logchannel *chan;
00287    char *facility;
00288    struct ast_tm tm;
00289    struct timeval now = ast_tvnow();
00290    char datestring[256];
00291 
00292    if (ast_strlen_zero(channel) || !(chan = ast_calloc(1, sizeof(*chan) + strlen(components) + 1)))
00293       return NULL;
00294 
00295    strcpy(chan->components, components);
00296    chan->lineno = lineno;
00297 
00298    if (!strcasecmp(channel, "console")) {
00299       chan->type = LOGTYPE_CONSOLE;
00300    } else if (!strncasecmp(channel, "syslog", 6)) {
00301       /*
00302       * syntax is:
00303       *  syslog.facility => level,level,level
00304       */
00305       facility = strchr(channel, '.');
00306       if (!facility++ || !facility) {
00307          facility = "local0";
00308       }
00309 
00310       chan->facility = ast_syslog_facility(facility);
00311 
00312       if (chan->facility < 0) {
00313          fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
00314          ast_free(chan);
00315          return NULL;
00316       }
00317 
00318       chan->type = LOGTYPE_SYSLOG;
00319       ast_copy_string(chan->filename, channel, sizeof(chan->filename));
00320       openlog("asterisk", LOG_PID, chan->facility);
00321    } else {
00322       const char *log_dir_prefix = "";
00323       const char *log_dir_separator = "";
00324 
00325       if (channel[0] != '/') {
00326          log_dir_prefix = ast_config_AST_LOG_DIR;
00327          log_dir_separator = "/";
00328       }
00329 
00330       if (!ast_strlen_zero(hostname)) {
00331          snprintf(chan->filename, sizeof(chan->filename), "%s%s%s.%s",
00332             log_dir_prefix, log_dir_separator, channel, hostname);
00333       } else {
00334          snprintf(chan->filename, sizeof(chan->filename), "%s%s%s",
00335             log_dir_prefix, log_dir_separator, channel);
00336       }
00337 
00338       if (!(chan->fileptr = fopen(chan->filename, "a"))) {
00339          /* Can't do real logging here since we're called with a lock
00340           * so log to any attached consoles */
00341          ast_console_puts_mutable("ERROR: Unable to open log file '", __LOG_ERROR);
00342          ast_console_puts_mutable(chan->filename, __LOG_ERROR);
00343          ast_console_puts_mutable("': ", __LOG_ERROR);
00344          ast_console_puts_mutable(strerror(errno), __LOG_ERROR);
00345          ast_console_puts_mutable("'\n", __LOG_ERROR);
00346          ast_free(chan);
00347          return NULL;
00348       } else {
00349          /* Create our date/time */
00350          ast_localtime(&now, &tm, NULL);
00351          ast_strftime(datestring, sizeof(datestring), dateformat, &tm);
00352 
00353          fprintf(chan->fileptr, "[%s] Asterisk %s built by %s @ %s on a %s running %s on %s\n",
00354             datestring, ast_get_version(), ast_build_user, ast_build_hostname,
00355             ast_build_machine, ast_build_os, ast_build_date);
00356          fflush(chan->fileptr);
00357       }
00358       chan->type = LOGTYPE_FILE;
00359    }
00360    make_components(chan);
00361 
00362    return chan;
00363 }
00364 
00365 static void init_logger_chain(int locked, const char *altconf)
00366 {
00367    struct logchannel *chan;
00368    struct ast_config *cfg;
00369    struct ast_variable *var;
00370    const char *s;
00371    struct ast_flags config_flags = { 0 };
00372 
00373    display_callids = 1;
00374 
00375    if (!(cfg = ast_config_load2(S_OR(altconf, "logger.conf"), "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
00376       return;
00377    }
00378 
00379    /* delete our list of log channels */
00380    if (!locked) {
00381       AST_RWLIST_WRLOCK(&logchannels);
00382    }
00383    while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list))) {
00384       ast_free(chan);
00385    }
00386    global_logmask = 0;
00387    if (!locked) {
00388       AST_RWLIST_UNLOCK(&logchannels);
00389    }
00390 
00391    errno = 0;
00392    /* close syslog */
00393    closelog();
00394 
00395    /* If no config file, we're fine, set default options. */
00396    if (!cfg) {
00397       if (errno) {
00398          fprintf(stderr, "Unable to open logger.conf: %s; default settings will be used.\n", strerror(errno));
00399       } else {
00400          fprintf(stderr, "Errors detected in logger.conf: see above; default settings will be used.\n");
00401       }
00402       if (!(chan = ast_calloc(1, sizeof(*chan)))) {
00403          return;
00404       }
00405       chan->type = LOGTYPE_CONSOLE;
00406       chan->logmask = __LOG_WARNING | __LOG_NOTICE | __LOG_ERROR;
00407       if (!locked) {
00408          AST_RWLIST_WRLOCK(&logchannels);
00409       }
00410       AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
00411       global_logmask |= chan->logmask;
00412       if (!locked) {
00413          AST_RWLIST_UNLOCK(&logchannels);
00414       }
00415       return;
00416    }
00417 
00418    if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
00419       if (ast_true(s)) {
00420          if (gethostname(hostname, sizeof(hostname) - 1)) {
00421             ast_copy_string(hostname, "unknown", sizeof(hostname));
00422             fprintf(stderr, "What box has no hostname???\n");
00423          }
00424       } else
00425          hostname[0] = '\0';
00426    } else
00427       hostname[0] = '\0';
00428    if ((s = ast_variable_retrieve(cfg, "general", "display_callids"))) {
00429       display_callids = ast_true(s);
00430    }
00431    if ((s = ast_variable_retrieve(cfg, "general", "dateformat")))
00432       ast_copy_string(dateformat, s, sizeof(dateformat));
00433    else
00434       ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
00435    if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) {
00436       logfiles.queue_log = ast_true(s);
00437    }
00438    if ((s = ast_variable_retrieve(cfg, "general", "queue_log_to_file"))) {
00439       logfiles.queue_log_to_file = ast_true(s);
00440    }
00441    if ((s = ast_variable_retrieve(cfg, "general", "queue_log_name"))) {
00442       ast_copy_string(queue_log_name, s, sizeof(queue_log_name));
00443    }
00444    if ((s = ast_variable_retrieve(cfg, "general", "exec_after_rotate"))) {
00445       ast_copy_string(exec_after_rotate, s, sizeof(exec_after_rotate));
00446    }
00447    if ((s = ast_variable_retrieve(cfg, "general", "rotatestrategy"))) {
00448       if (strcasecmp(s, "timestamp") == 0) {
00449          rotatestrategy = TIMESTAMP;
00450       } else if (strcasecmp(s, "rotate") == 0) {
00451          rotatestrategy = ROTATE;
00452       } else if (strcasecmp(s, "sequential") == 0) {
00453          rotatestrategy = SEQUENTIAL;
00454       } else {
00455          fprintf(stderr, "Unknown rotatestrategy: %s\n", s);
00456       }
00457    } else {
00458       if ((s = ast_variable_retrieve(cfg, "general", "rotatetimestamp"))) {
00459          rotatestrategy = ast_true(s) ? TIMESTAMP : SEQUENTIAL;
00460          fprintf(stderr, "rotatetimestamp option has been deprecated.  Please use rotatestrategy instead.\n");
00461       }
00462    }
00463 
00464    if (!locked) {
00465       AST_RWLIST_WRLOCK(&logchannels);
00466    }
00467    var = ast_variable_browse(cfg, "logfiles");
00468    for (; var; var = var->next) {
00469       if (!(chan = make_logchannel(var->name, var->value, var->lineno))) {
00470          /* Print error message directly to the consoles since the lock is held
00471           * and we don't want to unlock with the list partially built */
00472          ast_console_puts_mutable("ERROR: Unable to create log channel '", __LOG_ERROR);
00473          ast_console_puts_mutable(var->name, __LOG_ERROR);
00474          ast_console_puts_mutable("'\n", __LOG_ERROR);
00475          continue;
00476       }
00477       AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
00478       global_logmask |= chan->logmask;
00479    }
00480 
00481    if (qlog) {
00482       fclose(qlog);
00483       qlog = NULL;
00484    }
00485 
00486    if (!locked) {
00487       AST_RWLIST_UNLOCK(&logchannels);
00488    }
00489 
00490    ast_config_destroy(cfg);
00491 }
00492 
00493 void ast_child_verbose(int level, const char *fmt, ...)
00494 {
00495    char *msg = NULL, *emsg = NULL, *sptr, *eptr;
00496    va_list ap, aq;
00497    int size;
00498 
00499    va_start(ap, fmt);
00500    va_copy(aq, ap);
00501    if ((size = vsnprintf(msg, 0, fmt, ap)) < 0) {
00502       va_end(ap);
00503       va_end(aq);
00504       return;
00505    }
00506    va_end(ap);
00507 
00508    if (!(msg = ast_malloc(size + 1))) {
00509       va_end(aq);
00510       return;
00511    }
00512 
00513    vsnprintf(msg, size + 1, fmt, aq);
00514    va_end(aq);
00515 
00516    if (!(emsg = ast_malloc(size * 2 + 1))) {
00517       ast_free(msg);
00518       return;
00519    }
00520 
00521    for (sptr = msg, eptr = emsg; ; sptr++) {
00522       if (*sptr == '"') {
00523          *eptr++ = '\\';
00524       }
00525       *eptr++ = *sptr;
00526       if (*sptr == '\0') {
00527          break;
00528       }
00529    }
00530    ast_free(msg);
00531 
00532    fprintf(stdout, "verbose \"%s\" %d\n", emsg, level);
00533    fflush(stdout);
00534    ast_free(emsg);
00535 }
00536 
00537 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
00538 {
00539    va_list ap;
00540    struct timeval tv;
00541    struct ast_tm tm;
00542    char qlog_msg[8192];
00543    int qlog_len;
00544    char time_str[30];
00545 
00546    if (!logger_initialized) {
00547       /* You are too early.  We are not open yet! */
00548       return;
00549    }
00550    if (!queuelog_init) {
00551       AST_RWLIST_WRLOCK(&logchannels);
00552       if (!queuelog_init) {
00553          /*
00554           * We have delayed initializing the queue logging system so
00555           * preloaded realtime modules can get up.  We must initialize
00556           * now since someone is trying to log something.
00557           */
00558          logger_queue_init();
00559          queuelog_init = 1;
00560          AST_RWLIST_UNLOCK(&logchannels);
00561          ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
00562       } else {
00563          AST_RWLIST_UNLOCK(&logchannels);
00564       }
00565    }
00566 
00567    if (ast_check_realtime("queue_log")) {
00568       tv = ast_tvnow();
00569       ast_localtime(&tv, &tm, NULL);
00570       ast_strftime(time_str, sizeof(time_str), "%F %T.%6q", &tm);
00571       va_start(ap, fmt);
00572       vsnprintf(qlog_msg, sizeof(qlog_msg), fmt, ap);
00573       va_end(ap);
00574       if (logfiles.queue_adaptive_realtime) {
00575          AST_DECLARE_APP_ARGS(args,
00576             AST_APP_ARG(data)[5];
00577          );
00578          AST_NONSTANDARD_APP_ARGS(args, qlog_msg, '|');
00579          /* Ensure fields are large enough to receive data */
00580          ast_realtime_require_field("queue_log",
00581             "data1", RQ_CHAR, strlen(S_OR(args.data[0], "")),
00582             "data2", RQ_CHAR, strlen(S_OR(args.data[1], "")),
00583             "data3", RQ_CHAR, strlen(S_OR(args.data[2], "")),
00584             "data4", RQ_CHAR, strlen(S_OR(args.data[3], "")),
00585             "data5", RQ_CHAR, strlen(S_OR(args.data[4], "")),
00586             SENTINEL);
00587 
00588          /* Store the log */
00589          ast_store_realtime("queue_log", "time", time_str,
00590             "callid", callid,
00591             "queuename", queuename,
00592             "agent", agent,
00593             "event", event,
00594             "data1", S_OR(args.data[0], ""),
00595             "data2", S_OR(args.data[1], ""),
00596             "data3", S_OR(args.data[2], ""),
00597             "data4", S_OR(args.data[3], ""),
00598             "data5", S_OR(args.data[4], ""),
00599             SENTINEL);
00600       } else {
00601          ast_store_realtime("queue_log", "time", time_str,
00602             "callid", callid,
00603             "queuename", queuename,
00604             "agent", agent,
00605             "event", event,
00606             "data", qlog_msg,
00607             SENTINEL);
00608       }
00609 
00610       if (!logfiles.queue_log_to_file) {
00611          return;
00612       }
00613    }
00614 
00615    if (qlog) {
00616       va_start(ap, fmt);
00617       qlog_len = snprintf(qlog_msg, sizeof(qlog_msg), "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
00618       vsnprintf(qlog_msg + qlog_len, sizeof(qlog_msg) - qlog_len, fmt, ap);
00619       va_end(ap);
00620       AST_RWLIST_RDLOCK(&logchannels);
00621       if (qlog) {
00622          fprintf(qlog, "%s\n", qlog_msg);
00623          fflush(qlog);
00624       }
00625       AST_RWLIST_UNLOCK(&logchannels);
00626    }
00627 }
00628 
00629 static int rotate_file(const char *filename)
00630 {
00631    char old[PATH_MAX];
00632    char new[PATH_MAX];
00633    int x, y, which, found, res = 0, fd;
00634    char *suffixes[4] = { "", ".gz", ".bz2", ".Z" };
00635 
00636    switch (rotatestrategy) {
00637    case SEQUENTIAL:
00638       for (x = 0; ; x++) {
00639          snprintf(new, sizeof(new), "%s.%d", filename, x);
00640          fd = open(new, O_RDONLY);
00641          if (fd > -1)
00642             close(fd);
00643          else
00644             break;
00645       }
00646       if (rename(filename, new)) {
00647          fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
00648          res = -1;
00649       } else {
00650          filename = new;
00651       }
00652       break;
00653    case TIMESTAMP:
00654       snprintf(new, sizeof(new), "%s.%ld", filename, (long)time(NULL));
00655       if (rename(filename, new)) {
00656          fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
00657          res = -1;
00658       } else {
00659          filename = new;
00660       }
00661       break;
00662    case ROTATE:
00663       /* Find the next empty slot, including a possible suffix */
00664       for (x = 0; ; x++) {
00665          found = 0;
00666          for (which = 0; which < ARRAY_LEN(suffixes); which++) {
00667             snprintf(new, sizeof(new), "%s.%d%s", filename, x, suffixes[which]);
00668             fd = open(new, O_RDONLY);
00669             if (fd > -1) {
00670                close(fd);
00671                found = 1;
00672                break;
00673             }
00674          }
00675          if (!found) {
00676             break;
00677          }
00678       }
00679 
00680       /* Found an empty slot */
00681       for (y = x; y > 0; y--) {
00682          for (which = 0; which < ARRAY_LEN(suffixes); which++) {
00683             snprintf(old, sizeof(old), "%s.%d%s", filename, y - 1, suffixes[which]);
00684             fd = open(old, O_RDONLY);
00685             if (fd > -1) {
00686                /* Found the right suffix */
00687                close(fd);
00688                snprintf(new, sizeof(new), "%s.%d%s", filename, y, suffixes[which]);
00689                if (rename(old, new)) {
00690                   fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
00691                   res = -1;
00692                }
00693                break;
00694             }
00695          }
00696       }
00697 
00698       /* Finally, rename the current file */
00699       snprintf(new, sizeof(new), "%s.0", filename);
00700       if (rename(filename, new)) {
00701          fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
00702          res = -1;
00703       } else {
00704          filename = new;
00705       }
00706    }
00707 
00708    if (!ast_strlen_zero(exec_after_rotate)) {
00709       struct ast_channel *c = ast_dummy_channel_alloc();
00710       char buf[512];
00711 
00712       pbx_builtin_setvar_helper(c, "filename", filename);
00713       pbx_substitute_variables_helper(c, exec_after_rotate, buf, sizeof(buf));
00714       if (c) {
00715          c = ast_channel_unref(c);
00716       }
00717       if (ast_safe_system(buf) == -1) {
00718          ast_log(LOG_WARNING, "error executing '%s'\n", buf);
00719       }
00720    }
00721    return res;
00722 }
00723 
00724 /*!
00725  * \internal
00726  * \brief Start the realtime queue logging if configured.
00727  *
00728  * \retval TRUE if not to open queue log file.
00729  */
00730 static int logger_queue_rt_start(void)
00731 {
00732    if (ast_check_realtime("queue_log")) {
00733       if (!ast_realtime_require_field("queue_log",
00734          "time", RQ_DATETIME, 26,
00735          "data1", RQ_CHAR, 20,
00736          "data2", RQ_CHAR, 20,
00737          "data3", RQ_CHAR, 20,
00738          "data4", RQ_CHAR, 20,
00739          "data5", RQ_CHAR, 20,
00740          SENTINEL)) {
00741          logfiles.queue_adaptive_realtime = 1;
00742       } else {
00743          logfiles.queue_adaptive_realtime = 0;
00744       }
00745 
00746       if (!logfiles.queue_log_to_file) {
00747          /* Don't open the log file. */
00748          return 1;
00749       }
00750    }
00751    return 0;
00752 }
00753 
00754 /*!
00755  * \internal
00756  * \brief Rotate the queue log file and restart.
00757  *
00758  * \param queue_rotate Log queue rotation mode.
00759  *
00760  * \note Assumes logchannels is write locked on entry.
00761  *
00762  * \retval 0 on success.
00763  * \retval -1 on error.
00764  */
00765 static int logger_queue_restart(int queue_rotate)
00766 {
00767    int res = 0;
00768    char qfname[PATH_MAX];
00769 
00770    if (logger_queue_rt_start()) {
00771       return res;
00772    }
00773 
00774    snprintf(qfname, sizeof(qfname), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
00775    if (qlog) {
00776       /* Just in case it was still open. */
00777       fclose(qlog);
00778       qlog = NULL;
00779    }
00780    if (queue_rotate) {
00781       rotate_file(qfname);
00782    }
00783 
00784    /* Open the log file. */
00785    qlog = fopen(qfname, "a");
00786    if (!qlog) {
00787       ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
00788       res = -1;
00789    }
00790    return res;
00791 }
00792 
00793 static int reload_logger(int rotate, const char *altconf)
00794 {
00795    int queue_rotate = rotate;
00796    struct logchannel *f;
00797    int res = 0;
00798 
00799    AST_RWLIST_WRLOCK(&logchannels);
00800 
00801    if (qlog) {
00802       if (rotate < 0) {
00803          /* Check filesize - this one typically doesn't need an auto-rotate */
00804          if (ftello(qlog) > 0x40000000) { /* Arbitrarily, 1 GB */
00805             fclose(qlog);
00806             qlog = NULL;
00807          } else {
00808             queue_rotate = 0;
00809          }
00810       } else {
00811          fclose(qlog);
00812          qlog = NULL;
00813       }
00814    } else {
00815       queue_rotate = 0;
00816    }
00817 
00818    ast_mkdir(ast_config_AST_LOG_DIR, 0777);
00819 
00820    AST_RWLIST_TRAVERSE(&logchannels, f, list) {
00821       if (f->disabled) {
00822          f->disabled = 0;  /* Re-enable logging at reload */
00823          /*** DOCUMENTATION
00824             <managerEventInstance>
00825                <synopsis>Raised when a logging channel is re-enabled after a reload operation.</synopsis>
00826                <syntax>
00827                   <parameter name="Channel">
00828                      <para>The name of the logging channel.</para>
00829                   </parameter>
00830                </syntax>
00831             </managerEventInstance>
00832          ***/
00833          manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
00834       }
00835       if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
00836          int rotate_this = 0;
00837          if (ftello(f->fileptr) > 0x40000000) { /* Arbitrarily, 1 GB */
00838             /* Be more proactive about rotating massive log files */
00839             rotate_this = 1;
00840          }
00841          fclose(f->fileptr);  /* Close file */
00842          f->fileptr = NULL;
00843          if (rotate || rotate_this) {
00844             rotate_file(f->filename);
00845          }
00846       }
00847    }
00848 
00849    filesize_reload_needed = 0;
00850 
00851    init_logger_chain(1 /* locked */, altconf);
00852 
00853    ast_unload_realtime("queue_log");
00854    if (logfiles.queue_log) {
00855       res = logger_queue_restart(queue_rotate);
00856       AST_RWLIST_UNLOCK(&logchannels);
00857       ast_verb_update();
00858       ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
00859       ast_verb(1, "Asterisk Queue Logger restarted\n");
00860    } else {
00861       AST_RWLIST_UNLOCK(&logchannels);
00862       ast_verb_update();
00863    }
00864 
00865    return res;
00866 }
00867 
00868 /*! \brief Reload the logger module without rotating log files (also used from loader.c during
00869    a full Asterisk reload) */
00870 int logger_reload(void)
00871 {
00872    if (reload_logger(0, NULL)) {
00873       return RESULT_FAILURE;
00874    }
00875    return RESULT_SUCCESS;
00876 }
00877 
00878 static char *handle_logger_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00879 {
00880    switch (cmd) {
00881    case CLI_INIT:
00882       e->command = "logger reload";
00883       e->usage =
00884          "Usage: logger reload [<alt-conf>]\n"
00885          "       Reloads the logger subsystem state.  Use after restarting syslogd(8) if you are using syslog logging.\n";
00886       return NULL;
00887    case CLI_GENERATE:
00888       return NULL;
00889    }
00890    if (reload_logger(0, a->argc == 3 ? a->argv[2] : NULL)) {
00891       ast_cli(a->fd, "Failed to reload the logger\n");
00892       return CLI_FAILURE;
00893    }
00894    return CLI_SUCCESS;
00895 }
00896 
00897 static char *handle_logger_rotate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00898 {
00899    switch (cmd) {
00900    case CLI_INIT:
00901       e->command = "logger rotate";
00902       e->usage =
00903          "Usage: logger rotate\n"
00904          "       Rotates and Reopens the log files.\n";
00905       return NULL;
00906    case CLI_GENERATE:
00907       return NULL;
00908    }
00909    if (reload_logger(1, NULL)) {
00910       ast_cli(a->fd, "Failed to reload the logger and rotate log files\n");
00911       return CLI_FAILURE;
00912    }
00913    return CLI_SUCCESS;
00914 }
00915 
00916 static char *handle_logger_set_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00917 {
00918    int x;
00919    int state;
00920    int level = -1;
00921 
00922    switch (cmd) {
00923    case CLI_INIT:
00924       e->command = "logger set level {DEBUG|NOTICE|WARNING|ERROR|VERBOSE|DTMF} {on|off}";
00925       e->usage =
00926          "Usage: logger set level {DEBUG|NOTICE|WARNING|ERROR|VERBOSE|DTMF} {on|off}\n"
00927          "       Set a specific log level to enabled/disabled for this console.\n";
00928       return NULL;
00929    case CLI_GENERATE:
00930       return NULL;
00931    }
00932 
00933    if (a->argc < 5)
00934       return CLI_SHOWUSAGE;
00935 
00936    AST_RWLIST_WRLOCK(&logchannels);
00937 
00938    for (x = 0; x < ARRAY_LEN(levels); x++) {
00939       if (levels[x] && !strcasecmp(a->argv[3], levels[x])) {
00940          level = x;
00941          break;
00942       }
00943    }
00944 
00945    AST_RWLIST_UNLOCK(&logchannels);
00946 
00947    state = ast_true(a->argv[4]) ? 1 : 0;
00948 
00949    if (level != -1) {
00950       ast_console_toggle_loglevel(a->fd, level, state);
00951       ast_cli(a->fd, "Logger status for '%s' has been set to '%s'.\n", levels[level], state ? "on" : "off");
00952    } else
00953       return CLI_SHOWUSAGE;
00954 
00955    return CLI_SUCCESS;
00956 }
00957 
00958 /*! \brief CLI command to show logging system configuration */
00959 static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00960 {
00961 #define FORMATL   "%-35.35s %-8.8s %-9.9s "
00962    struct logchannel *chan;
00963    switch (cmd) {
00964    case CLI_INIT:
00965       e->command = "logger show channels";
00966       e->usage =
00967          "Usage: logger show channels\n"
00968          "       List configured logger channels.\n";
00969       return NULL;
00970    case CLI_GENERATE:
00971       return NULL;
00972    }
00973    ast_cli(a->fd, FORMATL, "Channel", "Type", "Status");
00974    ast_cli(a->fd, "Configuration\n");
00975    ast_cli(a->fd, FORMATL, "-------", "----", "------");
00976    ast_cli(a->fd, "-------------\n");
00977    AST_RWLIST_RDLOCK(&logchannels);
00978    AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
00979       unsigned int level;
00980 
00981       ast_cli(a->fd, FORMATL, chan->filename, chan->type == LOGTYPE_CONSOLE ? "Console" : (chan->type == LOGTYPE_SYSLOG ? "Syslog" : "File"),
00982          chan->disabled ? "Disabled" : "Enabled");
00983       ast_cli(a->fd, " - ");
00984       for (level = 0; level < ARRAY_LEN(levels); level++) {
00985          if ((chan->logmask & (1 << level)) && levels[level]) {
00986             ast_cli(a->fd, "%s ", levels[level]);
00987          }
00988       }
00989       ast_cli(a->fd, "\n");
00990    }
00991    AST_RWLIST_UNLOCK(&logchannels);
00992    ast_cli(a->fd, "\n");
00993 
00994    return CLI_SUCCESS;
00995 }
00996 
00997 struct verb {
00998    void (*verboser)(const char *string);
00999    AST_LIST_ENTRY(verb) list;
01000 };
01001 
01002 static AST_RWLIST_HEAD_STATIC(verbosers, verb);
01003 
01004 static struct ast_cli_entry cli_logger[] = {
01005    AST_CLI_DEFINE(handle_logger_show_channels, "List configured log channels"),
01006    AST_CLI_DEFINE(handle_logger_reload, "Reopens the log files"),
01007    AST_CLI_DEFINE(handle_logger_rotate, "Rotates and reopens the log files"),
01008    AST_CLI_DEFINE(handle_logger_set_level, "Enables/Disables a specific logging level for this console"),
01009 };
01010 
01011 static void _handle_SIGXFSZ(int sig)
01012 {
01013    /* Indicate need to reload */
01014    filesize_reload_needed = 1;
01015 }
01016 
01017 static struct sigaction handle_SIGXFSZ = {
01018    .sa_handler = _handle_SIGXFSZ,
01019    .sa_flags = SA_RESTART,
01020 };
01021 
01022 static void ast_log_vsyslog(struct logmsg *msg)
01023 {
01024    char buf[BUFSIZ];
01025    int syslog_level = ast_syslog_priority_from_loglevel(msg->level);
01026    char call_identifier_str[13];
01027 
01028    if (msg->callid) {
01029       snprintf(call_identifier_str, sizeof(call_identifier_str), "[C-%08x]", msg->callid->call_identifier);
01030    } else {
01031       call_identifier_str[0] = '\0';
01032    }
01033 
01034    if (syslog_level < 0) {
01035       /* we are locked here, so cannot ast_log() */
01036       fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", msg->level);
01037       return;
01038    }
01039 
01040    snprintf(buf, sizeof(buf), "%s[%d]%s: %s:%d in %s: %s",
01041        levels[msg->level], msg->lwp, call_identifier_str, msg->file, msg->line, msg->function, msg->message);
01042 
01043    term_strip(buf, buf, strlen(buf) + 1);
01044    syslog(syslog_level, "%s", buf);
01045 }
01046 
01047 /*! \brief Print a normal log message to the channels */
01048 static void logger_print_normal(struct logmsg *logmsg)
01049 {
01050    struct logchannel *chan = NULL;
01051    char buf[BUFSIZ];
01052    struct verb *v = NULL;
01053    int level = 0;
01054 
01055    if (logmsg->level == __LOG_VERBOSE) {
01056       char *tmpmsg = ast_strdupa(logmsg->message + 1);
01057 
01058       level = VERBOSE_MAGIC2LEVEL(logmsg->message);
01059 
01060       /* Iterate through the list of verbosers and pass them the log message string */
01061       AST_RWLIST_RDLOCK(&verbosers);
01062       AST_RWLIST_TRAVERSE(&verbosers, v, list)
01063          v->verboser(logmsg->message);
01064       AST_RWLIST_UNLOCK(&verbosers);
01065       ast_string_field_set(logmsg, message, tmpmsg);
01066    }
01067 
01068    AST_RWLIST_RDLOCK(&logchannels);
01069 
01070    if (!AST_RWLIST_EMPTY(&logchannels)) {
01071       AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
01072          char call_identifier_str[13];
01073 
01074          if (logmsg->callid) {
01075             snprintf(call_identifier_str, sizeof(call_identifier_str), "[C-%08x]", logmsg->callid->call_identifier);
01076          } else {
01077             call_identifier_str[0] = '\0';
01078          }
01079 
01080 
01081          /* If the channel is disabled, then move on to the next one */
01082          if (chan->disabled) {
01083             continue;
01084          }
01085          if (logmsg->level == __LOG_VERBOSE
01086             && (((chan->verbosity < 0) ? option_verbose : chan->verbosity)) < level) {
01087             continue;
01088          }
01089 
01090          /* Check syslog channels */
01091          if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << logmsg->level))) {
01092             ast_log_vsyslog(logmsg);
01093          /* Console channels */
01094          } else if (chan->type == LOGTYPE_CONSOLE && (chan->logmask & (1 << logmsg->level))) {
01095             char linestr[128];
01096             char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
01097 
01098             /* If the level is verbose, then skip it */
01099             if (logmsg->level == __LOG_VERBOSE)
01100                continue;
01101 
01102             /* Turn the numerical line number into a string */
01103             snprintf(linestr, sizeof(linestr), "%d", logmsg->line);
01104             /* Build string to print out */
01105             snprintf(buf, sizeof(buf), "[%s] %s[%d]%s: %s:%s %s: %s",
01106                 logmsg->date,
01107                 term_color(tmp1, logmsg->level_name, colors[logmsg->level], 0, sizeof(tmp1)),
01108                 logmsg->lwp,
01109                 call_identifier_str,
01110                 term_color(tmp2, logmsg->file, COLOR_BRWHITE, 0, sizeof(tmp2)),
01111                 term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
01112                 term_color(tmp4, logmsg->function, COLOR_BRWHITE, 0, sizeof(tmp4)),
01113                 logmsg->message);
01114             /* Print out */
01115             ast_console_puts_mutable(buf, logmsg->level);
01116          /* File channels */
01117          } else if (chan->type == LOGTYPE_FILE && (chan->logmask & (1 << logmsg->level))) {
01118             int res = 0;
01119 
01120             /* If no file pointer exists, skip it */
01121             if (!chan->fileptr) {
01122                continue;
01123             }
01124 
01125             /* Print out to the file */
01126             res = fprintf(chan->fileptr, "[%s] %s[%d]%s %s: %s",
01127                      logmsg->date, logmsg->level_name, logmsg->lwp, call_identifier_str,
01128                      logmsg->file, term_strip(buf, logmsg->message, BUFSIZ));
01129             if (res <= 0 && !ast_strlen_zero(logmsg->message)) {
01130                fprintf(stderr, "**** Asterisk Logging Error: ***********\n");
01131                if (errno == ENOMEM || errno == ENOSPC)
01132                   fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename);
01133                else
01134                   fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno));
01135                /*** DOCUMENTATION
01136                   <managerEventInstance>
01137                      <synopsis>Raised when a logging channel is disabled.</synopsis>
01138                      <syntax>
01139                         <parameter name="Channel">
01140                            <para>The name of the logging channel.</para>
01141                         </parameter>
01142                      </syntax>
01143                   </managerEventInstance>
01144                ***/
01145                manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno));
01146                chan->disabled = 1;
01147             } else if (res > 0) {
01148                fflush(chan->fileptr);
01149             }
01150          }
01151       }
01152    } else if (logmsg->level != __LOG_VERBOSE) {
01153       fputs(logmsg->message, stdout);
01154    }
01155 
01156    AST_RWLIST_UNLOCK(&logchannels);
01157 
01158    /* If we need to reload because of the file size, then do so */
01159    if (filesize_reload_needed) {
01160       reload_logger(-1, NULL);
01161       ast_verb(1, "Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
01162    }
01163 
01164    return;
01165 }
01166 
01167 /*! \brief Actual logging thread */
01168 static void *logger_thread(void *data)
01169 {
01170    struct logmsg *next = NULL, *msg = NULL;
01171 
01172    for (;;) {
01173       /* We lock the message list, and see if any message exists... if not we wait on the condition to be signalled */
01174       AST_LIST_LOCK(&logmsgs);
01175       if (AST_LIST_EMPTY(&logmsgs)) {
01176          if (close_logger_thread) {
01177             AST_LIST_UNLOCK(&logmsgs);
01178             break;
01179          } else {
01180             ast_cond_wait(&logcond, &logmsgs.lock);
01181          }
01182       }
01183       next = AST_LIST_FIRST(&logmsgs);
01184       AST_LIST_HEAD_INIT_NOLOCK(&logmsgs);
01185       AST_LIST_UNLOCK(&logmsgs);
01186 
01187       /* Otherwise go through and process each message in the order added */
01188       while ((msg = next)) {
01189          /* Get the next entry now so that we can free our current structure later */
01190          next = AST_LIST_NEXT(msg, list);
01191 
01192          /* Depending on the type, send it to the proper function */
01193          logger_print_normal(msg);
01194 
01195          /* Free the data since we are done */
01196          logmsg_free(msg);
01197       }
01198    }
01199 
01200    return NULL;
01201 }
01202 
01203 /*!
01204  * \internal
01205  * \brief Initialize the logger queue.
01206  *
01207  * \note Assumes logchannels is write locked on entry.
01208  *
01209  * \return Nothing
01210  */
01211 static void logger_queue_init(void)
01212 {
01213    ast_unload_realtime("queue_log");
01214    if (logfiles.queue_log) {
01215       char qfname[PATH_MAX];
01216 
01217       if (logger_queue_rt_start()) {
01218          return;
01219       }
01220 
01221       /* Open the log file. */
01222       snprintf(qfname, sizeof(qfname), "%s/%s", ast_config_AST_LOG_DIR,
01223          queue_log_name);
01224       if (qlog) {
01225          /* Just in case it was already open. */
01226          fclose(qlog);
01227       }
01228       qlog = fopen(qfname, "a");
01229       if (!qlog) {
01230          ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
01231       }
01232    }
01233 }
01234 
01235 int init_logger(void)
01236 {
01237    /* auto rotate if sig SIGXFSZ comes a-knockin */
01238    sigaction(SIGXFSZ, &handle_SIGXFSZ, NULL);
01239 
01240    /* Re-initialize the logmsgs mutex.  The recursive mutex can be accessed prior
01241     * to Asterisk being forked into the background, which can cause the thread
01242     * ID tracked by the underlying pthread mutex to be different than the ID of
01243     * the thread that unlocks the mutex.  Since init_logger is called after the
01244     * fork, it is safe to initialize the mutex here for future accesses.
01245     */
01246    ast_mutex_destroy(&logmsgs.lock);
01247    ast_mutex_init(&logmsgs.lock);
01248    ast_cond_init(&logcond, NULL);
01249 
01250    /* start logger thread */
01251    if (ast_pthread_create(&logthread, NULL, logger_thread, NULL) < 0) {
01252       ast_cond_destroy(&logcond);
01253       return -1;
01254    }
01255 
01256    /* register the logger cli commands */
01257    ast_cli_register_multiple(cli_logger, ARRAY_LEN(cli_logger));
01258 
01259    ast_mkdir(ast_config_AST_LOG_DIR, 0777);
01260 
01261    /* create log channels */
01262    init_logger_chain(0 /* locked */, NULL);
01263    ast_verb_update();
01264    logger_initialized = 1;
01265 
01266    return 0;
01267 }
01268 
01269 void close_logger(void)
01270 {
01271    struct logchannel *f = NULL;
01272    struct verb *cur = NULL;
01273 
01274    ast_cli_unregister_multiple(cli_logger, ARRAY_LEN(cli_logger));
01275 
01276    logger_initialized = 0;
01277 
01278    /* Stop logger thread */
01279    AST_LIST_LOCK(&logmsgs);
01280    close_logger_thread = 1;
01281    ast_cond_signal(&logcond);
01282    AST_LIST_UNLOCK(&logmsgs);
01283 
01284    if (logthread != AST_PTHREADT_NULL)
01285       pthread_join(logthread, NULL);
01286 
01287    AST_RWLIST_WRLOCK(&verbosers);
01288    while ((cur = AST_LIST_REMOVE_HEAD(&verbosers, list))) {
01289       ast_free(cur);
01290    }
01291    AST_RWLIST_UNLOCK(&verbosers);
01292 
01293    AST_RWLIST_WRLOCK(&logchannels);
01294 
01295    if (qlog) {
01296       fclose(qlog);
01297       qlog = NULL;
01298    }
01299 
01300    while ((f = AST_LIST_REMOVE_HEAD(&logchannels, list))) {
01301       if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
01302          fclose(f->fileptr);
01303          f->fileptr = NULL;
01304       }
01305       ast_free(f);
01306    }
01307 
01308    closelog(); /* syslog */
01309 
01310    AST_RWLIST_UNLOCK(&logchannels);
01311 }
01312 
01313 void ast_callid_strnprint(char *buffer, size_t buffer_size, struct ast_callid *callid)
01314 {
01315    snprintf(buffer, buffer_size, "[C-%08x]", callid->call_identifier);
01316 }
01317 
01318 struct ast_callid *ast_create_callid(void)
01319 {
01320    struct ast_callid *call;
01321 
01322    call = ao2_alloc_options(sizeof(*call), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
01323    if (!call) {
01324       ast_log(LOG_ERROR, "Could not allocate callid struct.\n");
01325       return NULL;
01326    }
01327 
01328    call->call_identifier = ast_atomic_fetchadd_int(&next_unique_callid, +1);
01329 #ifdef TEST_FRAMEWORK
01330    ast_debug(3, "CALL_ID [C-%08x] created by thread.\n", call->call_identifier);
01331 #endif
01332    return call;
01333 }
01334 
01335 struct ast_callid *ast_read_threadstorage_callid(void)
01336 {
01337    struct ast_callid **callid;
01338 
01339    callid = ast_threadstorage_get(&unique_callid, sizeof(*callid));
01340    if (callid && *callid) {
01341       ast_callid_ref(*callid);
01342       return *callid;
01343    }
01344 
01345    return NULL;
01346 
01347 }
01348 
01349 int ast_callid_threadassoc_change(struct ast_callid *callid)
01350 {
01351    struct ast_callid **id = ast_threadstorage_get(&unique_callid, sizeof(*id));
01352 
01353    if (!id) {
01354       ast_log(LOG_ERROR, "Failed to allocate thread storage.\n");
01355       return -1;
01356    }
01357 
01358    if (*id && (*id != callid)) {
01359 #ifdef TEST_FRAMEWORK
01360       ast_debug(3, "CALL_ID [C-%08x] being removed from thread.\n", (*id)->call_identifier);
01361 #endif
01362       *id = ast_callid_unref(*id);
01363       *id = NULL;
01364    }
01365 
01366    if (!(*id) && callid) {
01367       /* callid will be unreffed at thread destruction */
01368       ast_callid_ref(callid);
01369       *id = callid;
01370 #ifdef TEST_FRAMEWORK
01371       ast_debug(3, "CALL_ID [C-%08x] bound to thread.\n", callid->call_identifier);
01372 #endif
01373    }
01374 
01375    return 0;
01376 }
01377 
01378 int ast_callid_threadassoc_add(struct ast_callid *callid)
01379 {
01380    struct ast_callid **pointing;
01381 
01382    pointing = ast_threadstorage_get(&unique_callid, sizeof(*pointing));
01383    if (!(pointing)) {
01384       ast_log(LOG_ERROR, "Failed to allocate thread storage.\n");
01385       return -1;
01386    }
01387 
01388    if (!(*pointing)) {
01389       /* callid will be unreffed at thread destruction */
01390       ast_callid_ref(callid);
01391       *pointing = callid;
01392 #ifdef TEST_FRAMEWORK
01393       ast_debug(3, "CALL_ID [C-%08x] bound to thread.\n", callid->call_identifier);
01394 #endif
01395    } else {
01396       ast_log(LOG_WARNING, "Attempted to ast_callid_threadassoc_add on thread already associated with a callid.\n");
01397       return 1;
01398    }
01399 
01400    return 0;
01401 }
01402 
01403 int ast_callid_threadassoc_remove(void)
01404 {
01405    struct ast_callid **pointing;
01406 
01407    pointing = ast_threadstorage_get(&unique_callid, sizeof(*pointing));
01408    if (!(pointing)) {
01409       ast_log(LOG_ERROR, "Failed to allocate thread storage.\n");
01410       return -1;
01411    }
01412 
01413    if (!(*pointing)) {
01414       ast_log(LOG_ERROR, "Tried to clean callid thread storage with no callid in thread storage.\n");
01415       return -1;
01416    } else {
01417 #ifdef TEST_FRAMEWORK
01418       ast_debug(3, "Call_ID [C-%08x] being removed from thread.\n", (*pointing)->call_identifier);
01419 #endif
01420       *pointing = ast_callid_unref(*pointing);
01421       return 0;
01422    }
01423 }
01424 
01425 int ast_callid_threadstorage_auto(struct ast_callid **callid)
01426 {
01427    struct ast_callid *tmp;
01428 
01429    /* Start by trying to see if a callid is available from thread storage */
01430    tmp = ast_read_threadstorage_callid();
01431    if (tmp) {
01432       *callid = tmp;
01433       return 0;
01434    }
01435 
01436    /* If that failed, try to create a new one and bind it. */
01437    tmp = ast_create_callid();
01438    if (tmp) {
01439       ast_callid_threadassoc_add(tmp);
01440       *callid = tmp;
01441       return 1;
01442    }
01443 
01444    /* If neither worked, then something must have gone wrong. */
01445    return -1;
01446 }
01447 
01448 void ast_callid_threadstorage_auto_clean(struct ast_callid *callid, int callid_created)
01449 {
01450    if (callid) {
01451       /* If the callid was created rather than simply grabbed from the thread storage, we need to unbind here. */
01452       if (callid_created == 1) {
01453          ast_callid_threadassoc_remove();
01454       }
01455       callid = ast_callid_unref(callid);
01456    }
01457 }
01458 
01459 /*!
01460  * \internal
01461  * \brief thread storage cleanup function for unique_callid
01462  */
01463 static void unique_callid_cleanup(void *data)
01464 {
01465    struct ast_callid **callid = data;
01466 
01467    if (*callid) {
01468       ast_callid_unref(*callid);
01469    }
01470 
01471    ast_free(data);
01472 }
01473 
01474 /*!
01475  * \brief send log messages to syslog and/or the console
01476  */
01477 static void __attribute__((format(printf, 6, 0))) ast_log_full(int level, const char *file, int line, const char *function, struct ast_callid *callid, const char *fmt, va_list ap)
01478 {
01479    struct logmsg *logmsg = NULL;
01480    struct ast_str *buf = NULL;
01481    struct ast_tm tm;
01482    struct timeval now = ast_tvnow();
01483    int res = 0;
01484    char datestring[256];
01485 
01486    if (!(buf = ast_str_thread_get(&log_buf, LOG_BUF_INIT_SIZE)))
01487       return;
01488 
01489    if (level != __LOG_VERBOSE && AST_RWLIST_EMPTY(&logchannels)) {
01490       /*
01491        * we don't have the logger chain configured yet,
01492        * so just log to stdout
01493        */
01494       int result;
01495       result = ast_str_set_va(&buf, BUFSIZ, fmt, ap); /* XXX BUFSIZ ? */
01496       if (result != AST_DYNSTR_BUILD_FAILED) {
01497          term_filter_escapes(ast_str_buffer(buf));
01498          fputs(ast_str_buffer(buf), stdout);
01499       }
01500       return;
01501    }
01502 
01503    /* Ignore anything that never gets logged anywhere */
01504    if (level != __LOG_VERBOSE && !(global_logmask & (1 << level)))
01505       return;
01506 
01507    /* Build string */
01508    res = ast_str_set_va(&buf, BUFSIZ, fmt, ap);
01509 
01510    /* If the build failed, then abort and free this structure */
01511    if (res == AST_DYNSTR_BUILD_FAILED)
01512       return;
01513 
01514    /* Create a new logging message */
01515    if (!(logmsg = ast_calloc_with_stringfields(1, struct logmsg, res + 128)))
01516       return;
01517 
01518    /* Copy string over */
01519    ast_string_field_set(logmsg, message, ast_str_buffer(buf));
01520 
01521    /* Set type */
01522    if (level == __LOG_VERBOSE) {
01523       logmsg->type = LOGMSG_VERBOSE;
01524    } else {
01525       logmsg->type = LOGMSG_NORMAL;
01526    }
01527 
01528    if (display_callids && callid) {
01529       logmsg->callid = ast_callid_ref(callid);
01530       /* callid will be unreffed at logmsg destruction */
01531    }
01532 
01533    /* Create our date/time */
01534    ast_localtime(&now, &tm, NULL);
01535    ast_strftime(datestring, sizeof(datestring), dateformat, &tm);
01536    ast_string_field_set(logmsg, date, datestring);
01537 
01538    /* Copy over data */
01539    logmsg->level = level;
01540    logmsg->line = line;
01541    ast_string_field_set(logmsg, level_name, levels[level]);
01542    ast_string_field_set(logmsg, file, file);
01543    ast_string_field_set(logmsg, function, function);
01544    logmsg->lwp = ast_get_tid();
01545 
01546    /* If the logger thread is active, append it to the tail end of the list - otherwise skip that step */
01547    if (logthread != AST_PTHREADT_NULL) {
01548       AST_LIST_LOCK(&logmsgs);
01549       if (close_logger_thread) {
01550          /* Logger is either closing or closed.  We cannot log this message. */
01551          logmsg_free(logmsg);
01552       } else {
01553          AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
01554          ast_cond_signal(&logcond);
01555       }
01556       AST_LIST_UNLOCK(&logmsgs);
01557    } else {
01558       logger_print_normal(logmsg);
01559       logmsg_free(logmsg);
01560    }
01561 }
01562 
01563 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
01564 {
01565    struct ast_callid *callid;
01566    va_list ap;
01567 
01568    callid = ast_read_threadstorage_callid();
01569 
01570    va_start(ap, fmt);
01571    if (level == __LOG_VERBOSE) {
01572       __ast_verbose_ap(file, line, function, 0, callid, fmt, ap);
01573    } else {
01574       ast_log_full(level, file, line, function, callid, fmt, ap);
01575    }
01576    va_end(ap);
01577 
01578    if (callid) {
01579       ast_callid_unref(callid);
01580    }
01581 }
01582 
01583 void ast_log_callid(int level, const char *file, int line, const char *function, struct ast_callid *callid, const char *fmt, ...)
01584 {
01585    va_list ap;
01586    va_start(ap, fmt);
01587    ast_log_full(level, file, line, function, callid, fmt, ap);
01588    va_end(ap);
01589 }
01590 
01591 #ifdef HAVE_BKTR
01592 
01593 struct ast_bt *ast_bt_create(void)
01594 {
01595    struct ast_bt *bt = ast_calloc(1, sizeof(*bt));
01596    if (!bt) {
01597       ast_log(LOG_ERROR, "Unable to allocate memory for backtrace structure!\n");
01598       return NULL;
01599    }
01600 
01601    bt->alloced = 1;
01602 
01603    ast_bt_get_addresses(bt);
01604 
01605    return bt;
01606 }
01607 
01608 int ast_bt_get_addresses(struct ast_bt *bt)
01609 {
01610    bt->num_frames = backtrace(bt->addresses, AST_MAX_BT_FRAMES);
01611 
01612    return 0;
01613 }
01614 
01615 void *ast_bt_destroy(struct ast_bt *bt)
01616 {
01617    if (bt->alloced) {
01618       ast_free(bt);
01619    }
01620 
01621    return NULL;
01622 }
01623 
01624 char **ast_bt_get_symbols(void **addresses, size_t num_frames)
01625 {
01626    char **strings;
01627 #if defined(BETTER_BACKTRACES)
01628    int stackfr;
01629    bfd *bfdobj;           /* bfd.h */
01630    Dl_info dli;           /* dlfcn.h */
01631    long allocsize;
01632    asymbol **syms = NULL; /* bfd.h */
01633    bfd_vma offset;        /* bfd.h */
01634    const char *lastslash;
01635    asection *section;
01636    const char *file, *func;
01637    unsigned int line;
01638    char address_str[128];
01639    char msg[1024];
01640    size_t strings_size;
01641    size_t *eachlen;
01642 #endif
01643 
01644 #if defined(BETTER_BACKTRACES)
01645    strings_size = num_frames * sizeof(*strings);
01646 
01647    eachlen = ast_calloc(num_frames, sizeof(*eachlen));
01648    strings = ast_std_calloc(num_frames, sizeof(*strings));
01649    if (!eachlen || !strings) {
01650       ast_free(eachlen);
01651       ast_std_free(strings);
01652       return NULL;
01653    }
01654 
01655    for (stackfr = 0; stackfr < num_frames; stackfr++) {
01656       int found = 0, symbolcount;
01657 
01658       msg[0] = '\0';
01659 
01660       if (!dladdr(addresses[stackfr], &dli)) {
01661          continue;
01662       }
01663 
01664       if (strcmp(dli.dli_fname, "asterisk") == 0) {
01665          char asteriskpath[256];
01666 
01667          if (!(dli.dli_fname = ast_utils_which("asterisk", asteriskpath, sizeof(asteriskpath)))) {
01668             /* This will fail to find symbols */
01669             ast_debug(1, "Failed to find asterisk binary for debug symbols.\n");
01670             dli.dli_fname = "asterisk";
01671          }
01672       }
01673 
01674       lastslash = strrchr(dli.dli_fname, '/');
01675       if ((bfdobj = bfd_openr(dli.dli_fname, NULL)) &&
01676          bfd_check_format(bfdobj, bfd_object) &&
01677          (allocsize = bfd_get_symtab_upper_bound(bfdobj)) > 0 &&
01678          (syms = ast_malloc(allocsize)) &&
01679          (symbolcount = bfd_canonicalize_symtab(bfdobj, syms))) {
01680 
01681          if (bfdobj->flags & DYNAMIC) {
01682             offset = addresses[stackfr] - dli.dli_fbase;
01683          } else {
01684             offset = addresses[stackfr] - (void *) 0;
01685          }
01686 
01687          for (section = bfdobj->sections; section; section = section->next) {
01688             if (!bfd_get_section_flags(bfdobj, section) & SEC_ALLOC ||
01689                section->vma > offset ||
01690                section->size + section->vma < offset) {
01691                continue;
01692             }
01693 
01694             if (!bfd_find_nearest_line(bfdobj, section, syms, offset - section->vma, &file, &func, &line)) {
01695                continue;
01696             }
01697 
01698             /* file can possibly be null even with a success result from bfd_find_nearest_line */
01699             file = file ? file : "";
01700 
01701             /* Stack trace output */
01702             found++;
01703             if ((lastslash = strrchr(file, '/'))) {
01704                const char *prevslash;
01705 
01706                for (prevslash = lastslash - 1; *prevslash != '/' && prevslash >= file; prevslash--) {
01707                }
01708                if (prevslash >= file) {
01709                   lastslash = prevslash;
01710                }
01711             }
01712             if (dli.dli_saddr == NULL) {
01713                address_str[0] = '\0';
01714             } else {
01715                snprintf(address_str, sizeof(address_str), " (%p+%lX)",
01716                   dli.dli_saddr,
01717                   (unsigned long) (addresses[stackfr] - dli.dli_saddr));
01718             }
01719             snprintf(msg, sizeof(msg), "%s:%u %s()%s",
01720                lastslash ? lastslash + 1 : file, line,
01721                S_OR(func, "???"),
01722                address_str);
01723 
01724             break; /* out of section iteration */
01725          }
01726       }
01727       if (bfdobj) {
01728          bfd_close(bfdobj);
01729          ast_free(syms);
01730       }
01731 
01732       /* Default output, if we cannot find the information within BFD */
01733       if (!found) {
01734          if (dli.dli_saddr == NULL) {
01735             address_str[0] = '\0';
01736          } else {
01737             snprintf(address_str, sizeof(address_str), " (%p+%lX)",
01738                dli.dli_saddr,
01739                (unsigned long) (addresses[stackfr] - dli.dli_saddr));
01740          }
01741          snprintf(msg, sizeof(msg), "%s %s()%s",
01742             lastslash ? lastslash + 1 : dli.dli_fname,
01743             S_OR(dli.dli_sname, "<unknown>"),
01744             address_str);
01745       }
01746 
01747       if (!ast_strlen_zero(msg)) {
01748          char **tmp;
01749 
01750          eachlen[stackfr] = strlen(msg) + 1;
01751          if (!(tmp = ast_std_realloc(strings, strings_size + eachlen[stackfr]))) {
01752             ast_std_free(strings);
01753             strings = NULL;
01754             break; /* out of stack frame iteration */
01755          }
01756          strings = tmp;
01757          strings[stackfr] = (char *) strings + strings_size;
01758          strcpy(strings[stackfr], msg);/* Safe since we just allocated the room. */
01759          strings_size += eachlen[stackfr];
01760       }
01761    }
01762 
01763    if (strings) {
01764       /* Recalculate the offset pointers because of the reallocs. */
01765       strings[0] = (char *) strings + num_frames * sizeof(*strings);
01766       for (stackfr = 1; stackfr < num_frames; stackfr++) {
01767          strings[stackfr] = strings[stackfr - 1] + eachlen[stackfr - 1];
01768       }
01769    }
01770    ast_free(eachlen);
01771 
01772 #else /* !defined(BETTER_BACKTRACES) */
01773 
01774    strings = backtrace_symbols(addresses, num_frames);
01775 #endif /* defined(BETTER_BACKTRACES) */
01776    return strings;
01777 }
01778 
01779 #endif /* HAVE_BKTR */
01780 
01781 void ast_backtrace(void)
01782 {
01783 #ifdef HAVE_BKTR
01784    struct ast_bt *bt;
01785    int i = 0;
01786    char **strings;
01787 
01788    if (!(bt = ast_bt_create())) {
01789       ast_log(LOG_WARNING, "Unable to allocate space for backtrace structure\n");
01790       return;
01791    }
01792 
01793    if ((strings = ast_bt_get_symbols(bt->addresses, bt->num_frames))) {
01794       ast_debug(1, "Got %d backtrace record%c\n", bt->num_frames, bt->num_frames != 1 ? 's' : ' ');
01795       for (i = 3; i < bt->num_frames - 2; i++) {
01796          ast_debug(1, "#%d: [%p] %s\n", i - 3, bt->addresses[i], strings[i]);
01797       }
01798 
01799       ast_std_free(strings);
01800    } else {
01801       ast_debug(1, "Could not allocate memory for backtrace\n");
01802    }
01803    ast_bt_destroy(bt);
01804 #else
01805    ast_log(LOG_WARNING, "Must run configure with '--with-execinfo' for stack backtraces.\n");
01806 #endif /* defined(HAVE_BKTR) */
01807 }
01808 
01809 void __ast_verbose_ap(const char *file, int line, const char *func, int level, struct ast_callid *callid, const char *fmt, va_list ap)
01810 {
01811    const char *p;
01812    struct ast_str *prefixed, *buf = NULL;
01813    int res = 0;
01814    const char *prefix = level >= 4 ? VERBOSE_PREFIX_4 : level == 3 ? VERBOSE_PREFIX_3 : level == 2 ? VERBOSE_PREFIX_2 : level == 1 ? VERBOSE_PREFIX_1 : "";
01815    signed char magic = level > 9 ? -10 : -level - 1; /* 0 => -1, 1 => -2, etc.  Can't pass NUL, as it is EOS-delimiter */
01816 
01817    /* For compatibility with modules still calling ast_verbose() directly instead of using ast_verb() */
01818    if (level < 0) {
01819       if (!strncmp(fmt, VERBOSE_PREFIX_4, strlen(VERBOSE_PREFIX_4))) {
01820          magic = -5;
01821       } else if (!strncmp(fmt, VERBOSE_PREFIX_3, strlen(VERBOSE_PREFIX_3))) {
01822          magic = -4;
01823       } else if (!strncmp(fmt, VERBOSE_PREFIX_2, strlen(VERBOSE_PREFIX_2))) {
01824          magic = -3;
01825       } else if (!strncmp(fmt, VERBOSE_PREFIX_1, strlen(VERBOSE_PREFIX_1))) {
01826          magic = -2;
01827       } else {
01828          magic = -1;
01829       }
01830    }
01831 
01832    if (!(prefixed = ast_str_thread_get(&verbose_buf, VERBOSE_BUF_INIT_SIZE)) ||
01833        !(buf = ast_str_create(VERBOSE_BUF_INIT_SIZE))) {
01834       return;
01835    }
01836 
01837    res = ast_str_set_va(&buf, 0, fmt, ap);
01838    /* If the build failed then we can drop this allocated message */
01839    if (res == AST_DYNSTR_BUILD_FAILED) {
01840       ast_free(buf);
01841       return;
01842    }
01843 
01844    ast_str_reset(prefixed);
01845    /* for every newline found in the buffer add verbose prefix data */
01846    fmt = ast_str_buffer(buf);
01847    do {
01848       if (!(p = strchr(fmt, '\n'))) {
01849          p = strchr(fmt, '\0') - 1;
01850       }
01851       ++p;
01852 
01853       if (ast_opt_timestamp) {
01854          struct ast_tm tm;
01855          char date[40];
01856          struct timeval now = ast_tvnow();
01857          ast_localtime(&now, &tm, NULL);
01858          ast_strftime(date, sizeof(date), dateformat, &tm);
01859          ast_str_append(&prefixed, 0, "%c[%s] %s", (char) magic, date, prefix);
01860       } else {
01861          ast_str_append(&prefixed, 0, "%c%s", (char) magic, prefix);
01862       }
01863       ast_str_append_substr(&prefixed, 0, fmt, p - fmt);
01864       fmt = p;
01865    } while (p && *p);
01866 
01867    ast_log_callid(__LOG_VERBOSE, file, line, func, callid, "%s", ast_str_buffer(prefixed));
01868    ast_free(buf);
01869 }
01870 
01871 void __ast_verbose(const char *file, int line, const char *func, int level, const char *fmt, ...)
01872 {
01873    struct ast_callid *callid;
01874    va_list ap;
01875 
01876    callid = ast_read_threadstorage_callid();
01877 
01878    va_start(ap, fmt);
01879    __ast_verbose_ap(file, line, func, level, callid, fmt, ap);
01880    va_end(ap);
01881 
01882    if (callid) {
01883       ast_callid_unref(callid);
01884    }
01885 }
01886 
01887 void __ast_verbose_callid(const char *file, int line, const char *func, int level, struct ast_callid *callid, const char *fmt, ...)
01888 {
01889    va_list ap;
01890    va_start(ap, fmt);
01891    __ast_verbose_ap(file, line, func, level, callid, fmt, ap);
01892    va_end(ap);
01893 }
01894 
01895 /* No new code should use this directly, but we have the ABI for backwards compat */
01896 #undef ast_verbose
01897 void __attribute__((format(printf, 1,2))) ast_verbose(const char *fmt, ...);
01898 void ast_verbose(const char *fmt, ...)
01899 {
01900    struct ast_callid *callid;
01901    va_list ap;
01902 
01903    callid = ast_read_threadstorage_callid();
01904 
01905    va_start(ap, fmt);
01906    __ast_verbose_ap("", 0, "", 0, callid, fmt, ap);
01907    va_end(ap);
01908 
01909    if (callid) {
01910       ast_callid_unref(callid);
01911    }
01912 }
01913 
01914 /*! Console verbosity level node. */
01915 struct verb_console {
01916    /*! List node link */
01917    AST_LIST_ENTRY(verb_console) node;
01918    /*! Console verbosity level. */
01919    int *level;
01920 };
01921 
01922 /*! Registered console verbosity levels */
01923 static AST_RWLIST_HEAD_STATIC(verb_consoles, verb_console);
01924 
01925 /*! ast_verb_update() reentrancy protection lock. */
01926 AST_MUTEX_DEFINE_STATIC(verb_update_lock);
01927 
01928 void ast_verb_update(void)
01929 {
01930    struct logchannel *log;
01931    struct verb_console *console;
01932    int verb_level;
01933 
01934    ast_mutex_lock(&verb_update_lock);
01935 
01936    AST_RWLIST_RDLOCK(&verb_consoles);
01937 
01938    /* Default to the root console verbosity. */
01939    verb_level = option_verbose;
01940 
01941    /* Determine max remote console level. */
01942    AST_LIST_TRAVERSE(&verb_consoles, console, node) {
01943       if (verb_level < *console->level) {
01944          verb_level = *console->level;
01945       }
01946    }
01947    AST_RWLIST_UNLOCK(&verb_consoles);
01948 
01949    /* Determine max logger channel level. */
01950    AST_RWLIST_RDLOCK(&logchannels);
01951    AST_RWLIST_TRAVERSE(&logchannels, log, list) {
01952       if (verb_level < log->verbosity) {
01953          verb_level = log->verbosity;
01954       }
01955    }
01956    AST_RWLIST_UNLOCK(&logchannels);
01957 
01958    ast_verb_sys_level = verb_level;
01959 
01960    ast_mutex_unlock(&verb_update_lock);
01961 }
01962 
01963 /*!
01964  * \internal
01965  * \brief Unregister a console verbose level.
01966  *
01967  * \param console Which console to unregister.
01968  *
01969  * \return Nothing
01970  */
01971 static void verb_console_unregister(struct verb_console *console)
01972 {
01973    AST_RWLIST_WRLOCK(&verb_consoles);
01974    console = AST_RWLIST_REMOVE(&verb_consoles, console, node);
01975    AST_RWLIST_UNLOCK(&verb_consoles);
01976    if (console) {
01977       ast_verb_update();
01978    }
01979 }
01980 
01981 static void verb_console_free(void *v_console)
01982 {
01983    struct verb_console *console = v_console;
01984 
01985    verb_console_unregister(console);
01986    ast_free(console);
01987 }
01988 
01989 /*! Thread specific console verbosity level node. */
01990 AST_THREADSTORAGE_CUSTOM(my_verb_console, NULL, verb_console_free);
01991 
01992 void ast_verb_console_register(int *level)
01993 {
01994    struct verb_console *console;
01995 
01996    console = ast_threadstorage_get(&my_verb_console, sizeof(*console));
01997    if (!console || !level) {
01998       return;
01999    }
02000    console->level = level;
02001 
02002    AST_RWLIST_WRLOCK(&verb_consoles);
02003    AST_RWLIST_INSERT_HEAD(&verb_consoles, console, node);
02004    AST_RWLIST_UNLOCK(&verb_consoles);
02005    ast_verb_update();
02006 }
02007 
02008 void ast_verb_console_unregister(void)
02009 {
02010    struct verb_console *console;
02011 
02012    console = ast_threadstorage_get(&my_verb_console, sizeof(*console));
02013    if (!console) {
02014       return;
02015    }
02016    verb_console_unregister(console);
02017 }
02018 
02019 int ast_verb_console_get(void)
02020 {
02021    struct verb_console *console;
02022    int verb_level;
02023 
02024    console = ast_threadstorage_get(&my_verb_console, sizeof(*console));
02025    AST_RWLIST_RDLOCK(&verb_consoles);
02026    if (!console) {
02027       verb_level = 0;
02028    } else if (console->level) {
02029       verb_level = *console->level;
02030    } else {
02031       verb_level = option_verbose;
02032    }
02033    AST_RWLIST_UNLOCK(&verb_consoles);
02034    return verb_level;
02035 }
02036 
02037 void ast_verb_console_set(int verb_level)
02038 {
02039    struct verb_console *console;
02040 
02041    console = ast_threadstorage_get(&my_verb_console, sizeof(*console));
02042    if (!console) {
02043       return;
02044    }
02045 
02046    AST_RWLIST_WRLOCK(&verb_consoles);
02047    if (console->level) {
02048       *console->level = verb_level;
02049    } else {
02050       option_verbose = verb_level;
02051    }
02052    AST_RWLIST_UNLOCK(&verb_consoles);
02053    ast_verb_update();
02054 }
02055 
02056 int ast_register_verbose(void (*v)(const char *string))
02057 {
02058    struct verb *verb;
02059 
02060    if (!(verb = ast_malloc(sizeof(*verb))))
02061       return -1;
02062 
02063    verb->verboser = v;
02064 
02065    AST_RWLIST_WRLOCK(&verbosers);
02066    AST_RWLIST_INSERT_HEAD(&verbosers, verb, list);
02067    AST_RWLIST_UNLOCK(&verbosers);
02068 
02069    return 0;
02070 }
02071 
02072 int ast_unregister_verbose(void (*v)(const char *string))
02073 {
02074    struct verb *cur;
02075 
02076    AST_RWLIST_WRLOCK(&verbosers);
02077    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&verbosers, cur, list) {
02078       if (cur->verboser == v) {
02079          AST_RWLIST_REMOVE_CURRENT(list);
02080          ast_free(cur);
02081          break;
02082       }
02083    }
02084    AST_RWLIST_TRAVERSE_SAFE_END;
02085    AST_RWLIST_UNLOCK(&verbosers);
02086 
02087    return cur ? 0 : -1;
02088 }
02089 
02090 static void update_logchannels(void)
02091 {
02092    struct logchannel *cur;
02093 
02094    AST_RWLIST_WRLOCK(&logchannels);
02095 
02096    global_logmask = 0;
02097 
02098    AST_RWLIST_TRAVERSE(&logchannels, cur, list) {
02099       make_components(cur);
02100       global_logmask |= cur->logmask;
02101    }
02102 
02103    AST_RWLIST_UNLOCK(&logchannels);
02104 }
02105 
02106 int ast_logger_register_level(const char *name)
02107 {
02108    unsigned int level;
02109    unsigned int available = 0;
02110 
02111    AST_RWLIST_WRLOCK(&logchannels);
02112 
02113    for (level = 0; level < ARRAY_LEN(levels); level++) {
02114       if ((level >= 16) && !available && !levels[level]) {
02115          available = level;
02116          continue;
02117       }
02118 
02119       if (levels[level] && !strcasecmp(levels[level], name)) {
02120          ast_log(LOG_WARNING,
02121             "Unable to register dynamic logger level '%s': a standard logger level uses that name.\n",
02122             name);
02123          AST_RWLIST_UNLOCK(&logchannels);
02124 
02125          return -1;
02126       }
02127    }
02128 
02129    if (!available) {
02130       ast_log(LOG_WARNING,
02131          "Unable to register dynamic logger level '%s'; maximum number of levels registered.\n",
02132          name);
02133       AST_RWLIST_UNLOCK(&logchannels);
02134 
02135       return -1;
02136    }
02137 
02138    levels[available] = ast_strdup(name);
02139 
02140    AST_RWLIST_UNLOCK(&logchannels);
02141 
02142    ast_debug(1, "Registered dynamic logger level '%s' with index %d.\n", name, available);
02143 
02144    update_logchannels();
02145 
02146    return available;
02147 }
02148 
02149 void ast_logger_unregister_level(const char *name)
02150 {
02151    unsigned int found = 0;
02152    unsigned int x;
02153 
02154    AST_RWLIST_WRLOCK(&logchannels);
02155 
02156    for (x = 16; x < ARRAY_LEN(levels); x++) {
02157       if (!levels[x]) {
02158          continue;
02159       }
02160 
02161       if (strcasecmp(levels[x], name)) {
02162          continue;
02163       }
02164 
02165       found = 1;
02166       break;
02167    }
02168 
02169    if (found) {
02170       /* take this level out of the global_logmask, to ensure that no new log messages
02171        * will be queued for it
02172        */
02173 
02174       global_logmask &= ~(1 << x);
02175 
02176       ast_free(levels[x]);
02177       levels[x] = NULL;
02178       AST_RWLIST_UNLOCK(&logchannels);
02179 
02180       ast_debug(1, "Unregistered dynamic logger level '%s' with index %d.\n", name, x);
02181 
02182       update_logchannels();
02183    } else {
02184       AST_RWLIST_UNLOCK(&logchannels);
02185    }
02186 }