d6fa6ab9710a9bae88b12de5e6917986931117f9
[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             if (&tm) {
424                     /* Log events into the event log file, with a different format */
425                     strftime(date, sizeof(date), "%b %e %T", &tm);
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             } else
434                     /** Cannot use ast_log() from locked section of ast_log()!
435                         ast_log(LOG_WARNING, "Unable to retrieve local time?\n"); **/
436                     fprintf(stderr, "ast_log: Unable to retrieve local time for %ld?\n", (long)t);
437             ast_mutex_unlock(&loglock);
438             return;
439     }
440
441     if (logchannels) {
442         chan = logchannels;
443         while(chan) {
444             if (chan->syslog && (chan->logmask & (1 << level))) {
445                 va_start(ap, fmt);
446                 ast_log_vsyslog(level, file, line, function, fmt, ap);
447                 va_end(ap);
448             } else if ((chan->logmask & (1 << level)) && (chan->console || chan->fileptr)) {
449                 char linestr[128];
450                 char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
451
452                 time(&t);
453                 localtime_r(&t, &tm);
454                 if(!&tm) {
455                     /** Cannot use ast_log() from locked section of ast_log()!
456                         ast_log(AST_LOG_WARNING, "Unable to retrieve local time?\n"); **/
457                     fprintf(stderr, "ast_log: Unable to retrieve local time for %ld?\n", (long)t);
458                     ast_mutex_unlock(&loglock);
459                     return;
460                 }
461                 strftime(date, sizeof(date), "%b %e %T", &tm);
462
463                 sprintf(linestr, "%d", line);
464                 snprintf(buf, sizeof(buf), "%s %s[%ld]: %s:%s %s: ",
465                          date,
466                          term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)),
467                          (long)pthread_self(),
468                          term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
469                          term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
470                          term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
471
472                 if (chan->console)
473                         ast_console_puts(buf);
474                 else
475                         fprintf(chan->fileptr, buf);
476                 va_start(ap, fmt);
477                 vsnprintf(buf, sizeof(buf), fmt, ap);
478                 va_end(ap);
479                 if (chan->console)
480                         ast_console_puts(buf);
481                 else
482                         fprintf(chan->fileptr, buf);
483
484             }
485             chan = chan->next;
486         }
487     }
488
489     ast_mutex_unlock(&loglock);
490     /* end critical section */
491 }
492
493 extern void ast_verbose(const char *fmt, ...)
494 {
495         static char stuff[4096];
496         static int pos = 0, opos;
497         static int replacelast = 0, complete;
498         struct msglist *m;
499         struct verb *v;
500         va_list ap;
501         va_start(ap, fmt);
502         ast_mutex_lock(&msglist_lock);
503         vsnprintf(stuff + pos, sizeof(stuff) - pos, fmt, ap);
504         opos = pos;
505         pos = strlen(stuff);
506         if (fmt[strlen(fmt)-1] == '\n') 
507                 complete = 1;
508         else
509                 complete=0;
510         if (complete) {
511                 if (msgcnt < MAX_MSG_QUEUE) {
512                         /* Allocate new structure */
513                         m = malloc(sizeof(struct msglist));
514                         msgcnt++;
515                 } else {
516                         /* Recycle the oldest entry */
517                         m = list;
518                         list = list->next;
519                         free(m->msg);
520                 }
521                 if (m) {
522                         m->msg = strdup(stuff);
523                         if (m->msg) {
524                                 if (last)
525                                         last->next = m;
526                                 else
527                                         list = m;
528                                 m->next = NULL;
529                                 last = m;
530                         } else {
531                                 msgcnt--;
532                                 ast_log(LOG_ERROR, "Out of memory\n");
533                                 free(m);
534                         }
535                 }
536         }
537         if (verboser) {
538                 v = verboser;
539                 while(v) {
540                         v->verboser(stuff, opos, replacelast, complete);
541                         v = v->next;
542                 }
543         } /* else
544                 fprintf(stdout, stuff + opos); */
545
546         if (fmt[strlen(fmt)-1] != '\n') 
547                 replacelast = 1;
548         else 
549                 replacelast = pos = 0;
550         va_end(ap);
551
552         va_start(ap, fmt);
553         if(stuff[strlen(stuff)-1] == '\n')
554             stuff[strlen(stuff)-1] = '\0';
555         ast_log_vsyslog(0, NULL, 0, NULL, fmt, ap);
556         va_end(ap);
557
558         ast_mutex_unlock(&msglist_lock);
559 }
560
561 int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete))
562 {
563         struct msglist *m;
564         m = list;
565         ast_mutex_lock(&msglist_lock);
566         while(m) {
567                 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
568                 v(m->msg, 0, 0, 1);
569                 m = m->next;
570         }
571         ast_mutex_unlock(&msglist_lock);
572         return 0;
573 }
574
575 int ast_register_verbose(void (*v)(const char *string, int opos, int replacelast, int complete)) 
576 {
577         struct msglist *m;
578         struct verb *tmp;
579         /* XXX Should be more flexible here, taking > 1 verboser XXX */
580         if ((tmp = malloc(sizeof (struct verb)))) {
581                 tmp->verboser = v;
582                 ast_mutex_lock(&msglist_lock);
583                 tmp->next = verboser;
584                 verboser = tmp;
585                 m = list;
586                 while(m) {
587                         /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
588                         v(m->msg, 0, 0, 1);
589                         m = m->next;
590                 }
591                 ast_mutex_unlock(&msglist_lock);
592                 return 0;
593         }
594         return -1;
595 }
596
597 int ast_unregister_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
598 {
599         int res = -1;
600         struct verb *tmp, *tmpl=NULL;
601         ast_mutex_lock(&msglist_lock);
602         tmp = verboser;
603         while(tmp) {
604                 if (tmp->verboser == v) {
605                         if (tmpl)
606                                 tmpl->next = tmp->next;
607                         else
608                                 verboser = tmp->next;
609                         free(tmp);
610                         break;
611                 }
612                 tmpl = tmp;
613                 tmp = tmp->next;
614         }
615         if (tmp)
616                 res = 0;
617         ast_mutex_unlock(&msglist_lock);
618         return res;
619 }