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