remove rule for running bootstrap, it's only safe to run it manually now
[asterisk-bristuff.git] / logger.c
blobff3513634a7e3c68718855419cebb32c85864d8f
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 <signal.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <time.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <errno.h>
36 #include <sys/stat.h>
37 #ifdef STACK_BACKTRACES
38 #include <execinfo.h>
39 #define MAX_BACKTRACE_FRAMES 20
40 #endif
42 #define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values,
43 from <syslog.h> which is included by logger.h */
44 #include <syslog.h>
46 #include "asterisk.h"
48 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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(&loglock);
389 if (eventlog)
390 fclose(eventlog);
391 else
392 event_rotate = 0;
393 eventlog = NULL;
395 if (qlog)
396 fclose(qlog);
397 else
398 queue_rotate = 0;
399 qlog = NULL;
401 mkdir((char *)ast_config_AST_LOG_DIR, 0755);
403 f = logchannels;
404 while(f) {
405 if (f->disabled) {
406 f->disabled = 0; /* Re-enable logging at reload */
407 manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
409 if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
410 fclose(f->fileptr); /* Close file */
411 f->fileptr = NULL;
412 if(rotate) {
413 ast_copy_string(old, f->filename, sizeof(old));
415 for(x=0;;x++) {
416 snprintf(new, sizeof(new), "%s.%d", f->filename, x);
417 myf = fopen((char *)new, "r");
418 if (myf) {
419 fclose(myf);
420 } else {
421 break;
425 /* do it */
426 if (rename(old,new))
427 fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
430 f = f->next;
433 filesize_reload_needed = 0;
435 init_logger_chain();
437 if (logfiles.event_log) {
438 snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
439 if (event_rotate) {
440 for (x=0;;x++) {
441 snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, EVENTLOG,x);
442 myf = fopen((char *)new, "r");
443 if (myf) /* File exists */
444 fclose(myf);
445 else
446 break;
449 /* do it */
450 if (rename(old,new))
451 ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new);
454 eventlog = fopen(old, "a");
455 if (eventlog) {
456 ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
457 if (option_verbose)
458 ast_verbose("Asterisk Event Logger restarted\n");
459 } else {
460 ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
461 res = -1;
465 if (logfiles.queue_log) {
466 snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, QUEUELOG);
467 if (queue_rotate) {
468 for (x = 0; ; x++) {
469 snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, QUEUELOG, x);
470 myf = fopen((char *)new, "r");
471 if (myf) /* File exists */
472 fclose(myf);
473 else
474 break;
477 /* do it */
478 if (rename(old, new))
479 ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new);
482 qlog = fopen(old, "a");
483 if (qlog) {
484 ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
485 ast_log(LOG_EVENT, "Restarted Asterisk Queue Logger\n");
486 if (option_verbose)
487 ast_verbose("Asterisk Queue Logger restarted\n");
488 } else {
489 ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
490 res = -1;
493 ast_mutex_unlock(&loglock);
495 return res;
498 static int handle_logger_reload(int fd, int argc, char *argv[])
500 if(reload_logger(0)) {
501 ast_cli(fd, "Failed to reload the logger\n");
502 return RESULT_FAILURE;
503 } else
504 return RESULT_SUCCESS;
507 static int handle_logger_rotate(int fd, int argc, char *argv[])
509 if(reload_logger(1)) {
510 ast_cli(fd, "Failed to reload the logger and rotate log files\n");
511 return RESULT_FAILURE;
512 } else
513 return RESULT_SUCCESS;
516 /*! \brief CLI command to show logging system configuration */
517 static int handle_logger_show_channels(int fd, int argc, char *argv[])
519 #define FORMATL "%-35.35s %-8.8s %-9.9s "
520 struct logchannel *chan;
522 ast_mutex_lock(&loglock);
524 chan = logchannels;
525 ast_cli(fd,FORMATL, "Channel", "Type", "Status");
526 ast_cli(fd, "Configuration\n");
527 ast_cli(fd,FORMATL, "-------", "----", "------");
528 ast_cli(fd, "-------------\n");
529 while (chan) {
530 ast_cli(fd, FORMATL, chan->filename, chan->type==LOGTYPE_CONSOLE ? "Console" : (chan->type==LOGTYPE_SYSLOG ? "Syslog" : "File"),
531 chan->disabled ? "Disabled" : "Enabled");
532 ast_cli(fd, " - ");
533 if (chan->logmask & (1 << __LOG_DEBUG))
534 ast_cli(fd, "Debug ");
535 if (chan->logmask & (1 << __LOG_DTMF))
536 ast_cli(fd, "DTMF ");
537 if (chan->logmask & (1 << __LOG_VERBOSE))
538 ast_cli(fd, "Verbose ");
539 if (chan->logmask & (1 << __LOG_WARNING))
540 ast_cli(fd, "Warning ");
541 if (chan->logmask & (1 << __LOG_NOTICE))
542 ast_cli(fd, "Notice ");
543 if (chan->logmask & (1 << __LOG_ERROR))
544 ast_cli(fd, "Error ");
545 if (chan->logmask & (1 << __LOG_EVENT))
546 ast_cli(fd, "Event ");
547 ast_cli(fd, "\n");
548 chan = chan->next;
550 ast_cli(fd, "\n");
552 ast_mutex_unlock(&loglock);
554 return RESULT_SUCCESS;
557 static struct verb {
558 void (*verboser)(const char *string, int opos, int replacelast, int complete);
559 struct verb *next;
560 } *verboser = NULL;
563 static char logger_reload_help[] =
564 "Usage: logger reload\n"
565 " Reloads the logger subsystem state. Use after restarting syslogd(8) if you are using syslog logging.\n";
567 static char logger_rotate_help[] =
568 "Usage: logger rotate\n"
569 " Rotates and Reopens the log files.\n";
571 static char logger_show_channels_help[] =
572 "Usage: logger show channels\n"
573 " Show configured logger channels.\n";
575 static struct ast_cli_entry logger_show_channels_cli =
576 { { "logger", "show", "channels", NULL },
577 handle_logger_show_channels, "List configured log channels",
578 logger_show_channels_help };
580 static struct ast_cli_entry reload_logger_cli =
581 { { "logger", "reload", NULL },
582 handle_logger_reload, "Reopens the log files",
583 logger_reload_help };
585 static struct ast_cli_entry rotate_logger_cli =
586 { { "logger", "rotate", NULL },
587 handle_logger_rotate, "Rotates and reopens the log files",
588 logger_rotate_help };
590 static int handle_SIGXFSZ(int sig)
592 /* Indicate need to reload */
593 filesize_reload_needed = 1;
594 return 0;
597 int init_logger(void)
599 char tmp[256];
600 int res = 0;
602 /* auto rotate if sig SIGXFSZ comes a-knockin */
603 (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ);
605 /* register the relaod logger cli command */
606 ast_cli_register(&reload_logger_cli);
607 ast_cli_register(&rotate_logger_cli);
608 ast_cli_register(&logger_show_channels_cli);
610 mkdir((char *)ast_config_AST_LOG_DIR, 0755);
612 /* create log channels */
613 init_logger_chain();
615 /* create the eventlog */
616 if (logfiles.event_log) {
617 mkdir((char *)ast_config_AST_LOG_DIR, 0755);
618 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
619 eventlog = fopen((char *)tmp, "a");
620 if (eventlog) {
621 ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
622 if (option_verbose)
623 ast_verbose("Asterisk Event Logger Started %s\n",(char *)tmp);
624 } else {
625 ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
626 res = -1;
630 if (logfiles.queue_log) {
631 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, QUEUELOG);
632 qlog = fopen(tmp, "a");
633 ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
635 return res;
638 void close_logger(void)
640 struct msglist *m, *tmp;
642 ast_mutex_lock(&msglist_lock);
643 m = list;
644 while(m) {
645 if (m->msg) {
646 free(m->msg);
648 tmp = m->next;
649 free(m);
650 m = tmp;
652 list = last = NULL;
653 msgcnt = 0;
654 ast_mutex_unlock(&msglist_lock);
655 return;
658 static void ast_log_vsyslog(int level, const char *file, int line, const char *function, const char *fmt, va_list args)
660 char buf[BUFSIZ];
661 char *s;
663 if (level >= SYSLOG_NLEVELS) {
664 /* we are locked here, so cannot ast_log() */
665 fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level);
666 return;
668 if (level == __LOG_VERBOSE) {
669 snprintf(buf, sizeof(buf), "VERBOSE[%ld]: ", (long)GETTID());
670 level = __LOG_DEBUG;
671 } else if (level == __LOG_DTMF) {
672 snprintf(buf, sizeof(buf), "DTMF[%ld]: ", (long)GETTID());
673 level = __LOG_DEBUG;
674 } else {
675 snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: ",
676 levels[level], (long)GETTID(), file, line, function);
678 s = buf + strlen(buf);
679 vsnprintf(s, sizeof(buf) - strlen(buf), fmt, args);
680 term_strip(s, s, strlen(s) + 1);
681 syslog(syslog_level_map[level], "%s", buf);
685 * send log messages to syslog and/or the console
687 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
689 struct logchannel *chan;
690 char buf[BUFSIZ];
691 time_t t;
692 struct tm tm;
693 char date[256];
695 va_list ap;
697 /* don't display LOG_DEBUG messages unless option_verbose _or_ option_debug
698 are non-zero; LOG_DEBUG messages can still be displayed if option_debug
699 is zero, if option_verbose is non-zero (this allows for 'level zero'
700 LOG_DEBUG messages to be displayed, if the logmask on any channel
701 allows it)
703 if (!option_verbose && !option_debug && (level == __LOG_DEBUG)) {
704 return;
707 /* Ignore anything that never gets logged anywhere */
708 if (!(global_logmask & (1 << level)))
709 return;
711 /* Ignore anything other than the currently debugged file if there is one */
712 if ((level == __LOG_DEBUG) && !ast_strlen_zero(debug_filename) && strcasecmp(debug_filename, file))
713 return;
715 /* begin critical section */
716 ast_mutex_lock(&loglock);
718 time(&t);
719 localtime_r(&t, &tm);
720 strftime(date, sizeof(date), dateformat, &tm);
722 if (logfiles.event_log && level == __LOG_EVENT) {
723 va_start(ap, fmt);
725 fprintf(eventlog, "%s asterisk[%ld]: ", date, (long)getpid());
726 vfprintf(eventlog, fmt, ap);
727 fflush(eventlog);
729 va_end(ap);
730 ast_mutex_unlock(&loglock);
731 return;
734 if (logchannels) {
735 chan = logchannels;
736 while(chan && !chan->disabled) {
737 /* Check syslog channels */
738 if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << level))) {
739 va_start(ap, fmt);
740 ast_log_vsyslog(level, file, line, function, fmt, ap);
741 va_end(ap);
742 /* Console channels */
743 } else if ((chan->logmask & (1 << level)) && (chan->type == LOGTYPE_CONSOLE)) {
744 char linestr[128];
745 char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
747 if (level != __LOG_VERBOSE) {
748 sprintf(linestr, "%d", line);
749 snprintf(buf, sizeof(buf), ast_opt_timestamp ? "[%s] %s[%ld]: %s:%s %s: " : "%s %s[%ld]: %s:%s %s: ",
750 date,
751 term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)),
752 (long)GETTID(),
753 term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
754 term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
755 term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
757 ast_console_puts(buf);
758 va_start(ap, fmt);
759 vsnprintf(buf, sizeof(buf), fmt, ap);
760 va_end(ap);
761 ast_console_puts(buf);
763 /* File channels */
764 } else if ((chan->logmask & (1 << level)) && (chan->fileptr)) {
765 int res;
766 snprintf(buf, sizeof(buf), ast_opt_timestamp ? "[%s] %s[%ld]: " : "%s %s[%ld] %s: ", date,
767 levels[level], (long)GETTID(), file);
768 res = fprintf(chan->fileptr, buf);
769 if (res <= 0 && buf[0] != '\0') { /* Error, no characters printed */
770 fprintf(stderr,"**** Asterisk Logging Error: ***********\n");
771 if (errno == ENOMEM || errno == ENOSPC) {
772 fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename);
773 } else
774 fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno));
775 manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno));
776 chan->disabled = 1;
777 } else {
778 /* No error message, continue printing */
779 va_start(ap, fmt);
780 vsnprintf(buf, sizeof(buf), fmt, ap);
781 va_end(ap);
782 term_strip(buf, buf, sizeof(buf));
783 fputs(buf, chan->fileptr);
784 fflush(chan->fileptr);
787 chan = chan->next;
789 } else {
791 * we don't have the logger chain configured yet,
792 * so just log to stdout
794 if (level != __LOG_VERBOSE) {
795 va_start(ap, fmt);
796 vsnprintf(buf, sizeof(buf), fmt, ap);
797 va_end(ap);
798 fputs(buf, stdout);
802 ast_mutex_unlock(&loglock);
803 /* end critical section */
804 if (filesize_reload_needed) {
805 reload_logger(1);
806 ast_log(LOG_EVENT,"Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
807 if (option_verbose)
808 ast_verbose("Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
812 void ast_backtrace(void)
814 #ifdef STACK_BACKTRACES
815 int count=0, i=0;
816 void **addresses;
817 char **strings;
819 if ((addresses = ast_calloc(MAX_BACKTRACE_FRAMES, sizeof(*addresses)))) {
820 count = backtrace(addresses, MAX_BACKTRACE_FRAMES);
821 if ((strings = backtrace_symbols(addresses, count))) {
822 ast_log(LOG_DEBUG, "Got %d backtrace record%c\n", count, count != 1 ? 's' : ' ');
823 for (i=0; i < count ; i++) {
824 ast_log(LOG_DEBUG, "#%d: [%08X] %s\n", i, (unsigned int)addresses[i], strings[i]);
826 free(strings);
827 } else {
828 ast_log(LOG_DEBUG, "Could not allocate memory for backtrace\n");
830 free(addresses);
832 #else
833 #ifdef Linux
834 ast_log(LOG_WARNING, "Must compile with 'make dont-optimize' for stack backtraces\n");
835 #else
836 ast_log(LOG_WARNING, "Inline stack backtraces are only available on the Linux platform.\n");
837 #endif
838 #endif
841 void ast_verbose(const char *fmt, ...)
843 static char stuff[4096];
844 static int len = 0;
845 static int replacelast = 0;
847 int complete;
848 int olen;
849 struct msglist *m;
850 struct verb *v;
852 va_list ap;
853 va_start(ap, fmt);
855 if (ast_opt_timestamp) {
856 time_t t;
857 struct tm tm;
858 char date[40];
859 char *datefmt;
861 time(&t);
862 localtime_r(&t, &tm);
863 strftime(date, sizeof(date), dateformat, &tm);
864 if ((datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1))) {
865 sprintf(datefmt, "[%s] %s", date, fmt);
866 fmt = datefmt;
870 /* this lock is also protecting against multiple threads
871 being in this function at the same time, so it must be
872 held before any of the static variables are accessed
874 ast_mutex_lock(&msglist_lock);
876 /* there is a potential security problem here: if formatting
877 the current date using 'dateformat' results in a string
878 containing '%', then the vsnprintf() call below will
879 probably try to access random memory
881 vsnprintf(stuff + len, sizeof(stuff) - len, fmt, ap);
882 va_end(ap);
884 olen = len;
885 len = strlen(stuff);
887 complete = (stuff[len - 1] == '\n') ? 1 : 0;
889 /* If we filled up the stuff completely, then log it even without the '\n' */
890 if (len >= sizeof(stuff) - 1) {
891 complete = 1;
892 len = 0;
895 if (complete) {
896 if (msgcnt < MAX_MSG_QUEUE) {
897 /* Allocate new structure */
898 if ((m = ast_malloc(sizeof(*m))))
899 msgcnt++;
900 } else {
901 /* Recycle the oldest entry */
902 m = list;
903 list = list->next;
904 free(m->msg);
906 if (m) {
907 if ((m->msg = ast_strdup(stuff))) {
908 if (last)
909 last->next = m;
910 else
911 list = m;
912 m->next = NULL;
913 last = m;
914 } else {
915 msgcnt--;
916 free(m);
921 for (v = verboser; v; v = v->next)
922 v->verboser(stuff, olen, replacelast, complete);
924 ast_log(LOG_VERBOSE, "%s", stuff);
926 if (len) {
927 if (!complete)
928 replacelast = 1;
929 else
930 replacelast = len = 0;
933 ast_mutex_unlock(&msglist_lock);
936 int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete))
938 struct msglist *m;
939 ast_mutex_lock(&msglist_lock);
940 m = list;
941 while(m) {
942 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
943 v(m->msg, 0, 0, 1);
944 m = m->next;
946 ast_mutex_unlock(&msglist_lock);
947 return 0;
950 int ast_register_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
952 struct msglist *m;
953 struct verb *tmp;
954 /* XXX Should be more flexible here, taking > 1 verboser XXX */
955 if ((tmp = ast_malloc(sizeof(*tmp)))) {
956 tmp->verboser = v;
957 ast_mutex_lock(&msglist_lock);
958 tmp->next = verboser;
959 verboser = tmp;
960 m = list;
961 while(m) {
962 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
963 v(m->msg, 0, 0, 1);
964 m = m->next;
966 ast_mutex_unlock(&msglist_lock);
967 return 0;
969 return -1;
972 int ast_unregister_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
974 int res = -1;
975 struct verb *tmp, *tmpl=NULL;
976 ast_mutex_lock(&msglist_lock);
977 tmp = verboser;
978 while(tmp) {
979 if (tmp->verboser == v) {
980 if (tmpl)
981 tmpl->next = tmp->next;
982 else
983 verboser = tmp->next;
984 free(tmp);
985 break;
987 tmpl = tmp;
988 tmp = tmp->next;
990 if (tmp)
991 res = 0;
992 ast_mutex_unlock(&msglist_lock);
993 return res;