b520800cec38311649cda4c3ec6a0e02639488e8
[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", "");
256         else
257                 ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "");
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         if (eventlog) {
327                 init_logger_chain();
328                 ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
329                 if (option_verbose)
330                         ast_verbose("Asterisk Event Logger restarted\n");
331                 return 0;
332         } else 
333                 ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
334         queue_log_init();
335         init_logger_chain();
336         return -1;
337 }
338
339 static int handle_logger_reload(int fd, int argc, char *argv[])
340 {
341         if(reload_logger(0))
342         {
343                 ast_cli(fd, "Failed to reloadthe logger\n");
344                 return RESULT_FAILURE;
345         }
346         else
347                 return RESULT_SUCCESS;
348 }
349
350 static int handle_logger_rotate(int fd, int argc, char *argv[])
351 {
352         if(reload_logger(1))
353         {
354                 ast_cli(fd, "Failed to reloadthe logger\n");
355                 return RESULT_FAILURE;
356         }
357         else
358                 return RESULT_SUCCESS;
359 }
360
361 static struct verb {
362         void (*verboser)(const char *string, int opos, int replacelast, int complete);
363         struct verb *next;
364 } *verboser = NULL;
365
366
367 static char logger_reload_help[] =
368 "Usage: logger reload\n"
369 "       Reloads the logger subsystem state.  Use after restarting syslogd(8)\n";
370
371 static char logger_rotate_help[] =
372 "Usage: logger reload\n"
373 "       Rotates and Reopens the log files.\n";
374
375 static struct ast_cli_entry reload_logger_cli = 
376         { { "logger", "reload", NULL }, 
377         handle_logger_reload, "Reopens the log files",
378         logger_reload_help };
379
380 static struct ast_cli_entry rotate_logger_cli = 
381         { { "logger", "rotate", NULL }, 
382         handle_logger_rotate, "Rotates and reopens the log files",
383         logger_rotate_help };
384
385 static int handle_SIGXFSZ(int sig) {
386     reload_logger(1);
387     ast_log(LOG_EVENT,"Rotated Logs Per SIGXFSZ\n");
388     if (option_verbose)
389             ast_verbose("Rotated Logs Per SIGXFSZ\n");
390     return 0;
391 }
392
393 int init_logger(void)
394 {
395         char tmp[256];
396
397         /* auto rotate if sig SIGXFSZ comes a-knockin */
398         (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ);
399
400         /* register the relaod logger cli command */
401         ast_cli_register(&reload_logger_cli);
402         ast_cli_register(&rotate_logger_cli);
403
404         /* create the eventlog */
405         mkdir((char *)ast_config_AST_LOG_DIR, 0755);
406         snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
407         eventlog = fopen((char *)tmp, "a");
408         if (eventlog) {
409                 init_logger_chain();
410                 ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
411                 if (option_verbose)
412                         ast_verbose("Asterisk Event Logger Started %s\n",(char *)tmp);
413                 return 0;
414         } else 
415                 ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
416
417         init_logger_chain();
418
419         /* create log channels */
420         init_logger_chain();
421         /* initialize queue logger */
422         queue_log_init();
423         return -1;
424 }
425
426 static void ast_log_vsyslog(int level, const char *file, int line, const char *function, const char *fmt, va_list args) {
427     char buf[BUFSIZ];
428
429     if(level >= SYSLOG_NLEVELS) {
430             /* we are locked here, so cannot ast_log() */
431             fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level);
432             return;
433     }
434     if(level == __LOG_VERBOSE) {
435         snprintf(buf, sizeof(buf), "VERBOSE[%ld]: ", (long)pthread_self());
436         level = __LOG_DEBUG;
437     } else {
438         snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: ",
439                  levels[level], (long)pthread_self(), file, line, function);
440     }
441     vsnprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), fmt, args);
442     syslog(syslog_level_map[level], buf);
443 }
444
445 /*
446  * send log messages to syslog and/or the console
447  */
448 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
449 {
450     struct logchannel *chan;
451     char buf[BUFSIZ];
452     time_t t;
453     struct tm tm;
454     char date[256];
455
456     va_list ap;
457         
458     if (!option_verbose && !option_debug && (level == __LOG_DEBUG)) {
459         return;
460     }
461
462     /* begin critical section */
463     ast_mutex_lock(&loglock);
464
465     time(&t);
466     localtime_r(&t, &tm);
467     strftime(date, sizeof(date), "%b %e %T", &tm);
468
469
470     if (level == __LOG_EVENT) {
471             va_start(ap, fmt);
472
473             fprintf(eventlog, "%s asterisk[%d]: ", date, getpid());
474             vfprintf(eventlog, fmt, ap);
475             fflush(eventlog);
476
477             va_end(ap);
478             ast_mutex_unlock(&loglock);
479             return;
480     }
481
482     if (logchannels) {
483         chan = logchannels;
484         while(chan) {
485             if (chan->syslog && (chan->logmask & (1 << level))) {
486                 va_start(ap, fmt);
487                 ast_log_vsyslog(level, file, line, function, fmt, ap);
488                 va_end(ap);
489             } else if ((chan->logmask & (1 << level)) && (chan->console)) {
490                 char linestr[128];
491                 char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
492
493                 if(level != __LOG_VERBOSE) {
494                     sprintf(linestr, "%d", line);
495                     snprintf(buf, sizeof(buf), "%s %s[%ld]: %s:%s %s: ",
496                              date,
497                              term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)),
498                              (long)pthread_self(),
499                              term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
500                              term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
501                              term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
502                     
503                     ast_console_puts(buf);
504                     va_start(ap, fmt);
505                     vsnprintf(buf, sizeof(buf), fmt, ap);
506                     va_end(ap);
507                     ast_console_puts(buf);
508                 }
509             } else if ((chan->logmask & (1 << level)) && (chan->fileptr)) {
510                     snprintf(buf, sizeof(buf), "%s %s[%ld]: ", date,
511                              levels[level], (long)pthread_self());
512                     fprintf(chan->fileptr, buf);
513                     va_start(ap, fmt);
514                     vsnprintf(buf, sizeof(buf), fmt, ap);
515                     va_end(ap);
516                     fprintf(chan->fileptr, buf);
517                     fflush(chan->fileptr);
518             }
519             chan = chan->next;
520         }
521     } else {
522             /* 
523              * we don't have the logger chain configured yet,
524              * so just log to stdout 
525              */
526                 if (level != __LOG_VERBOSE) {
527                     va_start(ap, fmt);
528                     vsnprintf(buf, sizeof(buf), fmt, ap);
529                     va_end(ap);
530                     fprintf(stdout, buf);
531                 }
532     }
533
534     ast_mutex_unlock(&loglock);
535     /* end critical section */
536 }
537
538 extern void ast_verbose(const char *fmt, ...)
539 {
540         static char stuff[4096];
541         static int pos = 0, opos;
542         static int replacelast = 0, complete;
543         struct msglist *m;
544         struct verb *v;
545         va_list ap;
546         va_start(ap, fmt);
547         ast_mutex_lock(&msglist_lock);
548         vsnprintf(stuff + pos, sizeof(stuff) - pos, fmt, ap);
549         opos = pos;
550         pos = strlen(stuff);
551         if (fmt[strlen(fmt)-1] == '\n') 
552                 complete = 1;
553         else
554                 complete=0;
555         if (complete) {
556                 if (msgcnt < MAX_MSG_QUEUE) {
557                         /* Allocate new structure */
558                         m = malloc(sizeof(struct msglist));
559                         msgcnt++;
560                 } else {
561                         /* Recycle the oldest entry */
562                         m = list;
563                         list = list->next;
564                         free(m->msg);
565                 }
566                 if (m) {
567                         m->msg = strdup(stuff);
568                         if (m->msg) {
569                                 if (last)
570                                         last->next = m;
571                                 else
572                                         list = m;
573                                 m->next = NULL;
574                                 last = m;
575                         } else {
576                                 msgcnt--;
577                                 ast_log(LOG_ERROR, "Out of memory\n");
578                                 free(m);
579                         }
580                 }
581         }
582         if (verboser) {
583                 v = verboser;
584                 while(v) {
585                         v->verboser(stuff, opos, replacelast, complete);
586                         v = v->next;
587                 }
588         } /* else
589                 fprintf(stdout, stuff + opos); */
590
591         ast_log(LOG_VERBOSE, stuff);
592
593         if (fmt[strlen(fmt)-1] != '\n') 
594                 replacelast = 1;
595         else 
596                 replacelast = pos = 0;
597         va_end(ap);
598
599         ast_mutex_unlock(&msglist_lock);
600 }
601
602 int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete))
603 {
604         struct msglist *m;
605         m = list;
606         ast_mutex_lock(&msglist_lock);
607         while(m) {
608                 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
609                 v(m->msg, 0, 0, 1);
610                 m = m->next;
611         }
612         ast_mutex_unlock(&msglist_lock);
613         return 0;
614 }
615
616 int ast_register_verbose(void (*v)(const char *string, int opos, int replacelast, int complete)) 
617 {
618         struct msglist *m;
619         struct verb *tmp;
620         /* XXX Should be more flexible here, taking > 1 verboser XXX */
621         if ((tmp = malloc(sizeof (struct verb)))) {
622                 tmp->verboser = v;
623                 ast_mutex_lock(&msglist_lock);
624                 tmp->next = verboser;
625                 verboser = tmp;
626                 m = list;
627                 while(m) {
628                         /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
629                         v(m->msg, 0, 0, 1);
630                         m = m->next;
631                 }
632                 ast_mutex_unlock(&msglist_lock);
633                 return 0;
634         }
635         return -1;
636 }
637
638 int ast_unregister_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
639 {
640         int res = -1;
641         struct verb *tmp, *tmpl=NULL;
642         ast_mutex_lock(&msglist_lock);
643         tmp = verboser;
644         while(tmp) {
645                 if (tmp->verboser == v) {
646                         if (tmpl)
647                                 tmpl->next = tmp->next;
648                         else
649                                 verboser = tmp->next;
650                         free(tmp);
651                         break;
652                 }
653                 tmpl = tmp;
654                 tmp = tmp->next;
655         }
656         if (tmp)
657                 res = 0;
658         ast_mutex_unlock(&msglist_lock);
659         return res;
660 }