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