various minor portability fixes (mostly from tholo for OpenBSD)
[asterisk-bristuff.git] / logger.c
blob309e13a50d12abc8906238f4701c40a09d66610a
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
19 /*! \file
21 * \brief Asterisk Logger
23 * Logging routines
25 * \author Mark Spencer <markster@digium.com>
28 #include "asterisk.h"
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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
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>
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
60 #define SYSLOG_NLEVELS sizeof(syslog_level_map) / sizeof(int)
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"
72 #define MAX_MSG_QUEUE 200
74 #if defined(__linux__) && !defined(__NR_gettid)
75 #include <asm/unistd.h>
76 #endif
78 #if defined(__linux__) && defined(__NR_gettid)
79 #define GETTID() syscall(__NR_gettid)
80 #else
81 #define GETTID() getpid()
82 #endif
85 static char dateformat[256] = "%b %e %T"; /* Original Asterisk Format */
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;
92 static struct {
93 unsigned int queue_log:1;
94 unsigned int event_log:1;
95 } logfiles = { 1, 1 };
97 static struct msglist {
98 char *msg;
99 struct msglist *next;
100 } *list = NULL, *last = NULL;
102 static char hostname[MAXHOSTNAMELEN];
104 enum logtypes {
105 LOGTYPE_SYSLOG,
106 LOGTYPE_FILE,
107 LOGTYPE_CONSOLE,
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 */
120 static struct logchannel *logchannels = NULL;
122 static int msgcnt = 0;
124 static FILE *eventlog = NULL;
125 static FILE *qlog = NULL;
127 static char *levels[] = {
128 "DEBUG",
129 "EVENT",
130 "NOTICE",
131 "WARNING",
132 "ERROR",
133 "VERBOSE",
134 "DTMF"
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
147 static int make_components(char *s, int lineno)
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);
174 w = strsep(&stringp, ",");
176 return res;
179 static struct logchannel *make_logchannel(char *channel, char *components, int lineno)
181 struct logchannel *chan;
182 char *facility;
183 #ifndef SOLARIS
184 CODE *cptr;
185 #endif
187 if (ast_strlen_zero(channel) || !(chan = ast_calloc(1, sizeof(*chan))))
188 return NULL;
190 if (!strcasecmp(channel, "console")) {
191 chan->type = LOGTYPE_CONSOLE;
192 } else if (!strncasecmp(channel, "syslog", 6)) {
194 * syntax is:
195 * syslog.facility => level,level,level
197 facility = strchr(channel, '.');
198 if(!facility++ || !facility) {
199 facility = "local0";
202 #ifndef SOLARIS
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
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;
214 cptr++;
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 */
256 if (0 > chan->facility) {
257 fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
258 free(chan);
259 return NULL;
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));
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);
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));
284 chan->type = LOGTYPE_FILE;
286 chan->logmask = make_components(components, lineno);
287 return chan;
290 static void init_logger_chain(void)
292 struct logchannel *chan, *cur;
293 struct ast_config *cfg;
294 struct ast_variable *var;
295 char *s;
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;
305 logchannels = NULL;
306 ast_mutex_unlock(&loglock);
308 global_logmask = 0;
309 /* close syslog */
310 closelog();
312 cfg = ast_config_load("logger.conf");
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;
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");
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);
345 if ((s = ast_variable_retrieve(cfg, "general", "event_log"))) {
346 logfiles.event_log = ast_true(s);
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;
357 var = var->next;
360 ast_config_destroy(cfg);
361 ast_mutex_unlock(&loglock);
364 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
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);
376 ast_mutex_unlock(&loglock);
379 int reload_logger(int rotate)
381 char old[AST_CONFIG_MAX_PATH] = "";
382 char new[AST_CONFIG_MAX_PATH];
383 int event_rotate = rotate, queue_rotate = rotate;
384 struct logchannel *f;
385 FILE *myf;
386 int x, res = 0;
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;
396 if (qlog)
397 fclose(qlog);
398 else
399 queue_rotate = 0;
400 qlog = NULL;
402 mkdir((char *)ast_config_AST_LOG_DIR, 0755);
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);
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));
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;
426 /* do it */
427 if (rename(old,new))
428 fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
431 f = f->next;
434 filesize_reload_needed = 0;
436 init_logger_chain();
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;
450 /* do it */
451 if (rename(old,new))
452 ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new);
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;
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;
478 /* do it */
479 if (rename(old, new))
480 ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new);
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;
494 ast_mutex_unlock(&loglock);
495 ast_mutex_unlock(&msglist_lock);
497 return res;
500 static int handle_logger_reload(int fd, int argc, char *argv[])
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;
509 static int handle_logger_rotate(int fd, int argc, char *argv[])
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;
518 /*! \brief CLI command to show logging system configuration */
519 static int handle_logger_show_channels(int fd, int argc, char *argv[])
521 #define FORMATL "%-35.35s %-8.8s %-9.9s "
522 struct logchannel *chan;
524 ast_mutex_lock(&loglock);
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;
552 ast_cli(fd, "\n");
554 ast_mutex_unlock(&loglock);
556 return RESULT_SUCCESS;
559 static struct verb {
560 void (*verboser)(const char *string, int opos, int replacelast, int complete);
561 struct verb *next;
562 } *verboser = NULL;
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";
569 static char logger_rotate_help[] =
570 "Usage: logger rotate\n"
571 " Rotates and Reopens the log files.\n";
573 static char logger_show_channels_help[] =
574 "Usage: logger show channels\n"
575 " Show configured logger channels.\n";
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 };
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 };
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 };
592 static int handle_SIGXFSZ(int sig)
594 /* Indicate need to reload */
595 filesize_reload_needed = 1;
596 return 0;
599 int init_logger(void)
601 char tmp[256];
602 int res = 0;
604 /* auto rotate if sig SIGXFSZ comes a-knockin */
605 (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ);
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);
612 mkdir((char *)ast_config_AST_LOG_DIR, 0755);
614 /* create log channels */
615 init_logger_chain();
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;
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", "");
637 return res;
640 void close_logger(void)
642 struct msglist *m, *tmp;
644 ast_mutex_lock(&msglist_lock);
645 m = list;
646 while(m) {
647 if (m->msg) {
648 free(m->msg);
650 tmp = m->next;
651 free(m);
652 m = tmp;
654 list = last = NULL;
655 msgcnt = 0;
656 ast_mutex_unlock(&msglist_lock);
657 return;
660 static void ast_log_vsyslog(int level, const char *file, int line, const char *function, const char *fmt, va_list args)
662 char buf[BUFSIZ];
663 char *s;
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;
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);
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);
687 * send log messages to syslog and/or the console
689 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
691 struct logchannel *chan;
692 char buf[BUFSIZ];
693 time_t t;
694 struct tm tm;
695 char date[256];
697 va_list ap;
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)
705 if (!option_verbose && !option_debug && (level == __LOG_DEBUG)) {
706 return;
709 /* Ignore anything that never gets logged anywhere */
710 if (!(global_logmask & (1 << level)))
711 return;
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;
717 /* begin critical section */
718 ast_mutex_lock(&loglock);
720 time(&t);
721 localtime_r(&t, &tm);
722 strftime(date, sizeof(date), dateformat, &tm);
724 if (logfiles.event_log && level == __LOG_EVENT) {
725 va_start(ap, fmt);
727 fprintf(eventlog, "%s asterisk[%ld]: ", date, (long)getpid());
728 vfprintf(eventlog, fmt, ap);
729 fflush(eventlog);
731 va_end(ap);
732 ast_mutex_unlock(&loglock);
733 return;
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];
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)));
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);
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);
789 chan = chan->next;
791 } else {
793 * we don't have the logger chain configured yet,
794 * so just log to stdout
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);
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");
814 void ast_backtrace(void)
816 #ifdef STACK_BACKTRACES
817 int count=0, i=0;
818 void **addresses;
819 char **strings;
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]);
828 free(strings);
829 } else {
830 ast_log(LOG_DEBUG, "Could not allocate memory for backtrace\n");
832 free(addresses);
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
843 void ast_verbose(const char *fmt, ...)
845 static char stuff[4096];
846 static int len = 0;
847 static int replacelast = 0;
849 int complete;
850 int olen;
851 struct msglist *m;
852 struct verb *v;
854 va_list ap;
855 va_start(ap, fmt);
857 if (ast_opt_timestamp) {
858 time_t t;
859 struct tm tm;
860 char date[40];
861 char *datefmt;
863 time(&t);
864 localtime_r(&t, &tm);
865 strftime(date, sizeof(date), dateformat, &tm);
866 datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1);
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
873 ast_mutex_lock(&msglist_lock);
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
880 vsnprintf(stuff + len, sizeof(stuff) - len, fmt, ap);
881 va_end(ap);
883 olen = len;
884 len = strlen(stuff);
886 complete = (stuff[len - 1] == '\n') ? 1 : 0;
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;
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);
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);
920 for (v = verboser; v; v = v->next)
921 v->verboser(stuff, olen, replacelast, complete);
923 ast_log(LOG_VERBOSE, "%s", stuff);
925 if (len) {
926 if (!complete)
927 replacelast = 1;
928 else
929 replacelast = len = 0;
932 ast_mutex_unlock(&msglist_lock);
935 int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete))
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;
945 ast_mutex_unlock(&msglist_lock);
946 return 0;
949 int ast_register_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
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;
965 ast_mutex_unlock(&msglist_lock);
966 return 0;
968 return -1;
971 int ast_unregister_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
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;
986 tmpl = tmp;
987 tmp = tmp->next;
989 if (tmp)
990 res = 0;
991 ast_mutex_unlock(&msglist_lock);
992 return res;