Thu Oct 8 21:57:27 2009

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 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 115333 $")
00031 
00032 #include <signal.h>
00033 #include <stdarg.h>
00034 #include <stdio.h>
00035 #include <unistd.h>
00036 #include <time.h>
00037 #include <string.h>
00038 #include <stdlib.h>
00039 #include <errno.h>
00040 #include <sys/stat.h>
00041 #if ((defined(AST_DEVMODE)) && (defined(linux)))
00042 #include <execinfo.h>
00043 #define MAX_BACKTRACE_FRAMES 20
00044 #endif
00045 
00046 #define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values,
00047               from <syslog.h> which is included by logger.h */
00048 #include <syslog.h>
00049 
00050 static int syslog_level_map[] = {
00051    LOG_DEBUG,
00052    LOG_INFO,    /* arbitrary equivalent of LOG_EVENT */
00053    LOG_NOTICE,
00054    LOG_WARNING,
00055    LOG_ERR,
00056    LOG_DEBUG,
00057    LOG_DEBUG
00058 };
00059 
00060 #define SYSLOG_NLEVELS sizeof(syslog_level_map) / sizeof(int)
00061 
00062 #include "asterisk/logger.h"
00063 #include "asterisk/lock.h"
00064 #include "asterisk/options.h"
00065 #include "asterisk/channel.h"
00066 #include "asterisk/config.h"
00067 #include "asterisk/term.h"
00068 #include "asterisk/cli.h"
00069 #include "asterisk/utils.h"
00070 #include "asterisk/manager.h"
00071 #include "asterisk/threadstorage.h"
00072 
00073 #if defined(__linux__) && !defined(__NR_gettid)
00074 #include <asm/unistd.h>
00075 #endif
00076 
00077 #if defined(__linux__) && defined(__NR_gettid)
00078 #define GETTID() syscall(__NR_gettid)
00079 #else
00080 #define GETTID() getpid()
00081 #endif
00082 
00083 
00084 static char dateformat[256] = "%b %e %T";    /* Original Asterisk Format */
00085 
00086 static int filesize_reload_needed;
00087 static int global_logmask = -1;
00088 
00089 static struct {
00090    unsigned int queue_log:1;
00091    unsigned int event_log:1;
00092 } logfiles = { 1, 1 };
00093 
00094 static char hostname[MAXHOSTNAMELEN];
00095 
00096 enum logtypes {
00097    LOGTYPE_SYSLOG,
00098    LOGTYPE_FILE,
00099    LOGTYPE_CONSOLE,
00100 };
00101 
00102 struct logchannel {
00103    int logmask;         /* What to log to this channel */
00104    int disabled;        /* If this channel is disabled or not */
00105    int facility;        /* syslog facility */
00106    enum logtypes type;     /* Type of log channel */
00107    FILE *fileptr;       /* logfile logging file pointer */
00108    char filename[256];     /* Filename */
00109    AST_LIST_ENTRY(logchannel) list;
00110 };
00111 
00112 static AST_LIST_HEAD_STATIC(logchannels, logchannel);
00113 
00114 static FILE *eventlog;
00115 static FILE *qlog;
00116 
00117 static char *levels[] = {
00118    "DEBUG",
00119    "EVENT",
00120    "NOTICE",
00121    "WARNING",
00122    "ERROR",
00123    "VERBOSE",
00124    "DTMF"
00125 };
00126 
00127 static int colors[] = {
00128    COLOR_BRGREEN,
00129    COLOR_BRBLUE,
00130    COLOR_YELLOW,
00131    COLOR_BRRED,
00132    COLOR_RED,
00133    COLOR_GREEN,
00134    COLOR_BRGREEN
00135 };
00136 
00137 AST_THREADSTORAGE(verbose_buf, verbose_buf_init);
00138 #define VERBOSE_BUF_INIT_SIZE   128
00139 
00140 AST_THREADSTORAGE(log_buf, log_buf_init);
00141 #define LOG_BUF_INIT_SIZE       128
00142 
00143 static int make_components(char *s, int lineno)
00144 {
00145    char *w;
00146    int res = 0;
00147    char *stringp = s;
00148 
00149    while ((w = strsep(&stringp, ","))) {
00150       w = ast_skip_blanks(w);
00151       if (!strcasecmp(w, "error")) 
00152          res |= (1 << __LOG_ERROR);
00153       else if (!strcasecmp(w, "warning"))
00154          res |= (1 << __LOG_WARNING);
00155       else if (!strcasecmp(w, "notice"))
00156          res |= (1 << __LOG_NOTICE);
00157       else if (!strcasecmp(w, "event"))
00158          res |= (1 << __LOG_EVENT);
00159       else if (!strcasecmp(w, "debug"))
00160          res |= (1 << __LOG_DEBUG);
00161       else if (!strcasecmp(w, "verbose"))
00162          res |= (1 << __LOG_VERBOSE);
00163       else if (!strcasecmp(w, "dtmf"))
00164          res |= (1 << __LOG_DTMF);
00165       else {
00166          fprintf(stderr, "Logfile Warning: Unknown keyword '%s' at line %d of logger.conf\n", w, lineno);
00167       }
00168    }
00169 
00170    return res;
00171 }
00172 
00173 static struct logchannel *make_logchannel(char *channel, char *components, int lineno)
00174 {
00175    struct logchannel *chan;
00176    char *facility;
00177 #ifndef SOLARIS
00178    CODE *cptr;
00179 #endif
00180 
00181    if (ast_strlen_zero(channel) || !(chan = ast_calloc(1, sizeof(*chan))))
00182       return NULL;
00183 
00184    if (!strcasecmp(channel, "console")) {
00185       chan->type = LOGTYPE_CONSOLE;
00186    } else if (!strncasecmp(channel, "syslog", 6)) {
00187       /*
00188       * syntax is:
00189       *  syslog.facility => level,level,level
00190       */
00191       facility = strchr(channel, '.');
00192       if(!facility++ || !facility) {
00193          facility = "local0";
00194       }
00195 
00196 #ifndef SOLARIS
00197       /*
00198       * Walk through the list of facilitynames (defined in sys/syslog.h)
00199       * to see if we can find the one we have been given
00200       */
00201       chan->facility = -1;
00202       cptr = facilitynames;
00203       while (cptr->c_name) {
00204          if (!strcasecmp(facility, cptr->c_name)) {
00205             chan->facility = cptr->c_val;
00206             break;
00207          }
00208          cptr++;
00209       }
00210 #else
00211       chan->facility = -1;
00212       if (!strcasecmp(facility, "kern")) 
00213          chan->facility = LOG_KERN;
00214       else if (!strcasecmp(facility, "USER")) 
00215          chan->facility = LOG_USER;
00216       else if (!strcasecmp(facility, "MAIL")) 
00217          chan->facility = LOG_MAIL;
00218       else if (!strcasecmp(facility, "DAEMON")) 
00219          chan->facility = LOG_DAEMON;
00220       else if (!strcasecmp(facility, "AUTH")) 
00221          chan->facility = LOG_AUTH;
00222       else if (!strcasecmp(facility, "SYSLOG")) 
00223          chan->facility = LOG_SYSLOG;
00224       else if (!strcasecmp(facility, "LPR")) 
00225          chan->facility = LOG_LPR;
00226       else if (!strcasecmp(facility, "NEWS")) 
00227          chan->facility = LOG_NEWS;
00228       else if (!strcasecmp(facility, "UUCP")) 
00229          chan->facility = LOG_UUCP;
00230       else if (!strcasecmp(facility, "CRON")) 
00231          chan->facility = LOG_CRON;
00232       else if (!strcasecmp(facility, "LOCAL0")) 
00233          chan->facility = LOG_LOCAL0;
00234       else if (!strcasecmp(facility, "LOCAL1")) 
00235          chan->facility = LOG_LOCAL1;
00236       else if (!strcasecmp(facility, "LOCAL2")) 
00237          chan->facility = LOG_LOCAL2;
00238       else if (!strcasecmp(facility, "LOCAL3")) 
00239          chan->facility = LOG_LOCAL3;
00240       else if (!strcasecmp(facility, "LOCAL4")) 
00241          chan->facility = LOG_LOCAL4;
00242       else if (!strcasecmp(facility, "LOCAL5")) 
00243          chan->facility = LOG_LOCAL5;
00244       else if (!strcasecmp(facility, "LOCAL6")) 
00245          chan->facility = LOG_LOCAL6;
00246       else if (!strcasecmp(facility, "LOCAL7")) 
00247          chan->facility = LOG_LOCAL7;
00248 #endif /* Solaris */
00249 
00250       if (0 > chan->facility) {
00251          fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
00252          free(chan);
00253          return NULL;
00254       }
00255 
00256       chan->type = LOGTYPE_SYSLOG;
00257       snprintf(chan->filename, sizeof(chan->filename), "%s", channel);
00258       openlog("asterisk", LOG_PID, chan->facility);
00259    } else {
00260       if (channel[0] == '/') {
00261          if(!ast_strlen_zero(hostname)) { 
00262             snprintf(chan->filename, sizeof(chan->filename) - 1,"%s.%s", channel, hostname);
00263          } else {
00264             ast_copy_string(chan->filename, channel, sizeof(chan->filename));
00265          }
00266       }       
00267       
00268       if(!ast_strlen_zero(hostname)) {
00269          snprintf(chan->filename, sizeof(chan->filename), "%s/%s.%s",(char *)ast_config_AST_LOG_DIR, channel, hostname);
00270       } else {
00271          snprintf(chan->filename, sizeof(chan->filename), "%s/%s", (char *)ast_config_AST_LOG_DIR, channel);
00272       }
00273       chan->fileptr = fopen(chan->filename, "a");
00274       if (!chan->fileptr) {
00275          /* Can't log here, since we're called with a lock */
00276          fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", chan->filename, strerror(errno));
00277       } 
00278       chan->type = LOGTYPE_FILE;
00279    }
00280    chan->logmask = make_components(components, lineno);
00281    return chan;
00282 }
00283 
00284 static void init_logger_chain(void)
00285 {
00286    struct logchannel *chan;
00287    struct ast_config *cfg;
00288    struct ast_variable *var;
00289    const char *s;
00290 
00291    /* delete our list of log channels */
00292    AST_LIST_LOCK(&logchannels);
00293    while ((chan = AST_LIST_REMOVE_HEAD(&logchannels, list)))
00294       free(chan);
00295    AST_LIST_UNLOCK(&logchannels);
00296    
00297    global_logmask = 0;
00298    errno = 0;
00299    /* close syslog */
00300    closelog();
00301    
00302    cfg = ast_config_load("logger.conf");
00303    
00304    /* If no config file, we're fine, set default options. */
00305    if (!cfg) {
00306       if (errno)
00307          fprintf(stderr, "Unable to open logger.conf: %s; default settings will be used.\n", strerror(errno));
00308       else
00309          fprintf(stderr, "Errors detected in logger.conf: see above; default settings will be used.\n");
00310       if (!(chan = ast_calloc(1, sizeof(*chan))))
00311          return;
00312       chan->type = LOGTYPE_CONSOLE;
00313       chan->logmask = 28; /*warning,notice,error */
00314       AST_LIST_LOCK(&logchannels);
00315       AST_LIST_INSERT_HEAD(&logchannels, chan, list);
00316       AST_LIST_UNLOCK(&logchannels);
00317       global_logmask |= chan->logmask;
00318       return;
00319    }
00320    
00321    if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
00322       if (ast_true(s)) {
00323          if (gethostname(hostname, sizeof(hostname) - 1)) {
00324             ast_copy_string(hostname, "unknown", sizeof(hostname));
00325             ast_log(LOG_WARNING, "What box has no hostname???\n");
00326          }
00327       } else
00328          hostname[0] = '\0';
00329    } else
00330       hostname[0] = '\0';
00331    if ((s = ast_variable_retrieve(cfg, "general", "dateformat")))
00332       ast_copy_string(dateformat, s, sizeof(dateformat));
00333    else
00334       ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
00335    if ((s = ast_variable_retrieve(cfg, "general", "queue_log")))
00336       logfiles.queue_log = ast_true(s);
00337    if ((s = ast_variable_retrieve(cfg, "general", "event_log")))
00338       logfiles.event_log = ast_true(s);
00339 
00340    AST_LIST_LOCK(&logchannels);
00341    var = ast_variable_browse(cfg, "logfiles");
00342    for (; var; var = var->next) {
00343       if (!(chan = make_logchannel(var->name, var->value, var->lineno)))
00344          continue;
00345       AST_LIST_INSERT_HEAD(&logchannels, chan, list);
00346       global_logmask |= chan->logmask;
00347    }
00348    AST_LIST_UNLOCK(&logchannels);
00349 
00350    ast_config_destroy(cfg);
00351 }
00352 
00353 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
00354 {
00355    va_list ap;
00356    AST_LIST_LOCK(&logchannels);
00357    if (qlog) {
00358       va_start(ap, fmt);
00359       fprintf(qlog, "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
00360       vfprintf(qlog, fmt, ap);
00361       fprintf(qlog, "\n");
00362       va_end(ap);
00363       fflush(qlog);
00364    }
00365    AST_LIST_UNLOCK(&logchannels);
00366 }
00367 
00368 int reload_logger(int rotate)
00369 {
00370    char old[PATH_MAX] = "";
00371    char new[PATH_MAX];
00372    int event_rotate = rotate, queue_rotate = rotate;
00373    struct logchannel *f;
00374    FILE *myf;
00375    int x, res = 0;
00376 
00377    AST_LIST_LOCK(&logchannels);
00378 
00379    if (eventlog) 
00380       fclose(eventlog);
00381    else 
00382       event_rotate = 0;
00383    eventlog = NULL;
00384 
00385    if (qlog) 
00386       fclose(qlog);
00387    else 
00388       queue_rotate = 0;
00389    qlog = NULL;
00390 
00391    mkdir((char *)ast_config_AST_LOG_DIR, 0755);
00392 
00393    AST_LIST_TRAVERSE(&logchannels, f, list) {
00394       if (f->disabled) {
00395          f->disabled = 0;  /* Re-enable logging at reload */
00396          manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
00397       }
00398       if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
00399          fclose(f->fileptr);  /* Close file */
00400          f->fileptr = NULL;
00401          if (rotate) {
00402             ast_copy_string(old, f->filename, sizeof(old));
00403    
00404             for (x = 0; ; x++) {
00405                snprintf(new, sizeof(new), "%s.%d", f->filename, x);
00406                myf = fopen((char *)new, "r");
00407                if (myf)
00408                   fclose(myf);
00409                else
00410                   break;
00411             }
00412        
00413             /* do it */
00414             if (rename(old,new))
00415                fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
00416          }
00417       }
00418    }
00419 
00420    filesize_reload_needed = 0;
00421    
00422    init_logger_chain();
00423 
00424    if (logfiles.event_log) {
00425       snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
00426       if (event_rotate) {
00427          for (x=0;;x++) {
00428             snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, EVENTLOG,x);
00429             myf = fopen((char *)new, "r");
00430             if (myf)    /* File exists */
00431                fclose(myf);
00432             else
00433                break;
00434          }
00435    
00436          /* do it */
00437          if (rename(old,new))
00438             ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new);
00439       }
00440 
00441       eventlog = fopen(old, "a");
00442       if (eventlog) {
00443          ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
00444          if (option_verbose)
00445             ast_verbose("Asterisk Event Logger restarted\n");
00446       } else {
00447          ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
00448          res = -1;
00449       }
00450    }
00451 
00452    if (logfiles.queue_log) {
00453       snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, QUEUELOG);
00454       if (queue_rotate) {
00455          for (x = 0; ; x++) {
00456             snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, QUEUELOG, x);
00457             myf = fopen((char *)new, "r");
00458             if (myf)    /* File exists */
00459                fclose(myf);
00460             else
00461                break;
00462          }
00463    
00464          /* do it */
00465          if (rename(old, new))
00466             ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new);
00467       }
00468 
00469       qlog = fopen(old, "a");
00470       if (qlog) {
00471          ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
00472          ast_log(LOG_EVENT, "Restarted Asterisk Queue Logger\n");
00473          if (option_verbose)
00474             ast_verbose("Asterisk Queue Logger restarted\n");
00475       } else {
00476          ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
00477          res = -1;
00478       }
00479    }
00480 
00481    AST_LIST_UNLOCK(&logchannels);
00482 
00483    return res;
00484 }
00485 
00486 /*! \brief Reload the logger module without rotating log files (also used from loader.c during
00487    a full Asterisk reload) */
00488 int logger_reload(void)
00489 {
00490    if(reload_logger(0))
00491       return RESULT_FAILURE;
00492    return RESULT_SUCCESS;
00493 }
00494 
00495 static int handle_logger_reload(int fd, int argc, char *argv[])
00496 {
00497    int result = logger_reload();
00498    if (result == RESULT_FAILURE)
00499       ast_cli(fd, "Failed to reload the logger\n");
00500    return result;
00501 }
00502 
00503 static int handle_logger_rotate(int fd, int argc, char *argv[])
00504 {
00505    if(reload_logger(1)) {
00506       ast_cli(fd, "Failed to reload the logger and rotate log files\n");
00507       return RESULT_FAILURE;
00508    }
00509    return RESULT_SUCCESS;
00510 }
00511 
00512 /*! \brief CLI command to show logging system configuration */
00513 static int handle_logger_show_channels(int fd, int argc, char *argv[])
00514 {
00515 #define FORMATL   "%-35.35s %-8.8s %-9.9s "
00516    struct logchannel *chan;
00517 
00518    ast_cli(fd,FORMATL, "Channel", "Type", "Status");
00519    ast_cli(fd, "Configuration\n");
00520    ast_cli(fd,FORMATL, "-------", "----", "------");
00521    ast_cli(fd, "-------------\n");
00522    AST_LIST_LOCK(&logchannels);
00523    AST_LIST_TRAVERSE(&logchannels, chan, list) {
00524       ast_cli(fd, FORMATL, chan->filename, chan->type==LOGTYPE_CONSOLE ? "Console" : (chan->type==LOGTYPE_SYSLOG ? "Syslog" : "File"),
00525          chan->disabled ? "Disabled" : "Enabled");
00526       ast_cli(fd, " - ");
00527       if (chan->logmask & (1 << __LOG_DEBUG)) 
00528          ast_cli(fd, "Debug ");
00529       if (chan->logmask & (1 << __LOG_DTMF)) 
00530          ast_cli(fd, "DTMF ");
00531       if (chan->logmask & (1 << __LOG_VERBOSE)) 
00532          ast_cli(fd, "Verbose ");
00533       if (chan->logmask & (1 << __LOG_WARNING)) 
00534          ast_cli(fd, "Warning ");
00535       if (chan->logmask & (1 << __LOG_NOTICE)) 
00536          ast_cli(fd, "Notice ");
00537       if (chan->logmask & (1 << __LOG_ERROR)) 
00538          ast_cli(fd, "Error ");
00539       if (chan->logmask & (1 << __LOG_EVENT)) 
00540          ast_cli(fd, "Event ");
00541       ast_cli(fd, "\n");
00542    }
00543    AST_LIST_UNLOCK(&logchannels);
00544    ast_cli(fd, "\n");
00545       
00546    return RESULT_SUCCESS;
00547 }
00548 
00549 struct verb {
00550    void (*verboser)(const char *string);
00551    AST_LIST_ENTRY(verb) list;
00552 };
00553 
00554 static AST_LIST_HEAD_STATIC(verbosers, verb);
00555 
00556 static char logger_reload_help[] =
00557 "Usage: logger reload\n"
00558 "       Reloads the logger subsystem state.  Use after restarting syslogd(8) if you are using syslog logging.\n";
00559 
00560 static char logger_rotate_help[] =
00561 "Usage: logger rotate\n"
00562 "       Rotates and Reopens the log files.\n";
00563 
00564 static char logger_show_channels_help[] =
00565 "Usage: logger show channels\n"
00566 "       List configured logger channels.\n";
00567 
00568 static struct ast_cli_entry cli_logger[] = {
00569    { { "logger", "show", "channels", NULL }, 
00570    handle_logger_show_channels, "List configured log channels",
00571    logger_show_channels_help },
00572 
00573    { { "logger", "reload", NULL }, 
00574    handle_logger_reload, "Reopens the log files",
00575    logger_reload_help },
00576 
00577    { { "logger", "rotate", NULL }, 
00578    handle_logger_rotate, "Rotates and reopens the log files",
00579    logger_rotate_help },
00580 };
00581 
00582 static int handle_SIGXFSZ(int sig) 
00583 {
00584    /* Indicate need to reload */
00585    filesize_reload_needed = 1;
00586    return 0;
00587 }
00588 
00589 int init_logger(void)
00590 {
00591    char tmp[256];
00592    int res = 0;
00593 
00594    /* auto rotate if sig SIGXFSZ comes a-knockin */
00595    (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ);
00596 
00597    /* register the logger cli commands */
00598    ast_cli_register_multiple(cli_logger, sizeof(cli_logger) / sizeof(struct ast_cli_entry));
00599 
00600    mkdir((char *)ast_config_AST_LOG_DIR, 0755);
00601   
00602    /* create log channels */
00603    init_logger_chain();
00604 
00605    /* create the eventlog */
00606    if (logfiles.event_log) {
00607       mkdir((char *)ast_config_AST_LOG_DIR, 0755);
00608       snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
00609       eventlog = fopen((char *)tmp, "a");
00610       if (eventlog) {
00611          ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
00612          if (option_verbose)
00613             ast_verbose("Asterisk Event Logger Started %s\n",(char *)tmp);
00614       } else {
00615          ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
00616          res = -1;
00617       }
00618    }
00619 
00620    if (logfiles.queue_log) {
00621       snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, QUEUELOG);
00622       qlog = fopen(tmp, "a");
00623       ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
00624    }
00625    return res;
00626 }
00627 
00628 void close_logger(void)
00629 {
00630    struct logchannel *f;
00631 
00632    AST_LIST_LOCK(&logchannels);
00633 
00634    if (eventlog) {
00635       fclose(eventlog);
00636       eventlog = NULL;
00637    }
00638 
00639    if (qlog) {
00640       fclose(qlog);
00641       qlog = NULL;
00642    }
00643 
00644    AST_LIST_TRAVERSE(&logchannels, f, list) {
00645       if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
00646          fclose(f->fileptr);
00647          f->fileptr = NULL;
00648       }
00649    }
00650 
00651    closelog(); /* syslog */
00652 
00653    AST_LIST_UNLOCK(&logchannels);
00654 
00655    return;
00656 }
00657 
00658 static void ast_log_vsyslog(int level, const char *file, int line, const char *function, const char *fmt, va_list args) 
00659 {
00660    char buf[BUFSIZ];
00661    char *s;
00662 
00663    if (level >= SYSLOG_NLEVELS) {
00664       /* we are locked here, so cannot ast_log() */
00665       fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level);
00666       return;
00667    }
00668    if (level == __LOG_VERBOSE) {
00669       snprintf(buf, sizeof(buf), "VERBOSE[%ld]: ", (long)GETTID());
00670       level = __LOG_DEBUG;
00671    } else if (level == __LOG_DTMF) {
00672       snprintf(buf, sizeof(buf), "DTMF[%ld]: ", (long)GETTID());
00673       level = __LOG_DEBUG;
00674    } else {
00675       snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: ",
00676           levels[level], (long)GETTID(), file, line, function);
00677    }
00678    s = buf + strlen(buf);
00679    vsnprintf(s, sizeof(buf) - strlen(buf), fmt, args);
00680    term_strip(s, s, strlen(s) + 1);
00681    syslog(syslog_level_map[level], "%s", buf);
00682 }
00683 
00684 /*!
00685  * \brief send log messages to syslog and/or the console
00686  */
00687 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
00688 {
00689    struct logchannel *chan;
00690    struct ast_dynamic_str *buf;
00691    time_t t;
00692    struct tm tm;
00693    char date[256];
00694 
00695    va_list ap;
00696 
00697    if (!(buf = ast_dynamic_str_thread_get(&log_buf, LOG_BUF_INIT_SIZE)))
00698       return;
00699 
00700    if (AST_LIST_EMPTY(&logchannels))
00701    {
00702       /*
00703        * we don't have the logger chain configured yet,
00704        * so just log to stdout
00705       */
00706       if (level != __LOG_VERBOSE) {
00707          int res;
00708          va_start(ap, fmt);
00709          res = ast_dynamic_str_thread_set_va(&buf, BUFSIZ, &log_buf, fmt, ap);
00710          va_end(ap);
00711          if (res != AST_DYNSTR_BUILD_FAILED) {
00712             term_filter_escapes(buf->str);
00713             fputs(buf->str, stdout);
00714          }
00715       }
00716       return;
00717    }
00718 
00719    /* don't display LOG_DEBUG messages unless option_verbose _or_ option_debug
00720       are non-zero; LOG_DEBUG messages can still be displayed if option_debug
00721       is zero, if option_verbose is non-zero (this allows for 'level zero'
00722       LOG_DEBUG messages to be displayed, if the logmask on any channel
00723       allows it)
00724    */
00725    if (!option_verbose && !option_debug && (level == __LOG_DEBUG))
00726       return;
00727 
00728    /* Ignore anything that never gets logged anywhere */
00729    if (!(global_logmask & (1 << level)))
00730       return;
00731    
00732    /* Ignore anything other than the currently debugged file if there is one */
00733    if ((level == __LOG_DEBUG) && !ast_strlen_zero(debug_filename) && strcasecmp(debug_filename, file))
00734       return;
00735 
00736    time(&t);
00737    ast_localtime(&t, &tm, NULL);
00738    strftime(date, sizeof(date), dateformat, &tm);
00739 
00740    AST_LIST_LOCK(&logchannels);
00741 
00742    if (logfiles.event_log && level == __LOG_EVENT) {
00743       va_start(ap, fmt);
00744 
00745       fprintf(eventlog, "%s asterisk[%ld]: ", date, (long)getpid());
00746       vfprintf(eventlog, fmt, ap);
00747       fflush(eventlog);
00748 
00749       va_end(ap);
00750       AST_LIST_UNLOCK(&logchannels);
00751       return;
00752    }
00753 
00754    AST_LIST_TRAVERSE(&logchannels, chan, list) {
00755       if (chan->disabled)
00756          break;
00757       /* Check syslog channels */
00758       if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << level))) {
00759          va_start(ap, fmt);
00760          ast_log_vsyslog(level, file, line, function, fmt, ap);
00761          va_end(ap);
00762       /* Console channels */
00763       } else if ((chan->logmask & (1 << level)) && (chan->type == LOGTYPE_CONSOLE)) {
00764          char linestr[128];
00765          char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
00766 
00767          if (level != __LOG_VERBOSE) {
00768             int res;
00769             sprintf(linestr, "%d", line);
00770             ast_dynamic_str_thread_set(&buf, BUFSIZ, &log_buf,
00771                "[%s] %s[%ld]: %s:%s %s: ",
00772                date,
00773                term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)),
00774                (long)GETTID(),
00775                term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
00776                term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
00777                term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
00778             /*filter to the console!*/
00779             term_filter_escapes(buf->str);
00780             ast_console_puts_mutable(buf->str);
00781             
00782             va_start(ap, fmt);
00783             res = ast_dynamic_str_thread_set_va(&buf, BUFSIZ, &log_buf, fmt, ap);
00784             va_end(ap);
00785             if (res != AST_DYNSTR_BUILD_FAILED)
00786                ast_console_puts_mutable(buf->str);
00787          }
00788       /* File channels */
00789       } else if ((chan->logmask & (1 << level)) && (chan->fileptr)) {
00790          int res;
00791          ast_dynamic_str_thread_set(&buf, BUFSIZ, &log_buf, 
00792             "[%s] %s[%ld] %s: ",
00793             date, levels[level], (long)GETTID(), file);
00794          res = fprintf(chan->fileptr, "%s", buf->str);
00795          if (res <= 0 && !ast_strlen_zero(buf->str)) {   /* Error, no characters printed */
00796             fprintf(stderr,"**** Asterisk Logging Error: ***********\n");
00797             if (errno == ENOMEM || errno == ENOSPC) {
00798                fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename);
00799             } else
00800                fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno));
00801             manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno));
00802             chan->disabled = 1;  
00803          } else {
00804             int res;
00805             /* No error message, continue printing */
00806             va_start(ap, fmt);
00807             res = ast_dynamic_str_thread_set_va(&buf, BUFSIZ, &log_buf, fmt, ap);
00808             va_end(ap);
00809             if (res != AST_DYNSTR_BUILD_FAILED) {
00810                term_strip(buf->str, buf->str, buf->len);
00811                fputs(buf->str, chan->fileptr);
00812                fflush(chan->fileptr);
00813             }
00814          }
00815       }
00816    }
00817 
00818    AST_LIST_UNLOCK(&logchannels);
00819 
00820    if (filesize_reload_needed) {
00821       reload_logger(1);
00822       ast_log(LOG_EVENT,"Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
00823       if (option_verbose)
00824          ast_verbose("Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
00825    }
00826 }
00827 
00828 void ast_backtrace(void)
00829 {
00830 #ifdef linux
00831 #ifdef AST_DEVMODE
00832    int count=0, i=0;
00833    void **addresses;
00834    char **strings;
00835 
00836    if ((addresses = ast_calloc(MAX_BACKTRACE_FRAMES, sizeof(*addresses)))) {
00837       count = backtrace(addresses, MAX_BACKTRACE_FRAMES);
00838       if ((strings = backtrace_symbols(addresses, count))) {
00839          ast_log(LOG_DEBUG, "Got %d backtrace record%c\n", count, count != 1 ? 's' : ' ');
00840          for (i=0; i < count ; i++) {
00841 #if __WORDSIZE == 32
00842             ast_log(LOG_DEBUG, "#%d: [%08X] %s\n", i, (unsigned int)addresses[i], strings[i]);
00843 #elif __WORDSIZE == 64
00844             ast_log(LOG_DEBUG, "#%d: [%016lX] %s\n", i, (unsigned long)addresses[i], strings[i]);
00845 #endif
00846          }
00847          free(strings);
00848       } else {
00849          ast_log(LOG_DEBUG, "Could not allocate memory for backtrace\n");
00850       }
00851       free(addresses);
00852    }
00853 #else
00854    ast_log(LOG_WARNING, "Must run configure with '--enable-dev-mode' for stack backtraces.\n");
00855 #endif
00856 #else /* ndef linux */
00857    ast_log(LOG_WARNING, "Inline stack backtraces are only available on the Linux platform.\n");
00858 #endif
00859 }
00860 
00861 void ast_verbose(const char *fmt, ...)
00862 {
00863    struct verb *v;
00864    struct ast_dynamic_str *buf;
00865    int res;
00866    va_list ap;
00867 
00868    if (ast_opt_timestamp) {
00869       time_t t;
00870       struct tm tm;
00871       char date[40];
00872       char *datefmt;
00873 
00874       time(&t);
00875       ast_localtime(&t, &tm, NULL);
00876       strftime(date, sizeof(date), dateformat, &tm);
00877       datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1);
00878       sprintf(datefmt, "%c[%s] %s", 127, date, fmt);
00879       fmt = datefmt;
00880    } else {
00881       char *tmp = alloca(strlen(fmt) + 2);
00882       sprintf(tmp, "%c%s", 127, fmt);
00883       fmt = tmp;
00884    }
00885 
00886    if (!(buf = ast_dynamic_str_thread_get(&verbose_buf, VERBOSE_BUF_INIT_SIZE)))
00887       return;
00888 
00889    va_start(ap, fmt);
00890    res = ast_dynamic_str_thread_set_va(&buf, 0, &verbose_buf, fmt, ap);
00891    va_end(ap);
00892 
00893    if (res == AST_DYNSTR_BUILD_FAILED)
00894       return;
00895    
00896    /* filter out possibly hazardous escape sequences */
00897    term_filter_escapes(buf->str);
00898 
00899    AST_LIST_LOCK(&verbosers);
00900    AST_LIST_TRAVERSE(&verbosers, v, list)
00901       v->verboser(buf->str);
00902    AST_LIST_UNLOCK(&verbosers);
00903 
00904    ast_log(LOG_VERBOSE, "%s", buf->str + 1);
00905 }
00906 
00907 int ast_register_verbose(void (*v)(const char *string)) 
00908 {
00909    struct verb *verb;
00910 
00911    if (!(verb = ast_malloc(sizeof(*verb))))
00912       return -1;
00913 
00914    verb->verboser = v;
00915 
00916    AST_LIST_LOCK(&verbosers);
00917    AST_LIST_INSERT_HEAD(&verbosers, verb, list);
00918    AST_LIST_UNLOCK(&verbosers);
00919    
00920    return 0;
00921 }
00922 
00923 int ast_unregister_verbose(void (*v)(const char *string))
00924 {
00925    struct verb *cur;
00926 
00927    AST_LIST_LOCK(&verbosers);
00928    AST_LIST_TRAVERSE_SAFE_BEGIN(&verbosers, cur, list) {
00929       if (cur->verboser == v) {
00930          AST_LIST_REMOVE_CURRENT(&verbosers, list);
00931          free(cur);
00932          break;
00933       }
00934    }
00935    AST_LIST_TRAVERSE_SAFE_END
00936    AST_LIST_UNLOCK(&verbosers);
00937    
00938    return cur ? 0 : -1;
00939 }

Generated on Thu Oct 8 21:57:27 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.8