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