a22f78d5a7855aa67bea10c1a1c8045d0f831e0b
[asterisk/asterisk.git] / logger.c
1 /*
2  * Asterisk Logger
3  * 
4  * Mark Spencer <markster@marko.net>
5  *
6  * Copyright(C)1999, Linux Support Services, Inc.
7  * 
8  * Distributed under the terms of the GNU General Public License (GPL) Version 2
9  *
10  * Logging routines
11  *
12  */
13
14 #include <signal.h>
15 #include <stdarg.h>
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <time.h>
19 #include <asterisk/lock.h>
20 #include <asterisk/options.h>
21 #include <asterisk/channel.h>
22 #include <asterisk/config.h>
23 #include <asterisk/term.h>
24 #include <asterisk/cli.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <pthread.h>
29 #include <sys/stat.h>
30 #include "asterisk.h"
31 #include "astconf.h"
32
33 #define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values,
34                         from <syslog.h> which is included by logger.h */
35 #include <syslog.h>
36 static int syslog_level_map[] = {
37         LOG_DEBUG,
38         LOG_INFO,    /* arbitrary equivalent of LOG_EVENT */
39         LOG_NOTICE,
40         LOG_WARNING,
41         LOG_ERR,
42         LOG_DEBUG
43 };
44 #define SYSLOG_NLEVELS 6
45
46 #include <asterisk/logger.h>
47
48 #define MAX_MSG_QUEUE 200
49
50 static ast_mutex_t msglist_lock = AST_MUTEX_INITIALIZER;
51 static ast_mutex_t loglock = AST_MUTEX_INITIALIZER;
52
53 static struct msglist {
54         char *msg;
55         struct msglist *next;
56 } *list = NULL, *last = NULL;
57
58 struct logchannel {
59         int logmask;
60         int facility; /* syslog */
61         int syslog; /* syslog flag */
62         int console;  /* console logging */
63         FILE *fileptr; /* logfile logging */
64         char filename[256];
65         struct logchannel *next;
66 };
67
68 static struct logchannel *logchannels = NULL;
69
70 static int msgcnt = 0;
71
72 static FILE *eventlog = NULL;
73
74 static char *levels[] = {
75        "DEBUG",
76        "EVENT",
77        "NOTICE",
78        "WARNING",
79        "ERROR",
80        "VERBOSE"
81 };
82
83 static int colors[] = {
84        COLOR_BRGREEN,
85        COLOR_BRBLUE,
86        COLOR_YELLOW,
87        COLOR_BRRED,
88        COLOR_RED,
89        COLOR_GREEN
90 };
91
92 static int make_components(char *s, int lineno)
93 {
94         char *w;
95         int res = 0;
96         char *stringp=NULL;
97         stringp=s;
98         w = strsep(&stringp, ",");
99         while(w) {
100             while(*w && (*w < 33))
101                 w++;
102             if (!strcasecmp(w, "error")) 
103                 res |= (1 << __LOG_ERROR);
104             else if (!strcasecmp(w, "warning"))
105                 res |= (1 << __LOG_WARNING);
106             else if (!strcasecmp(w, "notice"))
107                 res |= (1 << __LOG_NOTICE);
108             else if (!strcasecmp(w, "event"))
109                 res |= (1 << __LOG_EVENT);
110             else if (!strcasecmp(w, "debug"))
111                 res |= (1 << __LOG_DEBUG);
112             else if (!strcasecmp(w, "verbose"))
113                 res |= (1 << __LOG_VERBOSE);
114             else {
115                 fprintf(stderr, "Logfile Warning: Unknown keyword '%s' at line %d of logger.conf\n", w, lineno);
116             }
117             w = strsep(&stringp, ",");
118         }
119         return res;
120 }
121
122 static struct logchannel *make_logchannel(char *channel, char *components, int lineno)
123 {
124         struct logchannel *chan;
125         char *facility;
126         CODE *cptr;
127
128         if (!strlen(channel))
129                 return NULL;
130         chan = malloc(sizeof(struct logchannel));
131
132         if (chan) {
133                 memset(chan, 0, sizeof(struct logchannel));
134                 if (!strcasecmp(channel, "console")) {
135                     chan->console = 1;
136                 } else if (!strncasecmp(channel, "syslog", 6)) {
137                     /*
138                      * syntax is:
139                      *  syslog.facility => level,level,level
140                      */
141                     facility = strchr(channel, '.');
142                     if(!facility++ || !facility) {
143                         facility = "local0";
144                     }
145                     /*
146                      * Walk through the list of facilitynames (defined in sys/syslog.h)
147                      * to see if we can find the one we have been given
148                      */
149                     chan->facility = -1;
150                     cptr = facilitynames;
151                     while (cptr->c_name) {
152                         if (!strncasecmp(facility, cptr->c_name, sizeof(cptr->c_name))) {
153                             chan->facility = cptr->c_val;
154                             break;
155                         }
156                         cptr++;
157                     }
158                     if (0 > chan->facility) {
159                         fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
160                         free(chan);
161                         return NULL;
162                     }
163
164                     chan->syslog = 1;
165                     openlog("asterisk", LOG_PID, chan->facility);
166                 } else {
167                         if (channel[0] == '/') 
168                                 strncpy(chan->filename, channel, sizeof(chan->filename) - 1);
169                         else
170                                 snprintf(chan->filename, sizeof(chan->filename), "%s/%s", (char *)ast_config_AST_LOG_DIR, channel);
171                         chan->fileptr = fopen(chan->filename, "a");
172                         if (!chan->fileptr) {
173                                 /* Can't log here, since we're called with a lock */
174                                 fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", chan->filename, strerror(errno));
175                         }
176                 }
177                 chan->logmask = make_components(components, lineno);
178         }
179         return chan;
180 }
181
182 static void init_logger_chain(void)
183 {
184         struct logchannel *chan, *cur;
185         struct ast_config *cfg;
186         struct ast_variable *var;
187
188         /* delete our list of log channels */
189         ast_mutex_lock(&loglock);
190         chan = logchannels;
191         while (chan) {
192             cur = chan->next;
193             free(chan);
194             chan = cur;
195         }
196         logchannels = NULL;
197         ast_mutex_unlock(&loglock);
198
199         /* close syslog */
200         closelog();
201
202         cfg = ast_load("logger.conf");
203         
204         /* If no config file, we're fine */
205         if (!cfg)
206             return;
207
208         ast_mutex_lock(&loglock);
209         var = ast_variable_browse(cfg, "logfiles");
210         while(var) {
211                 chan = make_logchannel(var->name, var->value, var->lineno);
212                 if (chan) {
213                         chan->next = logchannels;
214                         logchannels = chan;
215                 }
216                 var = var->next;
217         }
218
219         ast_destroy(cfg);
220         ast_mutex_unlock(&loglock);
221 }
222
223 static FILE *qlog = NULL;
224 static ast_mutex_t qloglock = AST_MUTEX_INITIALIZER;
225
226 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
227 {
228         va_list ap;
229         ast_mutex_lock(&qloglock);
230         if (qlog) {
231                 va_start(ap, fmt);
232                 fprintf(qlog, "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
233                 vfprintf(qlog, fmt, ap);
234                 fprintf(qlog, "\n");
235                 va_end(ap);
236                 fflush(qlog);
237         }
238         ast_mutex_unlock(&qloglock);
239 }
240
241 static void queue_log_init(void)
242 {
243         char filename[256];
244         int reloaded = 0;
245         ast_mutex_lock(&qloglock);
246         if (qlog) {
247                 reloaded = 1;
248                 fclose(qlog);
249                 qlog = NULL;
250         }
251         snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_LOG_DIR, "queue_log");
252         qlog = fopen(filename, "a");
253         ast_mutex_unlock(&qloglock);
254         if (reloaded) 
255                 ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
256         else
257                 ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
258 }
259
260 int reload_logger(int rotate)
261 {
262         char old[AST_CONFIG_MAX_PATH];
263         char new[AST_CONFIG_MAX_PATH];
264         struct logchannel *f;
265         FILE *myf;
266
267         int x;
268
269         ast_mutex_lock(&loglock);
270         if (eventlog) 
271                 fclose(eventlog);
272         else 
273                 rotate = 0;
274         eventlog = NULL;
275
276
277
278         mkdir((char *)ast_config_AST_LOG_DIR, 0755);
279         snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
280
281         if(rotate) {
282                 for(x=0;;x++) {
283                         snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, EVENTLOG,x);
284                         myf = fopen((char *)new, "r");
285                         if(myf) 
286                                 fclose(myf);
287                         else
288                                 break;
289                 }
290         
291                 /* do it */
292                 if (rename(old,new))
293                         fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
294         }
295
296         eventlog = fopen(old, "a");
297
298         f = logchannels;
299         while(f) {
300                 if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
301                         fclose(f->fileptr);
302                         f->fileptr = NULL;
303                         if(rotate) {
304                                 strncpy(old, f->filename, sizeof(old));
305         
306                                 for(x=0;;x++) {
307                                         snprintf(new, sizeof(new), "%s.%d", f->filename, x);
308                                         myf = fopen((char *)new, "r");
309                                         if (myf) {
310                                                 fclose(myf);
311                                         } else {
312                                                 break;
313                                         }
314                                 }
315             
316                                 /* do it */
317                                 if (rename(old,new))
318                                         fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
319                         }
320                 }
321                 f = f->next;
322         }
323
324         ast_mutex_unlock(&loglock);
325
326         queue_log_init();
327
328         if (eventlog) {
329                 init_logger_chain();
330                 ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
331                 if (option_verbose)
332                         ast_verbose("Asterisk Event Logger restarted\n");
333                 return 0;
334         } else 
335                 ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
336         init_logger_chain();
337         return -1;
338 }
339
340 static int handle_logger_reload(int fd, int argc, char *argv[])
341 {
342         if(reload_logger(0))
343         {
344                 ast_cli(fd, "Failed to reloadthe logger\n");
345                 return RESULT_FAILURE;
346         }
347         else
348                 return RESULT_SUCCESS;
349 }
350
351 static int handle_logger_rotate(int fd, int argc, char *argv[])
352 {
353         if(reload_logger(1))
354         {
355                 ast_cli(fd, "Failed to reloadthe logger\n");
356                 return RESULT_FAILURE;
357         }
358         else
359                 return RESULT_SUCCESS;
360 }
361
362 static struct verb {
363         void (*verboser)(const char *string, int opos, int replacelast, int complete);
364         struct verb *next;
365 } *verboser = NULL;
366
367
368 static char logger_reload_help[] =
369 "Usage: logger reload\n"
370 "       Reloads the logger subsystem state.  Use after restarting syslogd(8)\n";
371
372 static char logger_rotate_help[] =
373 "Usage: logger reload\n"
374 "       Rotates and Reopens the log files.\n";
375
376 static struct ast_cli_entry reload_logger_cli = 
377         { { "logger", "reload", NULL }, 
378         handle_logger_reload, "Reopens the log files",
379         logger_reload_help };
380
381 static struct ast_cli_entry rotate_logger_cli = 
382         { { "logger", "rotate", NULL }, 
383         handle_logger_rotate, "Rotates and reopens the log files",
384         logger_rotate_help };
385
386 static int handle_SIGXFSZ(int sig) {
387     reload_logger(1);
388     ast_log(LOG_EVENT,"Rotated Logs Per SIGXFSZ\n");
389     if (option_verbose)
390             ast_verbose("Rotated Logs Per SIGXFSZ\n");
391     return 0;
392 }
393
394 int init_logger(void)
395 {
396         char tmp[256];
397
398         /* auto rotate if sig SIGXFSZ comes a-knockin */
399         (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ);
400
401         /* register the relaod logger cli command */
402         ast_cli_register(&reload_logger_cli);
403         ast_cli_register(&rotate_logger_cli);
404
405         /* initialize queue logger */
406         queue_log_init();
407
408         /* create the eventlog */
409         mkdir((char *)ast_config_AST_LOG_DIR, 0755);
410         snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
411         eventlog = fopen((char *)tmp, "a");
412         if (eventlog) {
413                 init_logger_chain();
414                 ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
415                 if (option_verbose)
416                         ast_verbose("Asterisk Event Logger Started %s\n",(char *)tmp);
417                 return 0;
418         } else 
419                 ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
420
421         init_logger_chain();
422
423         /* create log channels */
424         init_logger_chain();
425         return -1;
426 }
427
428 static void ast_log_vsyslog(int level, const char *file, int line, const char *function, const char *fmt, va_list args) {
429     char buf[BUFSIZ];
430
431     if(level >= SYSLOG_NLEVELS) {
432             /* we are locked here, so cannot ast_log() */
433             fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level);
434             return;
435     }
436     if(level == __LOG_VERBOSE) {
437         snprintf(buf, sizeof(buf), "VERBOSE[%ld]: ", (long)pthread_self());
438         level = __LOG_DEBUG;
439     } else {
440         snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: ",
441                  levels[level], (long)pthread_self(), file, line, function);
442     }
443     vsnprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), fmt, args);
444     syslog(syslog_level_map[level], buf);
445 }
446
447 /*
448  * send log messages to syslog and/or the console
449  */
450 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
451 {
452     struct logchannel *chan;
453     char buf[BUFSIZ];
454     time_t t;
455     struct tm tm;
456     char date[256];
457
458     va_list ap;
459         
460     if (!option_verbose && !option_debug && (level == __LOG_DEBUG)) {
461         return;
462     }
463
464     /* begin critical section */
465     ast_mutex_lock(&loglock);
466
467     time(&t);
468     localtime_r(&t, &tm);
469     strftime(date, sizeof(date), "%b %e %T", &tm);
470
471
472     if (level == __LOG_EVENT) {
473             va_start(ap, fmt);
474
475             fprintf(eventlog, "%s asterisk[%d]: ", date, getpid());
476             vfprintf(eventlog, fmt, ap);
477             fflush(eventlog);
478
479             va_end(ap);
480             ast_mutex_unlock(&loglock);
481             return;
482     }
483
484     if (logchannels) {
485         chan = logchannels;
486         while(chan) {
487             if (chan->syslog && (chan->logmask & (1 << level))) {
488                 va_start(ap, fmt);
489                 ast_log_vsyslog(level, file, line, function, fmt, ap);
490                 va_end(ap);
491             } else if ((chan->logmask & (1 << level)) && (chan->console)) {
492                 char linestr[128];
493                 char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
494
495                 if(level != __LOG_VERBOSE) {
496                     sprintf(linestr, "%d", line);
497                     snprintf(buf, sizeof(buf), "%s %s[%ld]: %s:%s %s: ",
498                              date,
499                              term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)),
500                              (long)pthread_self(),
501                              term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
502                              term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
503                              term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
504                     
505                     ast_console_puts(buf);
506                     va_start(ap, fmt);
507                     vsnprintf(buf, sizeof(buf), fmt, ap);
508                     va_end(ap);
509                     ast_console_puts(buf);
510                 }
511             } else if ((chan->logmask & (1 << level)) && (chan->fileptr)) {
512                     snprintf(buf, sizeof(buf), "%s %s[%ld]: ", date,
513                              levels[level], (long)pthread_self());
514                     fprintf(chan->fileptr, buf);
515                     va_start(ap, fmt);
516                     vsnprintf(buf, sizeof(buf), fmt, ap);
517                     va_end(ap);
518                     fprintf(chan->fileptr, buf);
519                     fflush(chan->fileptr);
520             }
521             chan = chan->next;
522         }
523     } else {
524             /* 
525              * we don't have the logger chain configured yet,
526              * so just log to stdout 
527              */
528                 if (level != __LOG_VERBOSE) {
529                     va_start(ap, fmt);
530                     vsnprintf(buf, sizeof(buf), fmt, ap);
531                     va_end(ap);
532                     fprintf(stdout, buf);
533                 }
534     }
535
536     ast_mutex_unlock(&loglock);
537     /* end critical section */
538 }
539
540 extern void ast_verbose(const char *fmt, ...)
541 {
542         static char stuff[4096];
543         static int pos = 0, opos;
544         static int replacelast = 0, complete;
545         struct msglist *m;
546         struct verb *v;
547         va_list ap;
548         va_start(ap, fmt);
549         ast_mutex_lock(&msglist_lock);
550         vsnprintf(stuff + pos, sizeof(stuff) - pos, fmt, ap);
551         opos = pos;
552         pos = strlen(stuff);
553         if (fmt[strlen(fmt)-1] == '\n') 
554                 complete = 1;
555         else
556                 complete=0;
557         if (complete) {
558                 if (msgcnt < MAX_MSG_QUEUE) {
559                         /* Allocate new structure */
560                         m = malloc(sizeof(struct msglist));
561                         msgcnt++;
562                 } else {
563                         /* Recycle the oldest entry */
564                         m = list;
565                         list = list->next;
566                         free(m->msg);
567                 }
568                 if (m) {
569                         m->msg = strdup(stuff);
570                         if (m->msg) {
571                                 if (last)
572                                         last->next = m;
573                                 else
574                                         list = m;
575                                 m->next = NULL;
576                                 last = m;
577                         } else {
578                                 msgcnt--;
579                                 ast_log(LOG_ERROR, "Out of memory\n");
580                                 free(m);
581                         }
582                 }
583         }
584         if (verboser) {
585                 v = verboser;
586                 while(v) {
587                         v->verboser(stuff, opos, replacelast, complete);
588                         v = v->next;
589                 }
590         } /* else
591                 fprintf(stdout, stuff + opos); */
592
593         ast_log(LOG_VERBOSE, stuff);
594
595         if (fmt[strlen(fmt)-1] != '\n') 
596                 replacelast = 1;
597         else 
598                 replacelast = pos = 0;
599         va_end(ap);
600
601         ast_mutex_unlock(&msglist_lock);
602 }
603
604 int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete))
605 {
606         struct msglist *m;
607         m = list;
608         ast_mutex_lock(&msglist_lock);
609         while(m) {
610                 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
611                 v(m->msg, 0, 0, 1);
612                 m = m->next;
613         }
614         ast_mutex_unlock(&msglist_lock);
615         return 0;
616 }
617
618 int ast_register_verbose(void (*v)(const char *string, int opos, int replacelast, int complete)) 
619 {
620         struct msglist *m;
621         struct verb *tmp;
622         /* XXX Should be more flexible here, taking > 1 verboser XXX */
623         if ((tmp = malloc(sizeof (struct verb)))) {
624                 tmp->verboser = v;
625                 ast_mutex_lock(&msglist_lock);
626                 tmp->next = verboser;
627                 verboser = tmp;
628                 m = list;
629                 while(m) {
630                         /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
631                         v(m->msg, 0, 0, 1);
632                         m = m->next;
633                 }
634                 ast_mutex_unlock(&msglist_lock);
635                 return 0;
636         }
637         return -1;
638 }
639
640 int ast_unregister_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
641 {
642         int res = -1;
643         struct verb *tmp, *tmpl=NULL;
644         ast_mutex_lock(&msglist_lock);
645         tmp = verboser;
646         while(tmp) {
647                 if (tmp->verboser == v) {
648                         if (tmpl)
649                                 tmpl->next = tmp->next;
650                         else
651                                 verboser = tmp->next;
652                         free(tmp);
653                         break;
654                 }
655                 tmpl = tmp;
656                 tmp = tmp->next;
657         }
658         if (tmp)
659                 res = 0;
660         ast_mutex_unlock(&msglist_lock);
661         return res;
662 }