get rid of another annoying stray space in the gcc commands
[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 "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
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[PATH_MAX] = "";
382         char new[PATH_MAX];
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(&msglist_lock);  /* to avoid deadlock */
389         ast_mutex_lock(&loglock);
390         if (eventlog) 
391                 fclose(eventlog);
392         else 
393                 event_rotate = 0;
394         eventlog = NULL;
395
396         if (qlog) 
397                 fclose(qlog);
398         else 
399                 queue_rotate = 0;
400         qlog = NULL;
401
402         mkdir((char *)ast_config_AST_LOG_DIR, 0755);
403
404         f = logchannels;
405         while(f) {
406                 if (f->disabled) {
407                         f->disabled = 0;        /* Re-enable logging at reload */
408                         manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
409                 }
410                 if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
411                         fclose(f->fileptr);     /* Close file */
412                         f->fileptr = NULL;
413                         if(rotate) {
414                                 ast_copy_string(old, f->filename, sizeof(old));
415         
416                                 for(x=0;;x++) {
417                                         snprintf(new, sizeof(new), "%s.%d", f->filename, x);
418                                         myf = fopen((char *)new, "r");
419                                         if (myf) {
420                                                 fclose(myf);
421                                         } else {
422                                                 break;
423                                         }
424                                 }
425             
426                                 /* do it */
427                                 if (rename(old,new))
428                                         fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
429                         }
430                 }
431                 f = f->next;
432         }
433
434         filesize_reload_needed = 0;
435         
436         init_logger_chain();
437
438         if (logfiles.event_log) {
439                 snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
440                 if (event_rotate) {
441                         for (x=0;;x++) {
442                                 snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, EVENTLOG,x);
443                                 myf = fopen((char *)new, "r");
444                                 if (myf)        /* File exists */
445                                         fclose(myf);
446                                 else
447                                         break;
448                         }
449         
450                         /* do it */
451                         if (rename(old,new))
452                                 ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new);
453                 }
454
455                 eventlog = fopen(old, "a");
456                 if (eventlog) {
457                         ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
458                         if (option_verbose)
459                                 ast_verbose("Asterisk Event Logger restarted\n");
460                 } else {
461                         ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
462                         res = -1;
463                 }
464         }
465
466         if (logfiles.queue_log) {
467                 snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, QUEUELOG);
468                 if (queue_rotate) {
469                         for (x = 0; ; x++) {
470                                 snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, QUEUELOG, x);
471                                 myf = fopen((char *)new, "r");
472                                 if (myf)        /* File exists */
473                                         fclose(myf);
474                                 else
475                                         break;
476                         }
477         
478                         /* do it */
479                         if (rename(old, new))
480                                 ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new);
481                 }
482
483                 qlog = fopen(old, "a");
484                 if (qlog) {
485                         ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
486                         ast_log(LOG_EVENT, "Restarted Asterisk Queue Logger\n");
487                         if (option_verbose)
488                                 ast_verbose("Asterisk Queue Logger restarted\n");
489                 } else {
490                         ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
491                         res = -1;
492                 }
493         }
494         ast_mutex_unlock(&loglock);
495         ast_mutex_unlock(&msglist_lock);
496
497         return res;
498 }
499
500 static int handle_logger_reload(int fd, int argc, char *argv[])
501 {
502         if(reload_logger(0)) {
503                 ast_cli(fd, "Failed to reload the logger\n");
504                 return RESULT_FAILURE;
505         } else
506                 return RESULT_SUCCESS;
507 }
508
509 static int handle_logger_rotate(int fd, int argc, char *argv[])
510 {
511         if(reload_logger(1)) {
512                 ast_cli(fd, "Failed to reload the logger and rotate log files\n");
513                 return RESULT_FAILURE;
514         } else
515                 return RESULT_SUCCESS;
516 }
517
518 /*! \brief CLI command to show logging system configuration */
519 static int handle_logger_show_channels(int fd, int argc, char *argv[])
520 {
521 #define FORMATL "%-35.35s %-8.8s %-9.9s "
522         struct logchannel *chan;
523
524         ast_mutex_lock(&loglock);
525
526         chan = logchannels;
527         ast_cli(fd,FORMATL, "Channel", "Type", "Status");
528         ast_cli(fd, "Configuration\n");
529         ast_cli(fd,FORMATL, "-------", "----", "------");
530         ast_cli(fd, "-------------\n");
531         while (chan) {
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                 chan = chan->next;
551         }
552         ast_cli(fd, "\n");
553
554         ast_mutex_unlock(&loglock);
555                 
556         return RESULT_SUCCESS;
557 }
558
559 static struct verb {
560         void (*verboser)(const char *string, int opos, int replacelast, int complete);
561         struct verb *next;
562 } *verboser = NULL;
563
564
565 static char logger_reload_help[] =
566 "Usage: logger reload\n"
567 "       Reloads the logger subsystem state.  Use after restarting syslogd(8) if you are using syslog logging.\n";
568
569 static char logger_rotate_help[] =
570 "Usage: logger rotate\n"
571 "       Rotates and Reopens the log files.\n";
572
573 static char logger_show_channels_help[] =
574 "Usage: logger show channels\n"
575 "       Show configured logger channels.\n";
576
577 static struct ast_cli_entry logger_show_channels_cli = 
578         { { "logger", "show", "channels", NULL }, 
579         handle_logger_show_channels, "List configured log channels",
580         logger_show_channels_help };
581
582 static struct ast_cli_entry reload_logger_cli = 
583         { { "logger", "reload", NULL }, 
584         handle_logger_reload, "Reopens the log files",
585         logger_reload_help };
586
587 static struct ast_cli_entry rotate_logger_cli = 
588         { { "logger", "rotate", NULL }, 
589         handle_logger_rotate, "Rotates and reopens the log files",
590         logger_rotate_help };
591
592 static int handle_SIGXFSZ(int sig) 
593 {
594         /* Indicate need to reload */
595         filesize_reload_needed = 1;
596         return 0;
597 }
598
599 int init_logger(void)
600 {
601         char tmp[256];
602         int res = 0;
603
604         /* auto rotate if sig SIGXFSZ comes a-knockin */
605         (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ);
606
607         /* register the relaod logger cli command */
608         ast_cli_register(&reload_logger_cli);
609         ast_cli_register(&rotate_logger_cli);
610         ast_cli_register(&logger_show_channels_cli);
611
612         mkdir((char *)ast_config_AST_LOG_DIR, 0755);
613   
614         /* create log channels */
615         init_logger_chain();
616
617         /* create the eventlog */
618         if (logfiles.event_log) {
619                 mkdir((char *)ast_config_AST_LOG_DIR, 0755);
620                 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
621                 eventlog = fopen((char *)tmp, "a");
622                 if (eventlog) {
623                         ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
624                         if (option_verbose)
625                                 ast_verbose("Asterisk Event Logger Started %s\n",(char *)tmp);
626                 } else {
627                         ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
628                         res = -1;
629                 }
630         }
631
632         if (logfiles.queue_log) {
633                 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, QUEUELOG);
634                 qlog = fopen(tmp, "a");
635                 ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
636         }
637         return res;
638 }
639
640 void close_logger(void)
641 {
642         struct msglist *m, *tmp;
643
644         ast_mutex_lock(&msglist_lock);
645         m = list;
646         while(m) {
647                 if (m->msg) {
648                         free(m->msg);
649                 }
650                 tmp = m->next;
651                 free(m);
652                 m = tmp;
653         }
654         list = last = NULL;
655         msgcnt = 0;
656         ast_mutex_unlock(&msglist_lock);
657         return;
658 }
659
660 static void ast_log_vsyslog(int level, const char *file, int line, const char *function, const char *fmt, va_list args) 
661 {
662         char buf[BUFSIZ];
663         char *s;
664
665         if (level >= SYSLOG_NLEVELS) {
666                 /* we are locked here, so cannot ast_log() */
667                 fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level);
668                 return;
669         }
670         if (level == __LOG_VERBOSE) {
671                 snprintf(buf, sizeof(buf), "VERBOSE[%ld]: ", (long)GETTID());
672                 level = __LOG_DEBUG;
673         } else if (level == __LOG_DTMF) {
674                 snprintf(buf, sizeof(buf), "DTMF[%ld]: ", (long)GETTID());
675                 level = __LOG_DEBUG;
676         } else {
677                 snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: ",
678                          levels[level], (long)GETTID(), file, line, function);
679         }
680         s = buf + strlen(buf);
681         vsnprintf(s, sizeof(buf) - strlen(buf), fmt, args);
682         term_strip(s, s, strlen(s) + 1);
683         syslog(syslog_level_map[level], "%s", buf);
684 }
685
686 /*
687  * send log messages to syslog and/or the console
688  */
689 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
690 {
691         struct logchannel *chan;
692         char buf[BUFSIZ];
693         time_t t;
694         struct tm tm;
695         char date[256];
696
697         va_list ap;
698         
699         /* don't display LOG_DEBUG messages unless option_verbose _or_ option_debug
700            are non-zero; LOG_DEBUG messages can still be displayed if option_debug
701            is zero, if option_verbose is non-zero (this allows for 'level zero'
702            LOG_DEBUG messages to be displayed, if the logmask on any channel
703            allows it)
704         */
705         if (!option_verbose && !option_debug && (level == __LOG_DEBUG)) {
706                 return;
707         }
708
709         /* Ignore anything that never gets logged anywhere */
710         if (!(global_logmask & (1 << level)))
711                 return;
712         
713         /* Ignore anything other than the currently debugged file if there is one */
714         if ((level == __LOG_DEBUG) && !ast_strlen_zero(debug_filename) && strcasecmp(debug_filename, file))
715                 return;
716
717         /* begin critical section */
718         ast_mutex_lock(&loglock);
719
720         time(&t);
721         localtime_r(&t, &tm);
722         strftime(date, sizeof(date), dateformat, &tm);
723
724         if (logfiles.event_log && level == __LOG_EVENT) {
725                 va_start(ap, fmt);
726
727                 fprintf(eventlog, "%s asterisk[%ld]: ", date, (long)getpid());
728                 vfprintf(eventlog, fmt, ap);
729                 fflush(eventlog);
730
731                 va_end(ap);
732                 ast_mutex_unlock(&loglock);
733                 return;
734         }
735
736         if (logchannels) {
737                 chan = logchannels;
738                 while(chan && !chan->disabled) {
739                         /* Check syslog channels */
740                         if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << level))) {
741                                 va_start(ap, fmt);
742                                 ast_log_vsyslog(level, file, line, function, fmt, ap);
743                                 va_end(ap);
744                         /* Console channels */
745                         } else if ((chan->logmask & (1 << level)) && (chan->type == LOGTYPE_CONSOLE)) {
746                                 char linestr[128];
747                                 char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
748
749                                 if (level != __LOG_VERBOSE) {
750                                         sprintf(linestr, "%d", line);
751                                         snprintf(buf, sizeof(buf), ast_opt_timestamp ? "[%s] %s[%ld]: %s:%s %s: " : "%s %s[%ld]: %s:%s %s: ",
752                                                 date,
753                                                 term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)),
754                                                 (long)GETTID(),
755                                                 term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
756                                                 term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
757                                                 term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
758                                         
759                                         ast_console_puts_mutable(buf);
760                                         va_start(ap, fmt);
761                                         vsnprintf(buf, sizeof(buf), fmt, ap);
762                                         va_end(ap);
763                                         ast_console_puts_mutable(buf);
764                                 }
765                         /* File channels */
766                         } else if ((chan->logmask & (1 << level)) && (chan->fileptr)) {
767                                 int res;
768                                 snprintf(buf, sizeof(buf), ast_opt_timestamp ? "[%s] %s[%ld]: " : "%s %s[%ld] %s: ", date,
769                                         levels[level], (long)GETTID(), file);
770                                 res = fprintf(chan->fileptr, buf);
771                                 if (res <= 0 && buf[0] != '\0') {       /* Error, no characters printed */
772                                         fprintf(stderr,"**** Asterisk Logging Error: ***********\n");
773                                         if (errno == ENOMEM || errno == ENOSPC) {
774                                                 fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename);
775                                         } else
776                                                 fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno));
777                                         manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno));
778                                         chan->disabled = 1;     
779                                 } else {
780                                         /* No error message, continue printing */
781                                         va_start(ap, fmt);
782                                         vsnprintf(buf, sizeof(buf), fmt, ap);
783                                         va_end(ap);
784                                         term_strip(buf, buf, sizeof(buf));
785                                         fputs(buf, chan->fileptr);
786                                         fflush(chan->fileptr);
787                                 }
788                         }
789                         chan = chan->next;
790                 }
791         } else {
792                 /* 
793                  * we don't have the logger chain configured yet,
794                  * so just log to stdout 
795                 */
796                 if (level != __LOG_VERBOSE) {
797                         va_start(ap, fmt);
798                         vsnprintf(buf, sizeof(buf), fmt, ap);
799                         va_end(ap);
800                         fputs(buf, stdout);
801                 }
802         }
803
804         ast_mutex_unlock(&loglock);
805         /* end critical section */
806         if (filesize_reload_needed) {
807                 reload_logger(1);
808                 ast_log(LOG_EVENT,"Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
809                 if (option_verbose)
810                         ast_verbose("Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
811         }
812 }
813
814 void ast_backtrace(void)
815 {
816 #ifdef STACK_BACKTRACES
817         int count=0, i=0;
818         void **addresses;
819         char **strings;
820
821         if ((addresses = ast_calloc(MAX_BACKTRACE_FRAMES, sizeof(*addresses)))) {
822                 count = backtrace(addresses, MAX_BACKTRACE_FRAMES);
823                 if ((strings = backtrace_symbols(addresses, count))) {
824                         ast_log(LOG_DEBUG, "Got %d backtrace record%c\n", count, count != 1 ? 's' : ' ');
825                         for (i=0; i < count ; i++) {
826                                 ast_log(LOG_DEBUG, "#%d: [%08X] %s\n", i, (unsigned int)addresses[i], strings[i]);
827                         }
828                         free(strings);
829                 } else {
830                         ast_log(LOG_DEBUG, "Could not allocate memory for backtrace\n");
831                 }
832                 free(addresses);
833         }
834 #else
835 #ifdef Linux
836         ast_log(LOG_WARNING, "Must compile with 'make dont-optimize' for stack backtraces\n");
837 #else
838         ast_log(LOG_WARNING, "Inline stack backtraces are only available on the Linux platform.\n");
839 #endif
840 #endif
841 }
842
843 void ast_verbose(const char *fmt, ...)
844 {
845         static char stuff[4096];
846         static int len = 0;
847         static int replacelast = 0;
848
849         int complete;
850         int olen;
851         struct msglist *m;
852         struct verb *v;
853         
854         va_list ap;
855         va_start(ap, fmt);
856
857         if (ast_opt_timestamp) {
858                 time_t t;
859                 struct tm tm;
860                 char date[40];
861                 char *datefmt;
862
863                 time(&t);
864                 localtime_r(&t, &tm);
865                 strftime(date, sizeof(date), dateformat, &tm);
866                 datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1);
867         }
868
869         /* this lock is also protecting against multiple threads
870            being in this function at the same time, so it must be
871            held before any of the static variables are accessed
872         */
873         ast_mutex_lock(&msglist_lock);
874
875         /* there is a potential security problem here: if formatting
876            the current date using 'dateformat' results in a string
877            containing '%', then the vsnprintf() call below will
878            probably try to access random memory
879         */
880         vsnprintf(stuff + len, sizeof(stuff) - len, fmt, ap);
881         va_end(ap);
882
883         olen = len;
884         len = strlen(stuff);
885
886         complete = (stuff[len - 1] == '\n') ? 1 : 0;
887
888         /* If we filled up the stuff completely, then log it even without the '\n' */
889         if (len >= sizeof(stuff) - 1) {
890                 complete = 1;
891                 len = 0;
892         }
893
894         if (complete) {
895                 if (msgcnt < MAX_MSG_QUEUE) {
896                         /* Allocate new structure */
897                         if ((m = ast_malloc(sizeof(*m))))
898                                 msgcnt++;
899                 } else {
900                         /* Recycle the oldest entry */
901                         m = list;
902                         list = list->next;
903                         free(m->msg);
904                 }
905                 if (m) {
906                         if ((m->msg = ast_strdup(stuff))) {
907                                 if (last)
908                                         last->next = m;
909                                 else
910                                         list = m;
911                                 m->next = NULL;
912                                 last = m;
913                         } else {
914                                 msgcnt--;
915                                 free(m);
916                         }
917                 }
918         }
919
920         for (v = verboser; v; v = v->next)
921                 v->verboser(stuff, olen, replacelast, complete);
922
923         ast_log(LOG_VERBOSE, "%s", stuff);
924
925         if (len) {
926                 if (!complete)
927                         replacelast = 1;
928                 else 
929                         replacelast = len = 0;
930         }
931
932         ast_mutex_unlock(&msglist_lock);
933 }
934
935 int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete))
936 {
937         struct msglist *m;
938         ast_mutex_lock(&msglist_lock);
939         m = list;
940         while(m) {
941                 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
942                 v(m->msg, 0, 0, 1);
943                 m = m->next;
944         }
945         ast_mutex_unlock(&msglist_lock);
946         return 0;
947 }
948
949 int ast_register_verbose(void (*v)(const char *string, int opos, int replacelast, int complete)) 
950 {
951         struct msglist *m;
952         struct verb *tmp;
953         /* XXX Should be more flexible here, taking > 1 verboser XXX */
954         if ((tmp = ast_malloc(sizeof(*tmp)))) {
955                 tmp->verboser = v;
956                 ast_mutex_lock(&msglist_lock);
957                 tmp->next = verboser;
958                 verboser = tmp;
959                 m = list;
960                 while(m) {
961                         /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
962                         v(m->msg, 0, 0, 1);
963                         m = m->next;
964                 }
965                 ast_mutex_unlock(&msglist_lock);
966                 return 0;
967         }
968         return -1;
969 }
970
971 int ast_unregister_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
972 {
973         int res = -1;
974         struct verb *tmp, *tmpl=NULL;
975         ast_mutex_lock(&msglist_lock);
976         tmp = verboser;
977         while(tmp) {
978                 if (tmp->verboser == v) {
979                         if (tmpl)
980                                 tmpl->next = tmp->next;
981                         else
982                                 verboser = tmp->next;
983                         free(tmp);
984                         break;
985                 }
986                 tmpl = tmp;
987                 tmp = tmp->next;
988         }
989         if (tmp)
990                 res = 0;
991         ast_mutex_unlock(&msglist_lock);
992         return res;
993 }