Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

logger.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk Logger
00003  * 
00004  * Mark Spencer <markster@marko.net>
00005  *
00006  * Copyright(C)1999, Linux Support Services, Inc.
00007  * 
00008  * Distributed under the terms of the GNU General Public License (GPL) Version 2
00009  *
00010  * Logging routines
00011  *
00012  */
00013 
00014 #include <signal.h>
00015 #include <stdarg.h>
00016 #include <stdio.h>
00017 #include <unistd.h>
00018 #include <time.h>
00019 #include <asterisk/lock.h>
00020 #include <asterisk/options.h>
00021 #include <asterisk/channel.h>
00022 #include <asterisk/config.h>
00023 #include <asterisk/term.h>
00024 #include <asterisk/cli.h>
00025 #include <string.h>
00026 #include <stdlib.h>
00027 #include <errno.h>
00028 #include <pthread.h>
00029 #include <sys/stat.h>
00030 #include "asterisk.h"
00031 #include "astconf.h"
00032 
00033 #define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values,
00034               from <syslog.h> which is included by logger.h */
00035 #include <syslog.h>
00036 static int syslog_level_map[] = {
00037    LOG_DEBUG,
00038    LOG_INFO,    /* arbitrary equivalent of LOG_EVENT */
00039    LOG_NOTICE,
00040    LOG_WARNING,
00041    LOG_ERR,
00042         LOG_DEBUG
00043 };
00044 #define SYSLOG_NLEVELS 6
00045 
00046 #include <asterisk/logger.h>
00047 
00048 #define MAX_MSG_QUEUE 200
00049 
00050 static ast_mutex_t msglist_lock = AST_MUTEX_INITIALIZER;
00051 static ast_mutex_t loglock = AST_MUTEX_INITIALIZER;
00052 
00053 static struct msglist {
00054    char *msg;
00055    struct msglist *next;
00056 } *list = NULL, *last = NULL;
00057 
00058 struct logchannel {
00059         int logmask;
00060         int facility; /* syslog */
00061    int syslog; /* syslog flag */
00062         int console;  /* console logging */
00063    FILE *fileptr; /* logfile logging */
00064    char filename[256];
00065         struct logchannel *next;
00066 };
00067 
00068 static struct logchannel *logchannels = NULL;
00069 
00070 static int msgcnt = 0;
00071 
00072 static FILE *eventlog = NULL;
00073 
00074 static char *levels[] = {
00075        "DEBUG",
00076        "EVENT",
00077        "NOTICE",
00078        "WARNING",
00079        "ERROR",
00080        "VERBOSE"
00081 };
00082 
00083 static int colors[] = {
00084        COLOR_BRGREEN,
00085        COLOR_BRBLUE,
00086        COLOR_YELLOW,
00087        COLOR_BRRED,
00088        COLOR_RED,
00089        COLOR_GREEN
00090 };
00091 
00092 static int make_components(char *s, int lineno)
00093 {
00094    char *w;
00095    int res = 0;
00096    char *stringp=NULL;
00097    stringp=s;
00098    w = strsep(&stringp, ",");
00099    while(w) {
00100        while(*w && (*w < 33))
00101       w++;
00102        if (!strcasecmp(w, "error")) 
00103       res |= (1 << __LOG_ERROR);
00104        else if (!strcasecmp(w, "warning"))
00105       res |= (1 << __LOG_WARNING);
00106        else if (!strcasecmp(w, "notice"))
00107       res |= (1 << __LOG_NOTICE);
00108        else if (!strcasecmp(w, "event"))
00109       res |= (1 << __LOG_EVENT);
00110        else if (!strcasecmp(w, "debug"))
00111       res |= (1 << __LOG_DEBUG);
00112        else if (!strcasecmp(w, "verbose"))
00113       res |= (1 << __LOG_VERBOSE);
00114        else {
00115       fprintf(stderr, "Logfile Warning: Unknown keyword '%s' at line %d of logger.conf\n", w, lineno);
00116        }
00117        w = strsep(&stringp, ",");
00118    }
00119    return res;
00120 }
00121 
00122 static struct logchannel *make_logchannel(char *channel, char *components, int lineno)
00123 {
00124    struct logchannel *chan;
00125    char *facility;
00126    CODE *cptr;
00127 
00128    if (!strlen(channel))
00129       return NULL;
00130    chan = malloc(sizeof(struct logchannel));
00131 
00132    if (chan) {
00133       memset(chan, 0, sizeof(struct logchannel));
00134       if (!strcasecmp(channel, "console")) {
00135           chan->console = 1;
00136       } else if (!strncasecmp(channel, "syslog", 6)) {
00137           /*
00138            * syntax is:
00139            *  syslog.facility => level,level,level
00140            */
00141           facility = strchr(channel, '.');
00142           if(!facility++ || !facility) {
00143          facility = "local0";
00144           }
00145           /*
00146            * Walk through the list of facilitynames (defined in sys/syslog.h)
00147            * to see if we can find the one we have been given
00148            */
00149           chan->facility = -1;
00150           cptr = facilitynames;
00151           while (cptr->c_name) {
00152          if (!strncasecmp(facility, cptr->c_name, sizeof(cptr->c_name))) {
00153              chan->facility = cptr->c_val;
00154              break;
00155          }
00156          cptr++;
00157           }
00158           if (0 > chan->facility) {
00159          fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
00160          free(chan);
00161          return NULL;
00162           }
00163 
00164           chan->syslog = 1;
00165           openlog("asterisk", LOG_PID, chan->facility);
00166       } else {
00167          if (channel[0] == '/') 
00168             strncpy(chan->filename, channel, sizeof(chan->filename) - 1);
00169          else
00170             snprintf(chan->filename, sizeof(chan->filename), "%s/%s", (char *)ast_config_AST_LOG_DIR, channel);
00171          chan->fileptr = fopen(chan->filename, "a");
00172          if (!chan->fileptr) {
00173             /* Can't log here, since we're called with a lock */
00174             fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", chan->filename, strerror(errno));
00175          }
00176       }
00177       chan->logmask = make_components(components, lineno);
00178    }
00179    return chan;
00180 }
00181 
00182 static void init_logger_chain(void)
00183 {
00184    struct logchannel *chan, *cur;
00185    struct ast_config *cfg;
00186    struct ast_variable *var;
00187 
00188    /* delete our list of log channels */
00189    ast_mutex_lock(&loglock);
00190    chan = logchannels;
00191    while (chan) {
00192        cur = chan->next;
00193        free(chan);
00194        chan = cur;
00195    }
00196    logchannels = NULL;
00197    ast_mutex_unlock(&loglock);
00198 
00199    /* close syslog */
00200    closelog();
00201 
00202    cfg = ast_load("logger.conf");
00203    
00204    /* If no config file, we're fine */
00205    if (!cfg)
00206        return;
00207 
00208    ast_mutex_lock(&loglock);
00209    var = ast_variable_browse(cfg, "logfiles");
00210    while(var) {
00211       chan = make_logchannel(var->name, var->value, var->lineno);
00212       if (chan) {
00213          chan->next = logchannels;
00214          logchannels = chan;
00215       }
00216       var = var->next;
00217    }
00218 
00219    ast_destroy(cfg);
00220    ast_mutex_unlock(&loglock);
00221 }
00222 
00223 int reload_logger(int rotate)
00224 {
00225    char old[AST_CONFIG_MAX_PATH];
00226    char new[AST_CONFIG_MAX_PATH];
00227    struct logchannel *f;
00228    FILE *myf;
00229 
00230    int x;
00231 
00232    ast_mutex_lock(&loglock);
00233    if (eventlog) 
00234       fclose(eventlog);
00235    else 
00236       rotate = 0;
00237    eventlog = NULL;
00238 
00239 
00240 
00241    mkdir((char *)ast_config_AST_LOG_DIR, 0755);
00242    snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
00243 
00244    if(rotate) {
00245       for(x=0;;x++) {
00246          snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, EVENTLOG,x);
00247          myf = fopen((char *)new, "r");
00248          if(myf) 
00249             fclose(myf);
00250          else
00251             break;
00252       }
00253    
00254       /* do it */
00255       if (rename(old,new))
00256          fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
00257    }
00258 
00259    eventlog = fopen(old, "a");
00260 
00261    f = logchannels;
00262    while(f) {
00263       if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
00264          fclose(f->fileptr);
00265          f->fileptr = NULL;
00266          if(rotate) {
00267             strncpy(old, f->filename, sizeof(old));
00268    
00269             for(x=0;;x++) {
00270                snprintf(new, sizeof(new), "%s.%d", f->filename, x);
00271                myf = fopen((char *)new, "r");
00272                if (myf) {
00273                   fclose(myf);
00274                } else {
00275                   break;
00276                }
00277             }
00278        
00279             /* do it */
00280             if (rename(old,new))
00281                fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
00282          }
00283       }
00284       f = f->next;
00285    }
00286 
00287    ast_mutex_unlock(&loglock);
00288 
00289    if (eventlog) {
00290       init_logger_chain();
00291       ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
00292       if (option_verbose)
00293          ast_verbose("Asterisk Event Logger restarted\n");
00294       return 0;
00295    } else 
00296       ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
00297    init_logger_chain();
00298    return -1;
00299 }
00300 
00301 static int handle_logger_reload(int fd, int argc, char *argv[])
00302 {
00303    if(reload_logger(0))
00304    {
00305       ast_cli(fd, "Failed to reloadthe logger\n");
00306       return RESULT_FAILURE;
00307    }
00308    else
00309       return RESULT_SUCCESS;
00310 }
00311 
00312 static int handle_logger_rotate(int fd, int argc, char *argv[])
00313 {
00314    if(reload_logger(1))
00315    {
00316       ast_cli(fd, "Failed to reloadthe logger\n");
00317       return RESULT_FAILURE;
00318    }
00319    else
00320       return RESULT_SUCCESS;
00321 }
00322 
00323 static struct verb {
00324    void (*verboser)(const char *string, int opos, int replacelast, int complete);
00325    struct verb *next;
00326 } *verboser = NULL;
00327 
00328 
00329 static char logger_reload_help[] =
00330 "Usage: logger reload\n"
00331 "       Reloads the logger subsystem state.  Use after restarting syslogd(8)\n";
00332 
00333 static char logger_rotate_help[] =
00334 "Usage: logger reload\n"
00335 "       Rotates and Reopens the log files.\n";
00336 
00337 static struct ast_cli_entry reload_logger_cli = 
00338    { { "logger", "reload", NULL }, 
00339    handle_logger_reload, "Reopens the log files",
00340    logger_reload_help };
00341 
00342 static struct ast_cli_entry rotate_logger_cli = 
00343    { { "logger", "rotate", NULL }, 
00344    handle_logger_rotate, "Rotates and reopens the log files",
00345    logger_rotate_help };
00346 
00347 static int handle_SIGXFSZ(int sig) {
00348     reload_logger(1);
00349     ast_log(LOG_EVENT,"Rotated Logs Per SIGXFSZ\n");
00350     if (option_verbose)
00351        ast_verbose("Rotated Logs Per SIGXFSZ\n");
00352     return 0;
00353 }
00354 
00355 int init_logger(void)
00356 {
00357    char tmp[256];
00358 
00359    /* auto rotate if sig SIGXFSZ comes a-knockin */
00360    (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ);
00361 
00362    /* register the relaod logger cli command */
00363    ast_cli_register(&reload_logger_cli);
00364    ast_cli_register(&rotate_logger_cli);
00365 
00366    /* create the eventlog */
00367    mkdir((char *)ast_config_AST_LOG_DIR, 0755);
00368    snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
00369    eventlog = fopen((char *)tmp, "a");
00370    if (eventlog) {
00371       init_logger_chain();
00372       ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
00373       if (option_verbose)
00374          ast_verbose("Asterisk Event Logger Started %s\n",(char *)tmp);
00375       return 0;
00376    } else 
00377       ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
00378 
00379    init_logger_chain();
00380 
00381    /* create log channels */
00382    init_logger_chain();
00383    return -1;
00384 }
00385 
00386 static void ast_log_vsyslog(int level, const char *file, int line, const char *function, const char *fmt, va_list args) {
00387     char buf[BUFSIZ];
00388 
00389     if(level >= SYSLOG_NLEVELS) {
00390        /* we are locked here, so cannot ast_log() */
00391        fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level);
00392        return;
00393     }
00394     if(level == __LOG_VERBOSE) {
00395    snprintf(buf, sizeof(buf), "VERBOSE[%ld]: ", (long)pthread_self());
00396    level = __LOG_DEBUG;
00397     } else {
00398    snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: ",
00399        levels[level], (long)pthread_self(), file, line, function);
00400     }
00401     vsnprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), fmt, args);
00402     syslog(syslog_level_map[level], buf);
00403 }
00404 
00405 /*
00406  * send log messages to syslog and/or the console
00407  */
00408 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
00409 {
00410     struct logchannel *chan;
00411     char buf[BUFSIZ];
00412     time_t t;
00413     struct tm tm;
00414     char date[256];
00415 
00416     va_list ap;
00417    
00418     if (!option_verbose && !option_debug && (level == __LOG_DEBUG)) {
00419    return;
00420     }
00421 
00422     /* begin critical section */
00423     ast_mutex_lock(&loglock);
00424 
00425     time(&t);
00426     localtime_r(&t, &tm);
00427     strftime(date, sizeof(date), "%b %e %T", &tm);
00428 
00429 
00430     if (level == __LOG_EVENT) {
00431        va_start(ap, fmt);
00432 
00433        fprintf(eventlog, "%s asterisk[%d]: ", date, getpid());
00434        vfprintf(eventlog, fmt, ap);
00435        fflush(eventlog);
00436 
00437        va_end(ap);
00438        ast_mutex_unlock(&loglock);
00439        return;
00440     }
00441 
00442     if (logchannels) {
00443    chan = logchannels;
00444    while(chan) {
00445        if (chan->syslog && (chan->logmask & (1 << level))) {
00446       va_start(ap, fmt);
00447       ast_log_vsyslog(level, file, line, function, fmt, ap);
00448       va_end(ap);
00449        } else if ((chan->logmask & (1 << level)) && (chan->console)) {
00450       char linestr[128];
00451       char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
00452 
00453       if(level != __LOG_VERBOSE) {
00454           sprintf(linestr, "%d", line);
00455           snprintf(buf, sizeof(buf), "%s %s[%ld]: %s:%s %s: ",
00456               date,
00457               term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)),
00458               (long)pthread_self(),
00459               term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
00460               term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
00461               term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
00462           
00463           ast_console_puts(buf);
00464           va_start(ap, fmt);
00465           vsnprintf(buf, sizeof(buf), fmt, ap);
00466           va_end(ap);
00467           ast_console_puts(buf);
00468       }
00469        } else if ((chan->logmask & (1 << level)) && (chan->fileptr)) {
00470           snprintf(buf, sizeof(buf), "%s %s[%ld]: ", date,
00471               levels[level], (long)pthread_self());
00472           fprintf(chan->fileptr, buf);
00473           va_start(ap, fmt);
00474           vsnprintf(buf, sizeof(buf), fmt, ap);
00475           va_end(ap);
00476           fprintf(chan->fileptr, buf);
00477           fflush(chan->fileptr);
00478        }
00479        chan = chan->next;
00480    }
00481     } else {
00482        /* 
00483         * we don't have the logger chain configured yet,
00484         * so just log to stdout 
00485         */
00486       if (level != __LOG_VERBOSE) {
00487           va_start(ap, fmt);
00488           vsnprintf(buf, sizeof(buf), fmt, ap);
00489           va_end(ap);
00490           fprintf(stdout, buf);
00491       }
00492     }
00493 
00494     ast_mutex_unlock(&loglock);
00495     /* end critical section */
00496 }
00497 
00498 extern void ast_verbose(const char *fmt, ...)
00499 {
00500    static char stuff[4096];
00501    static int pos = 0, opos;
00502    static int replacelast = 0, complete;
00503    struct msglist *m;
00504    struct verb *v;
00505    va_list ap;
00506    va_start(ap, fmt);
00507    ast_mutex_lock(&msglist_lock);
00508    vsnprintf(stuff + pos, sizeof(stuff) - pos, fmt, ap);
00509    opos = pos;
00510    pos = strlen(stuff);
00511    if (fmt[strlen(fmt)-1] == '\n') 
00512       complete = 1;
00513    else
00514       complete=0;
00515    if (complete) {
00516       if (msgcnt < MAX_MSG_QUEUE) {
00517          /* Allocate new structure */
00518          m = malloc(sizeof(struct msglist));
00519          msgcnt++;
00520       } else {
00521          /* Recycle the oldest entry */
00522          m = list;
00523          list = list->next;
00524          free(m->msg);
00525       }
00526       if (m) {
00527          m->msg = strdup(stuff);
00528          if (m->msg) {
00529             if (last)
00530                last->next = m;
00531             else
00532                list = m;
00533             m->next = NULL;
00534             last = m;
00535          } else {
00536             msgcnt--;
00537             ast_log(LOG_ERROR, "Out of memory\n");
00538             free(m);
00539          }
00540       }
00541    }
00542    if (verboser) {
00543       v = verboser;
00544       while(v) {
00545          v->verboser(stuff, opos, replacelast, complete);
00546          v = v->next;
00547       }
00548    } /* else
00549       fprintf(stdout, stuff + opos); */
00550 
00551    ast_log(LOG_VERBOSE, stuff);
00552 
00553    if (fmt[strlen(fmt)-1] != '\n') 
00554       replacelast = 1;
00555    else 
00556       replacelast = pos = 0;
00557    va_end(ap);
00558 
00559    ast_mutex_unlock(&msglist_lock);
00560 }
00561 
00562 int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete))
00563 {
00564    struct msglist *m;
00565    m = list;
00566    ast_mutex_lock(&msglist_lock);
00567    while(m) {
00568       /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
00569       v(m->msg, 0, 0, 1);
00570       m = m->next;
00571    }
00572    ast_mutex_unlock(&msglist_lock);
00573    return 0;
00574 }
00575 
00576 int ast_register_verbose(void (*v)(const char *string, int opos, int replacelast, int complete)) 
00577 {
00578    struct msglist *m;
00579    struct verb *tmp;
00580    /* XXX Should be more flexible here, taking > 1 verboser XXX */
00581    if ((tmp = malloc(sizeof (struct verb)))) {
00582       tmp->verboser = v;
00583       ast_mutex_lock(&msglist_lock);
00584       tmp->next = verboser;
00585       verboser = tmp;
00586       m = list;
00587       while(m) {
00588          /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
00589          v(m->msg, 0, 0, 1);
00590          m = m->next;
00591       }
00592       ast_mutex_unlock(&msglist_lock);
00593       return 0;
00594    }
00595    return -1;
00596 }
00597 
00598 int ast_unregister_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
00599 {
00600    int res = -1;
00601    struct verb *tmp, *tmpl=NULL;
00602    ast_mutex_lock(&msglist_lock);
00603    tmp = verboser;
00604    while(tmp) {
00605       if (tmp->verboser == v) {
00606          if (tmpl)
00607             tmpl->next = tmp->next;
00608          else
00609             verboser = tmp->next;
00610          free(tmp);
00611          break;
00612       }
00613       tmpl = tmp;
00614       tmp = tmp->next;
00615    }
00616    if (tmp)
00617       res = 0;
00618    ast_mutex_unlock(&msglist_lock);
00619    return res;
00620 }

Generated on Fri Feb 27 12:19:43 2004 for Asterisk by doxygen 1.3.5