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