make verbose messages not go into syslog, for now. Bug #808
[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(struct logchannel));
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     time(&t);
421     localtime_r(&t, &tm);
422     strftime(date, sizeof(date), "%b %e %T", &tm);
423
424
425     if (level == __LOG_EVENT) {
426             va_start(ap, fmt);
427
428             fprintf(eventlog, "%s asterisk[%d]: ", date, getpid());
429             vfprintf(eventlog, fmt, ap);
430             fflush(eventlog);
431
432             va_end(ap);
433             ast_mutex_unlock(&loglock);
434             return;
435     }
436
437     if (logchannels) {
438         chan = logchannels;
439         while(chan) {
440             if (chan->syslog && (chan->logmask & (1 << level))) {
441                 va_start(ap, fmt);
442                 ast_log_vsyslog(level, file, line, function, fmt, ap);
443                 va_end(ap);
444             } else if ((chan->logmask & (1 << level)) && (chan->console)) {
445                 char linestr[128];
446                 char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
447
448                 sprintf(linestr, "%d", line);
449                 snprintf(buf, sizeof(buf), "%s %s[%ld]: %s:%s %s: ",
450                          date,
451                          term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)),
452                          (long)pthread_self(),
453                          term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
454                          term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
455                          term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
456
457                 ast_console_puts(buf);
458                 va_start(ap, fmt);
459                 vsnprintf(buf, sizeof(buf), fmt, ap);
460                 va_end(ap);
461                 ast_console_puts(buf);
462             } else if ((chan->logmask & (1 << level)) && (chan->fileptr)) {
463                     snprintf(buf, sizeof(buf), "%s %s[%ld]: ", date,
464                              levels[level], (long)pthread_self());
465                     fprintf(chan->fileptr, buf);
466                     va_start(ap, fmt);
467                     vsnprintf(buf, sizeof(buf), fmt, ap);
468                     va_end(ap);
469                     fprintf(chan->fileptr, buf);
470             }
471             chan = chan->next;
472         }
473     } else {
474             /* 
475              * we don't have the logger chain configured yet,
476              * so just log to stdout 
477              */
478             va_start(ap, fmt);
479             vsnprintf(buf, sizeof(buf), fmt, ap);
480             va_end(ap);
481             fprintf(stdout, buf);
482     }
483
484     ast_mutex_unlock(&loglock);
485     /* end critical section */
486 }
487
488 extern void ast_verbose(const char *fmt, ...)
489 {
490         static char stuff[4096];
491         static int pos = 0, opos;
492         static int replacelast = 0, complete;
493         struct msglist *m;
494         struct verb *v;
495         va_list ap;
496         va_start(ap, fmt);
497         ast_mutex_lock(&msglist_lock);
498         vsnprintf(stuff + pos, sizeof(stuff) - pos, fmt, ap);
499         opos = pos;
500         pos = strlen(stuff);
501         if (fmt[strlen(fmt)-1] == '\n') 
502                 complete = 1;
503         else
504                 complete=0;
505         if (complete) {
506                 if (msgcnt < MAX_MSG_QUEUE) {
507                         /* Allocate new structure */
508                         m = malloc(sizeof(struct msglist));
509                         msgcnt++;
510                 } else {
511                         /* Recycle the oldest entry */
512                         m = list;
513                         list = list->next;
514                         free(m->msg);
515                 }
516                 if (m) {
517                         m->msg = strdup(stuff);
518                         if (m->msg) {
519                                 if (last)
520                                         last->next = m;
521                                 else
522                                         list = m;
523                                 m->next = NULL;
524                                 last = m;
525                         } else {
526                                 msgcnt--;
527                                 ast_log(LOG_ERROR, "Out of memory\n");
528                                 free(m);
529                         }
530                 }
531         }
532         if (verboser) {
533                 v = verboser;
534                 while(v) {
535                         v->verboser(stuff, opos, replacelast, complete);
536                         v = v->next;
537                 }
538         } /* else
539                 fprintf(stdout, stuff + opos); */
540
541         if (fmt[strlen(fmt)-1] != '\n') 
542                 replacelast = 1;
543         else 
544                 replacelast = pos = 0;
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 }