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