Sat Mar 24 23:26:05 2007

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

Generated on Sat Mar 24 23:26:05 2007 for Asterisk - the Open Source PBX by  doxygen 1.4.6