Documentation / whitespace fix.
[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) || !(chan = ast_calloc(1, sizeof(*chan))))
188                 return NULL;
189
190         if (!strcasecmp(channel, "console")) {
191                 chan->type = LOGTYPE_CONSOLE;
192         } else if (!strncasecmp(channel, "syslog", 6)) {
193                 /*
194                 * syntax is:
195                 *  syslog.facility => level,level,level
196                 */
197                 facility = strchr(channel, '.');
198                 if(!facility++ || !facility) {
199                         facility = "local0";
200                 }
201
202 #ifndef SOLARIS
203                 /*
204                 * Walk through the list of facilitynames (defined in sys/syslog.h)
205                 * to see if we can find the one we have been given
206                 */
207                 chan->facility = -1;
208                 cptr = facilitynames;
209                 while (cptr->c_name) {
210                         if (!strcasecmp(facility, cptr->c_name)) {
211                                 chan->facility = cptr->c_val;
212                                 break;
213                         }
214                         cptr++;
215                 }
216 #else
217                 chan->facility = -1;
218                 if (!strcasecmp(facility, "kern")) 
219                         chan->facility = LOG_KERN;
220                 else if (!strcasecmp(facility, "USER")) 
221                         chan->facility = LOG_USER;
222                 else if (!strcasecmp(facility, "MAIL")) 
223                         chan->facility = LOG_MAIL;
224                 else if (!strcasecmp(facility, "DAEMON")) 
225                         chan->facility = LOG_DAEMON;
226                 else if (!strcasecmp(facility, "AUTH")) 
227                         chan->facility = LOG_AUTH;
228                 else if (!strcasecmp(facility, "SYSLOG")) 
229                         chan->facility = LOG_SYSLOG;
230                 else if (!strcasecmp(facility, "LPR")) 
231                         chan->facility = LOG_LPR;
232                 else if (!strcasecmp(facility, "NEWS")) 
233                         chan->facility = LOG_NEWS;
234                 else if (!strcasecmp(facility, "UUCP")) 
235                         chan->facility = LOG_UUCP;
236                 else if (!strcasecmp(facility, "CRON")) 
237                         chan->facility = LOG_CRON;
238                 else if (!strcasecmp(facility, "LOCAL0")) 
239                         chan->facility = LOG_LOCAL0;
240                 else if (!strcasecmp(facility, "LOCAL1")) 
241                         chan->facility = LOG_LOCAL1;
242                 else if (!strcasecmp(facility, "LOCAL2")) 
243                         chan->facility = LOG_LOCAL2;
244                 else if (!strcasecmp(facility, "LOCAL3")) 
245                         chan->facility = LOG_LOCAL3;
246                 else if (!strcasecmp(facility, "LOCAL4")) 
247                         chan->facility = LOG_LOCAL4;
248                 else if (!strcasecmp(facility, "LOCAL5")) 
249                         chan->facility = LOG_LOCAL5;
250                 else if (!strcasecmp(facility, "LOCAL6")) 
251                         chan->facility = LOG_LOCAL6;
252                 else if (!strcasecmp(facility, "LOCAL7")) 
253                         chan->facility = LOG_LOCAL7;
254 #endif /* Solaris */
255
256                 if (0 > chan->facility) {
257                         fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
258                         free(chan);
259                         return NULL;
260                 }
261
262                 chan->type = LOGTYPE_SYSLOG;
263                 snprintf(chan->filename, sizeof(chan->filename), "%s", channel);
264                 openlog("asterisk", LOG_PID, chan->facility);
265         } else {
266                 if (channel[0] == '/') {
267                         if(!ast_strlen_zero(hostname)) { 
268                                 snprintf(chan->filename, sizeof(chan->filename) - 1,"%s.%s", channel, hostname);
269                         } else {
270                                 ast_copy_string(chan->filename, channel, sizeof(chan->filename));
271                         }
272                 }                 
273                 
274                 if(!ast_strlen_zero(hostname)) {
275                         snprintf(chan->filename, sizeof(chan->filename), "%s/%s.%s",(char *)ast_config_AST_LOG_DIR, channel, hostname);
276                 } else {
277                         snprintf(chan->filename, sizeof(chan->filename), "%s/%s", (char *)ast_config_AST_LOG_DIR, channel);
278                 }
279                 chan->fileptr = fopen(chan->filename, "a");
280                 if (!chan->fileptr) {
281                         /* Can't log here, since we're called with a lock */
282                         fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", chan->filename, strerror(errno));
283                 } 
284                 chan->type = LOGTYPE_FILE;
285         }
286         chan->logmask = make_components(components, lineno);
287         return chan;
288 }
289
290 static void init_logger_chain(void)
291 {
292         struct logchannel *chan, *cur;
293         struct ast_config *cfg;
294         struct ast_variable *var;
295         char *s;
296
297         /* delete our list of log channels */
298         ast_mutex_lock(&loglock);
299         chan = logchannels;
300         while (chan) {
301                 cur = chan->next;
302                 free(chan);
303                 chan = cur;
304         }
305         logchannels = NULL;
306         ast_mutex_unlock(&loglock);
307         
308         global_logmask = 0;
309         /* close syslog */
310         closelog();
311         
312         cfg = ast_config_load("logger.conf");
313         
314         /* If no config file, we're fine, set default options. */
315         if (!cfg) {
316                 fprintf(stderr, "Unable to open logger.conf: %s\n", strerror(errno));
317                 if (!(chan = ast_calloc(1, sizeof(*chan))))
318                         return;
319                 chan->type = LOGTYPE_CONSOLE;
320                 chan->logmask = 28; /*warning,notice,error */
321                 chan->next = logchannels;
322                 logchannels = chan;
323                 global_logmask |= chan->logmask;
324                 return;
325         }
326         
327         ast_mutex_lock(&loglock);
328         if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
329                 if(ast_true(s)) {
330                         if(gethostname(hostname, sizeof(hostname)-1)) {
331                                 ast_copy_string(hostname, "unknown", sizeof(hostname));
332                                 ast_log(LOG_WARNING, "What box has no hostname???\n");
333                         }
334                 } else
335                         hostname[0] = '\0';
336         } else
337                 hostname[0] = '\0';
338         if ((s = ast_variable_retrieve(cfg, "general", "dateformat"))) {
339                 ast_copy_string(dateformat, s, sizeof(dateformat));
340         } else
341                 ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
342         if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) {
343                 logfiles.queue_log = ast_true(s);
344         }
345         if ((s = ast_variable_retrieve(cfg, "general", "event_log"))) {
346                 logfiles.event_log = ast_true(s);
347         }
348
349         var = ast_variable_browse(cfg, "logfiles");
350         while(var) {
351                 chan = make_logchannel(var->name, var->value, var->lineno);
352                 if (chan) {
353                         chan->next = logchannels;
354                         logchannels = chan;
355                         global_logmask |= chan->logmask;
356                 }
357                 var = var->next;
358         }
359
360         ast_config_destroy(cfg);
361         ast_mutex_unlock(&loglock);
362 }
363
364 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
365 {
366         va_list ap;
367         ast_mutex_lock(&loglock);
368         if (qlog) {
369                 va_start(ap, fmt);
370                 fprintf(qlog, "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
371                 vfprintf(qlog, fmt, ap);
372                 fprintf(qlog, "\n");
373                 va_end(ap);
374                 fflush(qlog);
375         }
376         ast_mutex_unlock(&loglock);
377 }
378
379 int reload_logger(int rotate)
380 {
381         char old[AST_CONFIG_MAX_PATH] = "";
382         char new[AST_CONFIG_MAX_PATH];
383         int event_rotate = rotate, queue_rotate = rotate;
384         struct logchannel *f;
385         FILE *myf;
386         int x, res = 0;
387
388         ast_mutex_lock(&loglock);
389         if (eventlog) 
390                 fclose(eventlog);
391         else 
392                 event_rotate = 0;
393         eventlog = NULL;
394
395         if (qlog) 
396                 fclose(qlog);
397         else 
398                 queue_rotate = 0;
399         qlog = NULL;
400
401         mkdir((char *)ast_config_AST_LOG_DIR, 0755);
402
403         f = logchannels;
404         while(f) {
405                 if (f->disabled) {
406                         f->disabled = 0;        /* Re-enable logging at reload */
407                         manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
408                 }
409                 if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
410                         fclose(f->fileptr);     /* Close file */
411                         f->fileptr = NULL;
412                         if(rotate) {
413                                 ast_copy_string(old, f->filename, sizeof(old));
414         
415                                 for(x=0;;x++) {
416                                         snprintf(new, sizeof(new), "%s.%d", f->filename, x);
417                                         myf = fopen((char *)new, "r");
418                                         if (myf) {
419                                                 fclose(myf);
420                                         } else {
421                                                 break;
422                                         }
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                 f = f->next;
431         }
432
433         filesize_reload_needed = 0;
434         
435         init_logger_chain();
436
437         if (logfiles.event_log) {
438                 snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
439                 if (event_rotate) {
440                         for (x=0;;x++) {
441                                 snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, EVENTLOG,x);
442                                 myf = fopen((char *)new, "r");
443                                 if (myf)        /* File exists */
444                                         fclose(myf);
445                                 else
446                                         break;
447                         }
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", (char *)ast_config_AST_LOG_DIR, QUEUELOG);
467                 if (queue_rotate) {
468                         for (x = 0; ; x++) {
469                                 snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, QUEUELOG, x);
470                                 myf = fopen((char *)new, "r");
471                                 if (myf)        /* File exists */
472                                         fclose(myf);
473                                 else
474                                         break;
475                         }
476         
477                         /* do it */
478                         if (rename(old, new))
479                                 ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new);
480                 }
481
482                 qlog = fopen(old, "a");
483                 if (qlog) {
484                         ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
485                         ast_log(LOG_EVENT, "Restarted Asterisk Queue Logger\n");
486                         if (option_verbose)
487                                 ast_verbose("Asterisk Queue Logger restarted\n");
488                 } else {
489                         ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
490                         res = -1;
491                 }
492         }
493         ast_mutex_unlock(&loglock);
494
495         return res;
496 }
497
498 static int handle_logger_reload(int fd, int argc, char *argv[])
499 {
500         if(reload_logger(0)) {
501                 ast_cli(fd, "Failed to reload the logger\n");
502                 return RESULT_FAILURE;
503         } else
504                 return RESULT_SUCCESS;
505 }
506
507 static int handle_logger_rotate(int fd, int argc, char *argv[])
508 {
509         if(reload_logger(1)) {
510                 ast_cli(fd, "Failed to reload the logger and rotate log files\n");
511                 return RESULT_FAILURE;
512         } else
513                 return RESULT_SUCCESS;
514 }
515
516 /*! \brief CLI command to show logging system configuration */
517 static int handle_logger_show_channels(int fd, int argc, char *argv[])
518 {
519 #define FORMATL "%-35.35s %-8.8s %-9.9s "
520         struct logchannel *chan;
521
522         ast_mutex_lock(&loglock);
523
524         chan = logchannels;
525         ast_cli(fd,FORMATL, "Channel", "Type", "Status");
526         ast_cli(fd, "Configuration\n");
527         ast_cli(fd,FORMATL, "-------", "----", "------");
528         ast_cli(fd, "-------------\n");
529         while (chan) {
530                 ast_cli(fd, FORMATL, chan->filename, chan->type==LOGTYPE_CONSOLE ? "Console" : (chan->type==LOGTYPE_SYSLOG ? "Syslog" : "File"),
531                         chan->disabled ? "Disabled" : "Enabled");
532                 ast_cli(fd, " - ");
533                 if (chan->logmask & (1 << __LOG_DEBUG)) 
534                         ast_cli(fd, "Debug ");
535                 if (chan->logmask & (1 << __LOG_DTMF)) 
536                         ast_cli(fd, "DTMF ");
537                 if (chan->logmask & (1 << __LOG_VERBOSE)) 
538                         ast_cli(fd, "Verbose ");
539                 if (chan->logmask & (1 << __LOG_WARNING)) 
540                         ast_cli(fd, "Warning ");
541                 if (chan->logmask & (1 << __LOG_NOTICE)) 
542                         ast_cli(fd, "Notice ");
543                 if (chan->logmask & (1 << __LOG_ERROR)) 
544                         ast_cli(fd, "Error ");
545                 if (chan->logmask & (1 << __LOG_EVENT)) 
546                         ast_cli(fd, "Event ");
547                 ast_cli(fd, "\n");
548                 chan = chan->next;
549         }
550         ast_cli(fd, "\n");
551
552         ast_mutex_unlock(&loglock);
553                 
554         return RESULT_SUCCESS;
555 }
556
557 static struct verb {
558         void (*verboser)(const char *string, int opos, int replacelast, int complete);
559         struct verb *next;
560 } *verboser = NULL;
561
562
563 static char logger_reload_help[] =
564 "Usage: logger reload\n"
565 "       Reloads the logger subsystem state.  Use after restarting syslogd(8) if you are using syslog logging.\n";
566
567 static char logger_rotate_help[] =
568 "Usage: logger rotate\n"
569 "       Rotates and Reopens the log files.\n";
570
571 static char logger_show_channels_help[] =
572 "Usage: logger show channels\n"
573 "       Show configured logger channels.\n";
574
575 static struct ast_cli_entry logger_show_channels_cli = 
576         { { "logger", "show", "channels", NULL }, 
577         handle_logger_show_channels, "List configured log channels",
578         logger_show_channels_help };
579
580 static struct ast_cli_entry reload_logger_cli = 
581         { { "logger", "reload", NULL }, 
582         handle_logger_reload, "Reopens the log files",
583         logger_reload_help };
584
585 static struct ast_cli_entry rotate_logger_cli = 
586         { { "logger", "rotate", NULL }, 
587         handle_logger_rotate, "Rotates and reopens the log files",
588         logger_rotate_help };
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 relaod logger cli command */
606         ast_cli_register(&reload_logger_cli);
607         ast_cli_register(&rotate_logger_cli);
608         ast_cli_register(&logger_show_channels_cli);
609
610         mkdir((char *)ast_config_AST_LOG_DIR, 0755);
611   
612         /* create log channels */
613         init_logger_chain();
614
615         /* create the eventlog */
616         if (logfiles.event_log) {
617                 mkdir((char *)ast_config_AST_LOG_DIR, 0755);
618                 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
619                 eventlog = fopen((char *)tmp, "a");
620                 if (eventlog) {
621                         ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
622                         if (option_verbose)
623                                 ast_verbose("Asterisk Event Logger Started %s\n",(char *)tmp);
624                 } else {
625                         ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
626                         res = -1;
627                 }
628         }
629
630         if (logfiles.queue_log) {
631                 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, QUEUELOG);
632                 qlog = fopen(tmp, "a");
633                 ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
634         }
635         return res;
636 }
637
638 void close_logger(void)
639 {
640         struct msglist *m, *tmp;
641
642         ast_mutex_lock(&msglist_lock);
643         m = list;
644         while(m) {
645                 if (m->msg) {
646                         free(m->msg);
647                 }
648                 tmp = m->next;
649                 free(m);
650                 m = tmp;
651         }
652         list = last = NULL;
653         msgcnt = 0;
654         ast_mutex_unlock(&msglist_lock);
655         return;
656 }
657
658 static void ast_log_vsyslog(int level, const char *file, int line, const char *function, const char *fmt, va_list args) 
659 {
660         char buf[BUFSIZ];
661         char *s;
662
663         if (level >= SYSLOG_NLEVELS) {
664                 /* we are locked here, so cannot ast_log() */
665                 fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level);
666                 return;
667         }
668         if (level == __LOG_VERBOSE) {
669                 snprintf(buf, sizeof(buf), "VERBOSE[%ld]: ", (long)GETTID());
670                 level = __LOG_DEBUG;
671         } else if (level == __LOG_DTMF) {
672                 snprintf(buf, sizeof(buf), "DTMF[%ld]: ", (long)GETTID());
673                 level = __LOG_DEBUG;
674         } else {
675                 snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: ",
676                          levels[level], (long)GETTID(), file, line, function);
677         }
678         s = buf + strlen(buf);
679         vsnprintf(s, sizeof(buf) - strlen(buf), fmt, args);
680         term_strip(s, s, strlen(s) + 1);
681         syslog(syslog_level_map[level], "%s", buf);
682 }
683
684 /*
685  * send log messages to syslog and/or the console
686  */
687 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
688 {
689         struct logchannel *chan;
690         char buf[BUFSIZ];
691         time_t t;
692         struct tm tm;
693         char date[256];
694
695         va_list ap;
696         
697         /* don't display LOG_DEBUG messages unless option_verbose _or_ option_debug
698            are non-zero; LOG_DEBUG messages can still be displayed if option_debug
699            is zero, if option_verbose is non-zero (this allows for 'level zero'
700            LOG_DEBUG messages to be displayed, if the logmask on any channel
701            allows it)
702         */
703         if (!option_verbose && !option_debug && (level == __LOG_DEBUG)) {
704                 return;
705         }
706
707         /* Ignore anything that never gets logged anywhere */
708         if (!(global_logmask & (1 << level)))
709                 return;
710         
711         /* Ignore anything other than the currently debugged file if there is one */
712         if ((level == __LOG_DEBUG) && !ast_strlen_zero(debug_filename) && strcasecmp(debug_filename, file))
713                 return;
714
715         /* begin critical section */
716         ast_mutex_lock(&loglock);
717
718         time(&t);
719         localtime_r(&t, &tm);
720         strftime(date, sizeof(date), dateformat, &tm);
721
722         if (logfiles.event_log && level == __LOG_EVENT) {
723                 va_start(ap, fmt);
724
725                 fprintf(eventlog, "%s asterisk[%d]: ", date, getpid());
726                 vfprintf(eventlog, fmt, ap);
727                 fflush(eventlog);
728
729                 va_end(ap);
730                 ast_mutex_unlock(&loglock);
731                 return;
732         }
733
734         if (logchannels) {
735                 chan = logchannels;
736                 while(chan && !chan->disabled) {
737                         /* Check syslog channels */
738                         if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << level))) {
739                                 va_start(ap, fmt);
740                                 ast_log_vsyslog(level, file, line, function, fmt, ap);
741                                 va_end(ap);
742                         /* Console channels */
743                         } else if ((chan->logmask & (1 << level)) && (chan->type == LOGTYPE_CONSOLE)) {
744                                 char linestr[128];
745                                 char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
746
747                                 if (level != __LOG_VERBOSE) {
748                                         sprintf(linestr, "%d", line);
749                                         snprintf(buf, sizeof(buf), ast_opt_timestamp ? "[%s] %s[%ld]: %s:%s %s: " : "%s %s[%ld]: %s:%s %s: ",
750                                                 date,
751                                                 term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)),
752                                                 (long)GETTID(),
753                                                 term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
754                                                 term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
755                                                 term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
756                                         
757                                         ast_console_puts(buf);
758                                         va_start(ap, fmt);
759                                         vsnprintf(buf, sizeof(buf), fmt, ap);
760                                         va_end(ap);
761                                         ast_console_puts(buf);
762                                 }
763                         /* File channels */
764                         } else if ((chan->logmask & (1 << level)) && (chan->fileptr)) {
765                                 int res;
766                                 snprintf(buf, sizeof(buf), ast_opt_timestamp ? "[%s] %s[%ld]: " : "%s %s[%ld] %s: ", date,
767                                         levels[level], (long)GETTID(), file);
768                                 res = fprintf(chan->fileptr, buf);
769                                 if (res <= 0 && buf[0] != '\0') {       /* Error, no characters printed */
770                                         fprintf(stderr,"**** Asterisk Logging Error: ***********\n");
771                                         if (errno == ENOMEM || errno == ENOSPC) {
772                                                 fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename);
773                                         } else
774                                                 fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno));
775                                         manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno));
776                                         chan->disabled = 1;     
777                                 } else {
778                                         /* No error message, continue printing */
779                                         va_start(ap, fmt);
780                                         vsnprintf(buf, sizeof(buf), fmt, ap);
781                                         va_end(ap);
782                                         term_strip(buf, buf, sizeof(buf));
783                                         fputs(buf, chan->fileptr);
784                                         fflush(chan->fileptr);
785                                 }
786                         }
787                         chan = chan->next;
788                 }
789         } else {
790                 /* 
791                  * we don't have the logger chain configured yet,
792                  * so just log to stdout 
793                 */
794                 if (level != __LOG_VERBOSE) {
795                         va_start(ap, fmt);
796                         vsnprintf(buf, sizeof(buf), fmt, ap);
797                         va_end(ap);
798                         fputs(buf, stdout);
799                 }
800         }
801
802         ast_mutex_unlock(&loglock);
803         /* end critical section */
804         if (filesize_reload_needed) {
805                 reload_logger(1);
806                 ast_log(LOG_EVENT,"Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
807                 if (option_verbose)
808                         ast_verbose("Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
809         }
810 }
811
812 void ast_backtrace(void)
813 {
814 #ifdef STACK_BACKTRACES
815         int count=0, i=0;
816         void **addresses;
817         char **strings;
818
819         if ((addresses = ast_calloc(MAX_BACKTRACE_FRAMES, sizeof(*addresses)))) {
820                 count = backtrace(addresses, MAX_BACKTRACE_FRAMES);
821                 if ((strings = backtrace_symbols(addresses, count))) {
822                         ast_log(LOG_DEBUG, "Got %d backtrace record%c\n", count, count != 1 ? 's' : ' ');
823                         for (i=0; i < count ; i++) {
824                                 ast_log(LOG_DEBUG, "#%d: [%08X] %s\n", i, (unsigned int)addresses[i], strings[i]);
825                         }
826                         free(strings);
827                 } else {
828                         ast_log(LOG_DEBUG, "Could not allocate memory for backtrace\n");
829                 }
830                 free(addresses);
831         }
832 #else
833 #ifdef Linux
834         ast_log(LOG_WARNING, "Must compile with 'make dont-optimize' for stack backtraces\n");
835 #else
836         ast_log(LOG_WARNING, "Inline stack backtraces are only available on the Linux platform.\n");
837 #endif
838 #endif
839 }
840
841 void ast_verbose(const char *fmt, ...)
842 {
843         static char stuff[4096];
844         static int len = 0;
845         static int replacelast = 0;
846
847         int complete;
848         int olen;
849         struct msglist *m;
850         struct verb *v;
851         
852         va_list ap;
853         va_start(ap, fmt);
854
855         if (ast_opt_timestamp) {
856                 time_t t;
857                 struct tm tm;
858                 char date[40];
859                 char *datefmt;
860
861                 time(&t);
862                 localtime_r(&t, &tm);
863                 strftime(date, sizeof(date), dateformat, &tm);
864                 if ((datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1))) {
865                         sprintf(datefmt, "[%s] %s", date, fmt);
866                         fmt = datefmt;
867                 }
868         }
869
870         /* this lock is also protecting against multiple threads
871            being in this function at the same time, so it must be
872            held before any of the static variables are accessed
873         */
874         ast_mutex_lock(&msglist_lock);
875
876         /* there is a potential security problem here: if formatting
877            the current date using 'dateformat' results in a string
878            containing '%', then the vsnprintf() call below will
879            probably try to access random memory
880         */
881         vsnprintf(stuff + len, sizeof(stuff) - len, fmt, ap);
882         va_end(ap);
883
884         olen = len;
885         len = strlen(stuff);
886
887         complete = (stuff[len - 1] == '\n') ? 1 : 0;
888
889         /* If we filled up the stuff completely, then log it even without the '\n' */
890         if (len >= sizeof(stuff) - 1) {
891                 complete = 1;
892                 len = 0;
893         }
894
895         if (complete) {
896                 if (msgcnt < MAX_MSG_QUEUE) {
897                         /* Allocate new structure */
898                         if ((m = ast_malloc(sizeof(*m))))
899                                 msgcnt++;
900                 } else {
901                         /* Recycle the oldest entry */
902                         m = list;
903                         list = list->next;
904                         free(m->msg);
905                 }
906                 if (m) {
907                         if ((m->msg = ast_strdup(stuff))) {
908                                 if (last)
909                                         last->next = m;
910                                 else
911                                         list = m;
912                                 m->next = NULL;
913                                 last = m;
914                         } else {
915                                 msgcnt--;
916                                 free(m);
917                         }
918                 }
919         }
920
921         for (v = verboser; v; v = v->next)
922                 v->verboser(stuff, olen, replacelast, complete);
923
924         ast_log(LOG_VERBOSE, "%s", stuff);
925
926         if (len) {
927                 if (!complete)
928                         replacelast = 1;
929                 else 
930                         replacelast = len = 0;
931         }
932
933         ast_mutex_unlock(&msglist_lock);
934 }
935
936 int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete))
937 {
938         struct msglist *m;
939         ast_mutex_lock(&msglist_lock);
940         m = list;
941         while(m) {
942                 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
943                 v(m->msg, 0, 0, 1);
944                 m = m->next;
945         }
946         ast_mutex_unlock(&msglist_lock);
947         return 0;
948 }
949
950 int ast_register_verbose(void (*v)(const char *string, int opos, int replacelast, int complete)) 
951 {
952         struct msglist *m;
953         struct verb *tmp;
954         /* XXX Should be more flexible here, taking > 1 verboser XXX */
955         if ((tmp = ast_malloc(sizeof(*tmp)))) {
956                 tmp->verboser = v;
957                 ast_mutex_lock(&msglist_lock);
958                 tmp->next = verboser;
959                 verboser = tmp;
960                 m = list;
961                 while(m) {
962                         /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
963                         v(m->msg, 0, 0, 1);
964                         m = m->next;
965                 }
966                 ast_mutex_unlock(&msglist_lock);
967                 return 0;
968         }
969         return -1;
970 }
971
972 int ast_unregister_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
973 {
974         int res = -1;
975         struct verb *tmp, *tmpl=NULL;
976         ast_mutex_lock(&msglist_lock);
977         tmp = verboser;
978         while(tmp) {
979                 if (tmp->verboser == v) {
980                         if (tmpl)
981                                 tmpl->next = tmp->next;
982                         else
983                                 verboser = tmp->next;
984                         free(tmp);
985                         break;
986                 }
987                 tmpl = tmp;
988                 tmp = tmp->next;
989         }
990         if (tmp)
991                 res = 0;
992         ast_mutex_unlock(&msglist_lock);
993         return res;
994 }