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

Generated on Sun Apr 18 23:33:52 2004 for Asterisk by doxygen 1.3.6-20040222