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