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