7a51c37847167c08a2a549089f872bba2c125d62
[asterisk/asterisk.git] / logger.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Asterisk Logger
22  * 
23  * Logging routines
24  *
25  * \author Mark Spencer <markster@digium.com>
26  */
27
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <time.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <errno.h>
36 #include <sys/stat.h>
37 #ifdef STACK_BACKTRACES
38 #include <execinfo.h>
39 #define MAX_BACKTRACE_FRAMES 20
40 #endif
41
42 #define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values,
43                         from <syslog.h> which is included by logger.h */
44 #include <syslog.h>
45
46 #include "asterisk.h"
47
48 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
49
50 static int syslog_level_map[] = {
51         LOG_DEBUG,
52         LOG_INFO,    /* arbitrary equivalent of LOG_EVENT */
53         LOG_NOTICE,
54         LOG_WARNING,
55         LOG_ERR,
56         LOG_DEBUG,
57         LOG_DEBUG
58 };
59
60 #define SYSLOG_NLEVELS 6
61
62 #include "asterisk/logger.h"
63 #include "asterisk/lock.h"
64 #include "asterisk/options.h"
65 #include "asterisk/channel.h"
66 #include "asterisk/config.h"
67 #include "asterisk/term.h"
68 #include "asterisk/cli.h"
69 #include "asterisk/utils.h"
70 #include "asterisk/manager.h"
71
72 #define MAX_MSG_QUEUE 200
73
74 #if defined(__linux__) && !defined(__NR_gettid)
75 #include <asm/unistd.h>
76 #endif
77
78 #if defined(__linux__) && defined(__NR_gettid)
79 #define GETTID() syscall(__NR_gettid)
80 #else
81 #define GETTID() getpid()
82 #endif
83
84
85 static char dateformat[256] = "%b %e %T";               /* Original Asterisk Format */
86
87 AST_MUTEX_DEFINE_STATIC(msglist_lock);
88 AST_MUTEX_DEFINE_STATIC(loglock);
89 static int filesize_reload_needed = 0;
90 static int global_logmask = -1;
91
92 static struct {
93         unsigned int queue_log:1;
94         unsigned int event_log:1;
95 } logfiles = { 1, 1 };
96
97 static struct msglist {
98         char *msg;
99         struct msglist *next;
100 } *list = NULL, *last = NULL;
101
102 static char hostname[MAXHOSTNAMELEN];
103
104 enum logtypes {
105         LOGTYPE_SYSLOG,
106         LOGTYPE_FILE,
107         LOGTYPE_CONSOLE,
108 };
109
110 struct logchannel {
111         int logmask;                    /* What to log to this channel */
112         int disabled;                   /* If this channel is disabled or not */
113         int facility;                   /* syslog facility */
114         enum logtypes type;             /* Type of log channel */
115         FILE *fileptr;                  /* logfile logging file pointer */
116         char filename[256];             /* Filename */
117         struct logchannel *next;        /* Next channel in chain */
118 };
119
120 static struct logchannel *logchannels = NULL;
121
122 static int msgcnt = 0;
123
124 static FILE *eventlog = NULL;
125
126 static char *levels[] = {
127         "DEBUG",
128         "EVENT",
129         "NOTICE",
130         "WARNING",
131         "ERROR",
132         "VERBOSE",
133         "DTMF"
134 };
135
136 static int colors[] = {
137         COLOR_BRGREEN,
138         COLOR_BRBLUE,
139         COLOR_YELLOW,
140         COLOR_BRRED,
141         COLOR_RED,
142         COLOR_GREEN,
143         COLOR_BRGREEN
144 };
145
146 static int make_components(char *s, int lineno)
147 {
148         char *w;
149         int res = 0;
150         char *stringp=NULL;
151         stringp=s;
152         w = strsep(&stringp, ",");
153         while(w) {
154                 while(*w && (*w < 33))
155                         w++;
156                 if (!strcasecmp(w, "error")) 
157                         res |= (1 << __LOG_ERROR);
158                 else if (!strcasecmp(w, "warning"))
159                         res |= (1 << __LOG_WARNING);
160                 else if (!strcasecmp(w, "notice"))
161                         res |= (1 << __LOG_NOTICE);
162                 else if (!strcasecmp(w, "event"))
163                         res |= (1 << __LOG_EVENT);
164                 else if (!strcasecmp(w, "debug"))
165                         res |= (1 << __LOG_DEBUG);
166                 else if (!strcasecmp(w, "verbose"))
167                         res |= (1 << __LOG_VERBOSE);
168                 else if (!strcasecmp(w, "dtmf"))
169                         res |= (1 << __LOG_DTMF);
170                 else {
171                         fprintf(stderr, "Logfile Warning: Unknown keyword '%s' at line %d of logger.conf\n", w, lineno);
172                 }
173                 w = strsep(&stringp, ",");
174         }
175         return res;
176 }
177
178 static struct logchannel *make_logchannel(char *channel, char *components, int lineno)
179 {
180         struct logchannel *chan;
181         char *facility;
182 #ifndef SOLARIS
183         CODE *cptr;
184 #endif
185
186         if (ast_strlen_zero(channel))
187                 return NULL;
188         chan = malloc(sizeof(struct logchannel));
189
190         if (!chan)      /* Can't allocate memory */
191                 return NULL;
192
193         memset(chan, 0, sizeof(struct logchannel));
194         if (!strcasecmp(channel, "console")) {
195                 chan->type = LOGTYPE_CONSOLE;
196         } else if (!strncasecmp(channel, "syslog", 6)) {
197                 /*
198                 * syntax is:
199                 *  syslog.facility => level,level,level
200                 */
201                 facility = strchr(channel, '.');
202                 if(!facility++ || !facility) {
203                         facility = "local0";
204                 }
205
206 #ifndef SOLARIS
207                 /*
208                 * Walk through the list of facilitynames (defined in sys/syslog.h)
209                 * to see if we can find the one we have been given
210                 */
211                 chan->facility = -1;
212                 cptr = facilitynames;
213                 while (cptr->c_name) {
214                         if (!strcasecmp(facility, cptr->c_name)) {
215                                 chan->facility = cptr->c_val;
216                                 break;
217                         }
218                         cptr++;
219                 }
220 #else
221                 chan->facility = -1;
222                 if (!strcasecmp(facility, "kern")) 
223                         chan->facility = LOG_KERN;
224                 else if (!strcasecmp(facility, "USER")) 
225                         chan->facility = LOG_USER;
226                 else if (!strcasecmp(facility, "MAIL")) 
227                         chan->facility = LOG_MAIL;
228                 else if (!strcasecmp(facility, "DAEMON")) 
229                         chan->facility = LOG_DAEMON;
230                 else if (!strcasecmp(facility, "AUTH")) 
231                         chan->facility = LOG_AUTH;
232                 else if (!strcasecmp(facility, "SYSLOG")) 
233                         chan->facility = LOG_SYSLOG;
234                 else if (!strcasecmp(facility, "LPR")) 
235                         chan->facility = LOG_LPR;
236                 else if (!strcasecmp(facility, "NEWS")) 
237                         chan->facility = LOG_NEWS;
238                 else if (!strcasecmp(facility, "UUCP")) 
239                         chan->facility = LOG_UUCP;
240                 else if (!strcasecmp(facility, "CRON")) 
241                         chan->facility = LOG_CRON;
242                 else if (!strcasecmp(facility, "LOCAL0")) 
243                         chan->facility = LOG_LOCAL0;
244                 else if (!strcasecmp(facility, "LOCAL1")) 
245                         chan->facility = LOG_LOCAL1;
246                 else if (!strcasecmp(facility, "LOCAL2")) 
247                         chan->facility = LOG_LOCAL2;
248                 else if (!strcasecmp(facility, "LOCAL3")) 
249                         chan->facility = LOG_LOCAL3;
250                 else if (!strcasecmp(facility, "LOCAL4")) 
251                         chan->facility = LOG_LOCAL4;
252                 else if (!strcasecmp(facility, "LOCAL5")) 
253                         chan->facility = LOG_LOCAL5;
254                 else if (!strcasecmp(facility, "LOCAL6")) 
255                         chan->facility = LOG_LOCAL6;
256                 else if (!strcasecmp(facility, "LOCAL7")) 
257                         chan->facility = LOG_LOCAL7;
258 #endif /* Solaris */
259
260                 if (0 > chan->facility) {
261                         fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
262                         free(chan);
263                         return NULL;
264                 }
265
266                 chan->type = LOGTYPE_SYSLOG;
267                 snprintf(chan->filename, sizeof(chan->filename), "%s", channel);
268                 openlog("asterisk", LOG_PID, chan->facility);
269         } else {
270                 if (channel[0] == '/') {
271                         if(!ast_strlen_zero(hostname)) { 
272                                 snprintf(chan->filename, sizeof(chan->filename) - 1,"%s.%s", channel, hostname);
273                         } else {
274                                 ast_copy_string(chan->filename, channel, sizeof(chan->filename));
275                         }
276                 }                 
277                 
278                 if(!ast_strlen_zero(hostname)) {
279                         snprintf(chan->filename, sizeof(chan->filename), "%s/%s.%s",(char *)ast_config_AST_LOG_DIR, channel, hostname);
280                 } else {
281                         snprintf(chan->filename, sizeof(chan->filename), "%s/%s", (char *)ast_config_AST_LOG_DIR, channel);
282                 }
283                 chan->fileptr = fopen(chan->filename, "a");
284                 if (!chan->fileptr) {
285                         /* Can't log here, since we're called with a lock */
286                         fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", chan->filename, strerror(errno));
287                 } 
288                 chan->type = LOGTYPE_FILE;
289         }
290         chan->logmask = make_components(components, lineno);
291         return chan;
292 }
293
294 static void init_logger_chain(void)
295 {
296         struct logchannel *chan, *cur;
297         struct ast_config *cfg;
298         struct ast_variable *var;
299         char *s;
300
301         /* delete our list of log channels */
302         ast_mutex_lock(&loglock);
303         chan = logchannels;
304         while (chan) {
305                 cur = chan->next;
306                 free(chan);
307                 chan = cur;
308         }
309         logchannels = NULL;
310         ast_mutex_unlock(&loglock);
311         
312         global_logmask = 0;
313         /* close syslog */
314         closelog();
315         
316         cfg = ast_config_load("logger.conf");
317         
318         /* If no config file, we're fine, set default options. */
319         if (!cfg) {
320                 fprintf(stderr, "Unable to open logger.conf: %s\n", strerror(errno));
321                 chan = malloc(sizeof(struct logchannel));
322                 memset(chan, 0, sizeof(struct logchannel));
323                 chan->type = LOGTYPE_CONSOLE;
324                 chan->logmask = 28; /*warning,notice,error */
325                 chan->next = logchannels;
326                 logchannels = chan;
327                 global_logmask |= chan->logmask;
328                 return;
329         }
330         
331         ast_mutex_lock(&loglock);
332         if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
333                 if(ast_true(s)) {
334                         if(gethostname(hostname, sizeof(hostname)-1)) {
335                                 ast_copy_string(hostname, "unknown", sizeof(hostname));
336                                 ast_log(LOG_WARNING, "What box has no hostname???\n");
337                         }
338                 } else
339                         hostname[0] = '\0';
340         } else
341                 hostname[0] = '\0';
342         if ((s = ast_variable_retrieve(cfg, "general", "dateformat"))) {
343                 ast_copy_string(dateformat, s, sizeof(dateformat));
344         } else
345                 ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
346         if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) {
347                 logfiles.queue_log = ast_true(s);
348         }
349         if ((s = ast_variable_retrieve(cfg, "general", "event_log"))) {
350                 logfiles.event_log = ast_true(s);
351         }
352
353         var = ast_variable_browse(cfg, "logfiles");
354         while(var) {
355                 chan = make_logchannel(var->name, var->value, var->lineno);
356                 if (chan) {
357                         chan->next = logchannels;
358                         logchannels = chan;
359                         global_logmask |= chan->logmask;
360                 }
361                 var = var->next;
362         }
363
364         ast_config_destroy(cfg);
365         ast_mutex_unlock(&loglock);
366 }
367
368 static FILE *qlog = NULL;
369 AST_MUTEX_DEFINE_STATIC(qloglock);
370
371 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
372 {
373         va_list ap;
374         ast_mutex_lock(&qloglock);
375         if (qlog) {
376                 va_start(ap, fmt);
377                 fprintf(qlog, "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
378                 vfprintf(qlog, fmt, ap);
379                 fprintf(qlog, "\n");
380                 va_end(ap);
381                 fflush(qlog);
382         }
383         ast_mutex_unlock(&qloglock);
384 }
385
386 static void queue_log_init(void)
387 {
388         char filename[256];
389         int reloaded = 0;
390
391         ast_mutex_lock(&qloglock);
392         if (qlog) {
393                 reloaded = 1;
394                 fclose(qlog);
395                 qlog = NULL;
396         }
397         snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_LOG_DIR, "queue_log");
398         if (logfiles.queue_log) {
399                 qlog = fopen(filename, "a");
400         }
401         ast_mutex_unlock(&qloglock);
402         if (reloaded) 
403                 ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
404         else
405                 ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
406 }
407
408 int reload_logger(int rotate)
409 {
410         char old[AST_CONFIG_MAX_PATH] = "";
411         char new[AST_CONFIG_MAX_PATH];
412         struct logchannel *f;
413         FILE *myf;
414         int x;
415
416         ast_mutex_lock(&loglock);
417         if (eventlog) 
418                 fclose(eventlog);
419         else 
420                 rotate = 0;
421         eventlog = NULL;
422
423         mkdir((char *)ast_config_AST_LOG_DIR, 0755);
424         snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
425
426         if (logfiles.event_log) {
427                 if (rotate) {
428                         for (x=0;;x++) {
429                                 snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, EVENTLOG,x);
430                                 myf = fopen((char *)new, "r");
431                                 if (myf)        /* File exists */
432                                         fclose(myf);
433                                 else
434                                         break;
435                         }
436         
437                         /* do it */
438                         if (rename(old,new))
439                                 fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
440                 }
441
442                 eventlog = fopen(old, "a");
443         }
444
445         f = logchannels;
446         while(f) {
447                 if (f->disabled) {
448                         f->disabled = 0;        /* Re-enable logging at reload */
449                         manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
450                 }
451                 if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
452                         fclose(f->fileptr);     /* Close file */
453                         f->fileptr = NULL;
454                         if(rotate) {
455                                 ast_copy_string(old, f->filename, sizeof(old));
456         
457                                 for(x=0;;x++) {
458                                         snprintf(new, sizeof(new), "%s.%d", f->filename, x);
459                                         myf = fopen((char *)new, "r");
460                                         if (myf) {
461                                                 fclose(myf);
462                                         } else {
463                                                 break;
464                                         }
465                                 }
466             
467                                 /* do it */
468                                 if (rename(old,new))
469                                         fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
470                         }
471                 }
472                 f = f->next;
473         }
474
475         ast_mutex_unlock(&loglock);
476
477         filesize_reload_needed = 0;
478         
479         init_logger_chain();
480         queue_log_init();
481
482         if (logfiles.event_log) {
483                 if (eventlog) {
484                         ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
485                         if (option_verbose)
486                                 ast_verbose("Asterisk Event Logger restarted\n");
487                         return 0;
488                 } else 
489                         ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
490         } else 
491                 return 0;
492         return -1;
493 }
494
495 static int handle_logger_reload(int fd, int argc, char *argv[])
496 {
497         if(reload_logger(0)) {
498                 ast_cli(fd, "Failed to reload the logger\n");
499                 return RESULT_FAILURE;
500         } else
501                 return RESULT_SUCCESS;
502 }
503
504 static int handle_logger_rotate(int fd, int argc, char *argv[])
505 {
506         if(reload_logger(1)) {
507                 ast_cli(fd, "Failed to reload the logger and rotate log files\n");
508                 return RESULT_FAILURE;
509         } else
510                 return RESULT_SUCCESS;
511 }
512
513 /*! \brief CLI command to show logging system configuration */
514 static int handle_logger_show_channels(int fd, int argc, char *argv[])
515 {
516 #define FORMATL "%-35.35s %-8.8s %-9.9s "
517         struct logchannel *chan;
518
519         ast_mutex_lock(&loglock);
520
521         chan = logchannels;
522         ast_cli(fd,FORMATL, "Channel", "Type", "Status");
523         ast_cli(fd, "Configuration\n");
524         ast_cli(fd,FORMATL, "-------", "----", "------");
525         ast_cli(fd, "-------------\n");
526         while (chan) {
527                 ast_cli(fd, FORMATL, chan->filename, chan->type==LOGTYPE_CONSOLE ? "Console" : (chan->type==LOGTYPE_SYSLOG ? "Syslog" : "File"),
528                         chan->disabled ? "Disabled" : "Enabled");
529                 ast_cli(fd, " - ");
530                 if (chan->logmask & (1 << __LOG_DEBUG)) 
531                         ast_cli(fd, "Debug ");
532                 if (chan->logmask & (1 << __LOG_DTMF)) 
533                         ast_cli(fd, "DTMF ");
534                 if (chan->logmask & (1 << __LOG_VERBOSE)) 
535                         ast_cli(fd, "Verbose ");
536                 if (chan->logmask & (1 << __LOG_WARNING)) 
537                         ast_cli(fd, "Warning ");
538                 if (chan->logmask & (1 << __LOG_NOTICE)) 
539                         ast_cli(fd, "Notice ");
540                 if (chan->logmask & (1 << __LOG_ERROR)) 
541                         ast_cli(fd, "Error ");
542                 if (chan->logmask & (1 << __LOG_EVENT)) 
543                         ast_cli(fd, "Event ");
544                 ast_cli(fd, "\n");
545                 chan = chan->next;
546         }
547         ast_cli(fd, "\n");
548
549         ast_mutex_unlock(&loglock);
550                 
551         return RESULT_SUCCESS;
552 }
553
554 static struct verb {
555         void (*verboser)(const char *string, int opos, int replacelast, int complete);
556         struct verb *next;
557 } *verboser = NULL;
558
559
560 static char logger_reload_help[] =
561 "Usage: logger reload\n"
562 "       Reloads the logger subsystem state.  Use after restarting syslogd(8) if you are using syslog logging.\n";
563
564 static char logger_rotate_help[] =
565 "Usage: logger rotate\n"
566 "       Rotates and Reopens the log files.\n";
567
568 static char logger_show_channels_help[] =
569 "Usage: logger show channels\n"
570 "       Show configured logger channels.\n";
571
572 static struct ast_cli_entry logger_show_channels_cli = 
573         { { "logger", "show", "channels", NULL }, 
574         handle_logger_show_channels, "List configured log channels",
575         logger_show_channels_help };
576
577 static struct ast_cli_entry reload_logger_cli = 
578         { { "logger", "reload", NULL }, 
579         handle_logger_reload, "Reopens the log files",
580         logger_reload_help };
581
582 static struct ast_cli_entry rotate_logger_cli = 
583         { { "logger", "rotate", NULL }, 
584         handle_logger_rotate, "Rotates and reopens the log files",
585         logger_rotate_help };
586
587 static int handle_SIGXFSZ(int sig) 
588 {
589         /* Indicate need to reload */
590         filesize_reload_needed = 1;
591         return 0;
592 }
593
594 int init_logger(void)
595 {
596         char tmp[256];
597
598         /* auto rotate if sig SIGXFSZ comes a-knockin */
599         (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ);
600
601         /* register the relaod logger cli command */
602         ast_cli_register(&reload_logger_cli);
603         ast_cli_register(&rotate_logger_cli);
604         ast_cli_register(&logger_show_channels_cli);
605
606         /* create log channels */
607         init_logger_chain();
608
609         /* initialize queue logger */
610         queue_log_init();
611
612         /* create the eventlog */
613         if (logfiles.event_log) {
614                 mkdir((char *)ast_config_AST_LOG_DIR, 0755);
615                 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
616                 eventlog = fopen((char *)tmp, "a");
617                 if (eventlog) {
618                         ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
619                         if (option_verbose)
620                                 ast_verbose("Asterisk Event Logger Started %s\n",(char *)tmp);
621                         return 0;
622                 } else 
623                         ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
624         } else
625                 return 0;
626
627         return -1;
628 }
629
630 void close_logger(void)
631 {
632         struct msglist *m, *tmp;
633
634         ast_mutex_lock(&msglist_lock);
635         m = list;
636         while(m) {
637                 if (m->msg) {
638                         free(m->msg);
639                 }
640                 tmp = m->next;
641                 free(m);
642                 m = tmp;
643         }
644         list = last = NULL;
645         msgcnt = 0;
646         ast_mutex_unlock(&msglist_lock);
647         return;
648 }
649
650 static void strip_coloring(char *str)
651 {
652         char *src, *dest, *end;
653         
654         if (!str)
655                 return;
656
657         /* find the first potential escape sequence in the string */
658
659         src = strchr(str, '\033');
660         if (!src)
661                 return;
662
663         dest = src;
664         while (*src) {
665                 /* at the top of this loop, *src will always be an ESC character */
666                 if ((src[1] == '[') && ((end = strchr(src + 2, 'm'))))
667                         src = end + 1;
668                 else
669                         *dest++ = *src++;
670
671                 /* copy characters, checking for ESC as we go */
672                 while (*src && (*src != '\033'))
673                         *dest++ = *src++;
674         }
675
676         *dest = '\0';
677 }
678
679 static void ast_log_vsyslog(int level, const char *file, int line, const char *function, const char *fmt, va_list args) 
680 {
681         char buf[BUFSIZ];
682         char *s;
683
684         if (level >= SYSLOG_NLEVELS) {
685                 /* we are locked here, so cannot ast_log() */
686                 fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level);
687                 return;
688         }
689         if (level == __LOG_VERBOSE) {
690                 snprintf(buf, sizeof(buf), "VERBOSE[%ld]: ", (long)GETTID());
691                 level = __LOG_DEBUG;
692         } else if (level == __LOG_DTMF) {
693                 snprintf(buf, sizeof(buf), "DTMF[%ld]: ", (long)GETTID());
694                 level = __LOG_DEBUG;
695         } else {
696                 snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: ",
697                          levels[level], (long)GETTID(), file, line, function);
698         }
699         s = buf + strlen(buf);
700         vsnprintf(s, sizeof(buf) - strlen(buf), fmt, args);
701         strip_coloring(s);
702         syslog(syslog_level_map[level], "%s", buf);
703 }
704
705 /*
706  * send log messages to syslog and/or the console
707  */
708 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
709 {
710         struct logchannel *chan;
711         char buf[BUFSIZ];
712         time_t t;
713         struct tm tm;
714         char date[256];
715
716         va_list ap;
717         
718         /* don't display LOG_DEBUG messages unless option_verbose _or_ option_debug
719            are non-zero; LOG_DEBUG messages can still be displayed if option_debug
720            is zero, if option_verbose is non-zero (this allows for 'level zero'
721            LOG_DEBUG messages to be displayed, if the logmask on any channel
722            allows it)
723         */
724         if (!option_verbose && !option_debug && (level == __LOG_DEBUG)) {
725                 return;
726         }
727
728         /* Ignore anything that never gets logged anywhere */
729         if (!(global_logmask & (1 << level)))
730                 return;
731         
732         /* Ignore anything other than the currently debugged file if there is one */
733         if ((level == __LOG_DEBUG) && !ast_strlen_zero(debug_filename) && strcasecmp(debug_filename, file))
734                 return;
735
736         /* begin critical section */
737         ast_mutex_lock(&loglock);
738
739         time(&t);
740         localtime_r(&t, &tm);
741         strftime(date, sizeof(date), dateformat, &tm);
742
743         if (logfiles.event_log && level == __LOG_EVENT) {
744                 va_start(ap, fmt);
745
746                 fprintf(eventlog, "%s asterisk[%d]: ", date, getpid());
747                 vfprintf(eventlog, fmt, ap);
748                 fflush(eventlog);
749
750                 va_end(ap);
751                 ast_mutex_unlock(&loglock);
752                 return;
753         }
754
755         if (logchannels) {
756                 chan = logchannels;
757                 while(chan && !chan->disabled) {
758                         /* Check syslog channels */
759                         if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << level))) {
760                                 va_start(ap, fmt);
761                                 ast_log_vsyslog(level, file, line, function, fmt, ap);
762                                 va_end(ap);
763                         /* Console channels */
764                         } else if ((chan->logmask & (1 << level)) && (chan->type == LOGTYPE_CONSOLE)) {
765                                 char linestr[128];
766                                 char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
767
768                                 if (level != __LOG_VERBOSE) {
769                                         sprintf(linestr, "%d", line);
770                                         snprintf(buf, sizeof(buf), ast_opt_timestamp ? "[%s] %s[%ld]: %s:%s %s: " : "%s %s[%ld]: %s:%s %s: ",
771                                                 date,
772                                                 term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)),
773                                                 (long)GETTID(),
774                                                 term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
775                                                 term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
776                                                 term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
777                                         
778                                         ast_console_puts(buf);
779                                         va_start(ap, fmt);
780                                         vsnprintf(buf, sizeof(buf), fmt, ap);
781                                         va_end(ap);
782                                         ast_console_puts(buf);
783                                 }
784                         /* File channels */
785                         } else if ((chan->logmask & (1 << level)) && (chan->fileptr)) {
786                                 int res;
787                                 snprintf(buf, sizeof(buf), ast_opt_timestamp ? "[%s] %s[%ld]: " : "%s %s[%ld] %s: ", date,
788                                         levels[level], (long)GETTID(), file);
789                                 res = fprintf(chan->fileptr, buf);
790                                 if (res <= 0 && buf[0] != '\0') {       /* Error, no characters printed */
791                                         fprintf(stderr,"**** Asterisk Logging Error: ***********\n");
792                                         if (errno == ENOMEM || errno == ENOSPC) {
793                                                 fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename);
794                                         } else
795                                                 fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno));
796                                         manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno));
797                                         chan->disabled = 1;     
798                                 } else {
799                                         /* No error message, continue printing */
800                                         va_start(ap, fmt);
801                                         vsnprintf(buf, sizeof(buf), fmt, ap);
802                                         va_end(ap);
803                                         strip_coloring(buf);
804                                         fputs(buf, chan->fileptr);
805                                         fflush(chan->fileptr);
806                                 }
807                         }
808                         chan = chan->next;
809                 }
810         } else {
811                 /* 
812                  * we don't have the logger chain configured yet,
813                  * so just log to stdout 
814                 */
815                 if (level != __LOG_VERBOSE) {
816                         va_start(ap, fmt);
817                         vsnprintf(buf, sizeof(buf), fmt, ap);
818                         va_end(ap);
819                         fputs(buf, stdout);
820                 }
821         }
822
823         ast_mutex_unlock(&loglock);
824         /* end critical section */
825         if (filesize_reload_needed) {
826                 reload_logger(1);
827                 ast_log(LOG_EVENT,"Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
828                 if (option_verbose)
829                         ast_verbose("Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
830         }
831 }
832
833 void ast_backtrace(void)
834 {
835 #ifdef STACK_BACKTRACES
836         int count=0, i=0;
837         void **addresses;
838         char **strings;
839
840         addresses = calloc(MAX_BACKTRACE_FRAMES, sizeof(void *));
841         if (addresses) {
842                 count = backtrace(addresses, MAX_BACKTRACE_FRAMES);
843                 strings = backtrace_symbols(addresses, count);
844                 if (strings) {
845                         ast_log(LOG_DEBUG, "Got %d backtrace record%c\n", count, count != 1 ? 's' : ' ');
846                         for (i=0; i < count ; i++) {
847                                 ast_log(LOG_DEBUG, "#%d: [%08X] %s\n", i, (unsigned int)addresses[i], strings[i]);
848                         }
849                         free(strings);
850                 } else {
851                         ast_log(LOG_DEBUG, "Could not allocate memory for backtrace\n");
852                 }
853                 free(addresses);
854         } else {
855                 ast_log(LOG_DEBUG, "Could not allocate memory for backtrace\n");
856         }
857 #else
858 #ifdef Linux
859         ast_log(LOG_WARNING, "Must compile with 'make dont-optimize' for stack backtraces\n");
860 #else
861         ast_log(LOG_WARNING, "Inline stack backtraces are only available on the Linux platform.\n");
862 #endif
863 #endif
864 }
865
866 void ast_verbose(const char *fmt, ...)
867 {
868         static char stuff[4096];
869         static int len = 0;
870         static int replacelast = 0;
871
872         int complete;
873         int olen;
874         struct msglist *m;
875         struct verb *v;
876         
877         va_list ap;
878         va_start(ap, fmt);
879
880         if (ast_opt_timestamp) {
881                 time_t t;
882                 struct tm tm;
883                 char date[40];
884                 char *datefmt;
885
886                 time(&t);
887                 localtime_r(&t, &tm);
888                 strftime(date, sizeof(date), dateformat, &tm);
889                 datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1);
890                 if (datefmt) {
891                         sprintf(datefmt, "[%s] %s", date, fmt);
892                         fmt = datefmt;
893                 }
894         }
895
896         /* this lock is also protecting against multiple threads
897            being in this function at the same time, so it must be
898            held before any of the static variables are accessed
899         */
900         ast_mutex_lock(&msglist_lock);
901
902         /* there is a potential security problem here: if formatting
903            the current date using 'dateformat' results in a string
904            containing '%', then the vsnprintf() call below will
905            probably try to access random memory
906         */
907         vsnprintf(stuff + len, sizeof(stuff) - len, fmt, ap);
908         va_end(ap);
909
910         olen = len;
911         len = strlen(stuff);
912
913         complete = (stuff[len - 1] == '\n') ? 1 : 0;
914
915         /* If we filled up the stuff completely, then log it even without the '\n' */
916         if (len >= sizeof(stuff) - 1) {
917                 complete = 1;
918                 len = 0;
919         }
920
921         if (complete) {
922                 if (msgcnt < MAX_MSG_QUEUE) {
923                         /* Allocate new structure */
924                         if ((m = malloc(sizeof(*m))))
925                                 msgcnt++;
926                 } else {
927                         /* Recycle the oldest entry */
928                         m = list;
929                         list = list->next;
930                         free(m->msg);
931                 }
932                 if (m) {
933                         m->msg = strdup(stuff);
934                         if (m->msg) {
935                                 if (last)
936                                         last->next = m;
937                                 else
938                                         list = m;
939                                 m->next = NULL;
940                                 last = m;
941                         } else {
942                                 msgcnt--;
943                                 ast_log(LOG_ERROR, "Out of memory\n");
944                                 free(m);
945                         }
946                 }
947         }
948
949         for (v = verboser; v; v = v->next)
950                 v->verboser(stuff, olen, replacelast, complete);
951
952         ast_log(LOG_VERBOSE, "%s", stuff);
953
954         if (len) {
955                 if (!complete)
956                         replacelast = 1;
957                 else 
958                         replacelast = len = 0;
959         }
960
961         ast_mutex_unlock(&msglist_lock);
962 }
963
964 int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete))
965 {
966         struct msglist *m;
967         ast_mutex_lock(&msglist_lock);
968         m = list;
969         while(m) {
970                 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
971                 v(m->msg, 0, 0, 1);
972                 m = m->next;
973         }
974         ast_mutex_unlock(&msglist_lock);
975         return 0;
976 }
977
978 int ast_register_verbose(void (*v)(const char *string, int opos, int replacelast, int complete)) 
979 {
980         struct msglist *m;
981         struct verb *tmp;
982         /* XXX Should be more flexible here, taking > 1 verboser XXX */
983         if ((tmp = malloc(sizeof (struct verb)))) {
984                 tmp->verboser = v;
985                 ast_mutex_lock(&msglist_lock);
986                 tmp->next = verboser;
987                 verboser = tmp;
988                 m = list;
989                 while(m) {
990                         /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
991                         v(m->msg, 0, 0, 1);
992                         m = m->next;
993                 }
994                 ast_mutex_unlock(&msglist_lock);
995                 return 0;
996         }
997         return -1;
998 }
999
1000 int ast_unregister_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
1001 {
1002         int res = -1;
1003         struct verb *tmp, *tmpl=NULL;
1004         ast_mutex_lock(&msglist_lock);
1005         tmp = verboser;
1006         while(tmp) {
1007                 if (tmp->verboser == v) {
1008                         if (tmpl)
1009                                 tmpl->next = tmp->next;
1010                         else
1011                                 verboser = tmp->next;
1012                         free(tmp);
1013                         break;
1014                 }
1015                 tmpl = tmp;
1016                 tmp = tmp->next;
1017         }
1018         if (tmp)
1019                 res = 0;
1020         ast_mutex_unlock(&msglist_lock);
1021         return res;
1022 }