German language improvements (bug #1606)
[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 <pthread.h>
30 #include <sys/stat.h>
31 #include "asterisk.h"
32 #include "astconf.h"
33
34 #define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values,
35                         from <syslog.h> which is included by logger.h */
36 #include <syslog.h>
37 static int syslog_level_map[] = {
38         LOG_DEBUG,
39         LOG_INFO,    /* arbitrary equivalent of LOG_EVENT */
40         LOG_NOTICE,
41         LOG_WARNING,
42         LOG_ERR,
43         LOG_DEBUG
44 };
45 #define SYSLOG_NLEVELS 6
46
47 #include <asterisk/logger.h>
48
49 #define MAX_MSG_QUEUE 200
50
51 static ast_mutex_t msglist_lock = AST_MUTEX_INITIALIZER;
52 static ast_mutex_t loglock = AST_MUTEX_INITIALIZER;
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
190         /* delete our list of log channels */
191         ast_mutex_lock(&loglock);
192         chan = logchannels;
193         while (chan) {
194             cur = chan->next;
195             free(chan);
196             chan = cur;
197         }
198         logchannels = NULL;
199         ast_mutex_unlock(&loglock);
200
201         /* close syslog */
202         closelog();
203
204         cfg = ast_load("logger.conf");
205         
206         /* If no config file, we're fine */
207         if (!cfg)
208             return;
209
210         ast_mutex_lock(&loglock);
211         var = ast_variable_browse(cfg, "logfiles");
212         while(var) {
213                 chan = make_logchannel(var->name, var->value, var->lineno);
214                 if (chan) {
215                         chan->next = logchannels;
216                         logchannels = chan;
217                 }
218                 var = var->next;
219         }
220
221         ast_destroy(cfg);
222         ast_mutex_unlock(&loglock);
223 }
224
225 static FILE *qlog = NULL;
226 static ast_mutex_t qloglock = AST_MUTEX_INITIALIZER;
227
228 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
229 {
230         va_list ap;
231         ast_mutex_lock(&qloglock);
232         if (qlog) {
233                 va_start(ap, fmt);
234                 fprintf(qlog, "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
235                 vfprintf(qlog, fmt, ap);
236                 fprintf(qlog, "\n");
237                 va_end(ap);
238                 fflush(qlog);
239         }
240         ast_mutex_unlock(&qloglock);
241 }
242
243 static void queue_log_init(void)
244 {
245         char filename[256];
246         int reloaded = 0;
247         ast_mutex_lock(&qloglock);
248         if (qlog) {
249                 reloaded = 1;
250                 fclose(qlog);
251                 qlog = NULL;
252         }
253         snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_LOG_DIR, "queue_log");
254         qlog = fopen(filename, "a");
255         ast_mutex_unlock(&qloglock);
256         if (reloaded) 
257                 ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
258         else
259                 ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
260 }
261
262 int reload_logger(int rotate)
263 {
264         char old[AST_CONFIG_MAX_PATH];
265         char new[AST_CONFIG_MAX_PATH];
266         struct logchannel *f;
267         FILE *myf;
268
269         int x;
270         ast_mutex_lock(&loglock);
271         if (eventlog) 
272                 fclose(eventlog);
273         else 
274                 rotate = 0;
275         eventlog = NULL;
276
277
278
279         mkdir((char *)ast_config_AST_LOG_DIR, 0755);
280         snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
281
282         if(rotate) {
283                 for(x=0;;x++) {
284                         snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, EVENTLOG,x);
285                         myf = fopen((char *)new, "r");
286                         if(myf) 
287                                 fclose(myf);
288                         else
289                                 break;
290                 }
291         
292                 /* do it */
293                 if (rename(old,new))
294                         fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
295         }
296
297         eventlog = fopen(old, "a");
298
299         f = logchannels;
300         while(f) {
301                 if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
302                         fclose(f->fileptr);
303                         f->fileptr = NULL;
304                         if(rotate) {
305                                 strncpy(old, f->filename, sizeof(old));
306         
307                                 for(x=0;;x++) {
308                                         snprintf(new, sizeof(new), "%s.%d", f->filename, x);
309                                         myf = fopen((char *)new, "r");
310                                         if (myf) {
311                                                 fclose(myf);
312                                         } else {
313                                                 break;
314                                         }
315                                 }
316             
317                                 /* do it */
318                                 if (rename(old,new))
319                                         fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
320                         }
321                 }
322                 f = f->next;
323         }
324
325         ast_mutex_unlock(&loglock);
326
327         queue_log_init();
328
329         if (eventlog) {
330                 init_logger_chain();
331                 ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
332                 if (option_verbose)
333                         ast_verbose("Asterisk Event Logger restarted\n");
334                 return 0;
335         } else 
336                 ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
337         init_logger_chain();
338         pending_logger_reload = 0;
339         return -1;
340 }
341
342 static int handle_logger_reload(int fd, int argc, char *argv[])
343 {
344         if(reload_logger(0))
345         {
346                 ast_cli(fd, "Failed to reloadthe logger\n");
347                 return RESULT_FAILURE;
348         }
349         else
350                 return RESULT_SUCCESS;
351 }
352
353 static int handle_logger_rotate(int fd, int argc, char *argv[])
354 {
355         if(reload_logger(1))
356         {
357                 ast_cli(fd, "Failed to reloadthe logger\n");
358                 return RESULT_FAILURE;
359         }
360         else
361                 return RESULT_SUCCESS;
362 }
363
364 static struct verb {
365         void (*verboser)(const char *string, int opos, int replacelast, int complete);
366         struct verb *next;
367 } *verboser = NULL;
368
369
370 static char logger_reload_help[] =
371 "Usage: logger reload\n"
372 "       Reloads the logger subsystem state.  Use after restarting syslogd(8)\n";
373
374 static char logger_rotate_help[] =
375 "Usage: logger rotate\n"
376 "       Rotates and Reopens the log files.\n";
377
378 static struct ast_cli_entry reload_logger_cli = 
379         { { "logger", "reload", NULL }, 
380         handle_logger_reload, "Reopens the log files",
381         logger_reload_help };
382
383 static struct ast_cli_entry rotate_logger_cli = 
384         { { "logger", "rotate", NULL }, 
385         handle_logger_rotate, "Rotates and reopens the log files",
386         logger_rotate_help };
387
388 static int handle_SIGXFSZ(int sig) {
389         /* Indicate need to reload */
390         pending_logger_reload = 1;
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], "%s", 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                     fputs(buf, chan->fileptr);
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                     fputs(buf, stdout);
533                 }
534     }
535
536     ast_mutex_unlock(&loglock);
537     /* end critical section */
538         if (pending_logger_reload) {
539             reload_logger(1);
540             ast_log(LOG_EVENT,"Rotated Logs Per SIGXFSZ\n");
541             if (option_verbose)
542                     ast_verbose("Rotated Logs Per SIGXFSZ\n");
543         }
544 }
545
546 extern void ast_verbose(const char *fmt, ...)
547 {
548         static char stuff[4096];
549         static int pos = 0, opos;
550         static int replacelast = 0, complete;
551         struct msglist *m;
552         struct verb *v;
553         va_list ap;
554         va_start(ap, fmt);
555         ast_mutex_lock(&msglist_lock);
556         vsnprintf(stuff + pos, sizeof(stuff) - pos, fmt, ap);
557         opos = pos;
558         pos = strlen(stuff);
559         if (fmt[strlen(fmt)-1] == '\n') 
560                 complete = 1;
561         else
562                 complete=0;
563         if (complete) {
564                 if (msgcnt < MAX_MSG_QUEUE) {
565                         /* Allocate new structure */
566                         m = malloc(sizeof(struct msglist));
567                         msgcnt++;
568                 } else {
569                         /* Recycle the oldest entry */
570                         m = list;
571                         list = list->next;
572                         free(m->msg);
573                 }
574                 if (m) {
575                         m->msg = strdup(stuff);
576                         if (m->msg) {
577                                 if (last)
578                                         last->next = m;
579                                 else
580                                         list = m;
581                                 m->next = NULL;
582                                 last = m;
583                         } else {
584                                 msgcnt--;
585                                 ast_log(LOG_ERROR, "Out of memory\n");
586                                 free(m);
587                         }
588                 }
589         }
590         if (verboser) {
591                 v = verboser;
592                 while(v) {
593                         v->verboser(stuff, opos, replacelast, complete);
594                         v = v->next;
595                 }
596         } /* else
597                 fprintf(stdout, stuff + opos); */
598
599         ast_log(LOG_VERBOSE, stuff);
600
601         if (fmt[strlen(fmt)-1] != '\n') 
602                 replacelast = 1;
603         else 
604                 replacelast = pos = 0;
605         va_end(ap);
606
607         ast_mutex_unlock(&msglist_lock);
608 }
609
610 int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete))
611 {
612         struct msglist *m;
613         m = list;
614         ast_mutex_lock(&msglist_lock);
615         while(m) {
616                 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
617                 v(m->msg, 0, 0, 1);
618                 m = m->next;
619         }
620         ast_mutex_unlock(&msglist_lock);
621         return 0;
622 }
623
624 int ast_register_verbose(void (*v)(const char *string, int opos, int replacelast, int complete)) 
625 {
626         struct msglist *m;
627         struct verb *tmp;
628         /* XXX Should be more flexible here, taking > 1 verboser XXX */
629         if ((tmp = malloc(sizeof (struct verb)))) {
630                 tmp->verboser = v;
631                 ast_mutex_lock(&msglist_lock);
632                 tmp->next = verboser;
633                 verboser = tmp;
634                 m = list;
635                 while(m) {
636                         /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
637                         v(m->msg, 0, 0, 1);
638                         m = m->next;
639                 }
640                 ast_mutex_unlock(&msglist_lock);
641                 return 0;
642         }
643         return -1;
644 }
645
646 int ast_unregister_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
647 {
648         int res = -1;
649         struct verb *tmp, *tmpl=NULL;
650         ast_mutex_lock(&msglist_lock);
651         tmp = verboser;
652         while(tmp) {
653                 if (tmp->verboser == v) {
654                         if (tmpl)
655                                 tmpl->next = tmp->next;
656                         else
657                                 verboser = tmp->next;
658                         free(tmp);
659                         break;
660                 }
661                 tmpl = tmp;
662                 tmp = tmp->next;
663         }
664         if (tmp)
665                 res = 0;
666         ast_mutex_unlock(&msglist_lock);
667         return res;
668 }