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