Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:49 2007

Asterisk developer's documentation :: Codename Pineapple


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

Asterisk is a trademark for Digium, inc.. | Edvina.net | Asterisk.org | This documentation was generated with Doxygen