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