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