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.
21 * \brief Asterisk Logger
25 * \author Mark Spencer <markster@digium.com>
37 #ifdef STACK_BACKTRACES
39 #define MAX_BACKTRACE_FRAMES 20
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 */
48 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
50 static int syslog_level_map
[] = {
52 LOG_INFO
, /* arbitrary equivalent of LOG_EVENT */
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>
78 #if defined(__linux__) && defined(__NR_gettid)
79 #define GETTID() syscall(__NR_gettid)
81 #define GETTID() getpid()
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;
93 unsigned int queue_log
:1;
94 unsigned int event_log
:1;
95 } logfiles
= { 1, 1 };
97 static struct msglist
{
100 } *list
= NULL
, *last
= NULL
;
102 static char hostname
[MAXHOSTNAMELEN
];
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
[] = {
137 static int colors
[] = {
147 static int make_components(char *s
, int lineno
)
153 w
= strsep(&stringp
, ",");
155 while(*w
&& (*w
< 33))
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
);
172 fprintf(stderr
, "Logfile Warning: Unknown keyword '%s' at line %d of logger.conf\n", w
, lineno
);
174 w
= strsep(&stringp
, ",");
179 static struct logchannel
*make_logchannel(char *channel
, char *components
, int lineno
)
181 struct logchannel
*chan
;
187 if (ast_strlen_zero(channel
) || !(chan
= ast_calloc(1, sizeof(*chan
))))
190 if (!strcasecmp(channel
, "console")) {
191 chan
->type
= LOGTYPE_CONSOLE
;
192 } else if (!strncasecmp(channel
, "syslog", 6)) {
195 * syslog.facility => level,level,level
197 facility
= strchr(channel
, '.');
198 if(!facility
++ || !facility
) {
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
208 cptr
= facilitynames
;
209 while (cptr
->c_name
) {
210 if (!strcasecmp(facility
, cptr
->c_name
)) {
211 chan
->facility
= cptr
->c_val
;
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
;
256 if (0 > chan
->facility
) {
257 fprintf(stderr
, "Logger Warning: bad syslog facility in logger.conf\n");
262 chan
->type
= LOGTYPE_SYSLOG
;
263 snprintf(chan
->filename
, sizeof(chan
->filename
), "%s", channel
);
264 openlog("asterisk", LOG_PID
, chan
->facility
);
266 if (channel
[0] == '/') {
267 if(!ast_strlen_zero(hostname
)) {
268 snprintf(chan
->filename
, sizeof(chan
->filename
) - 1,"%s.%s", channel
, hostname
);
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
);
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
);
290 static void init_logger_chain(void)
292 struct logchannel
*chan
, *cur
;
293 struct ast_config
*cfg
;
294 struct ast_variable
*var
;
297 /* delete our list of log channels */
298 ast_mutex_lock(&loglock
);
306 ast_mutex_unlock(&loglock
);
312 cfg
= ast_config_load("logger.conf");
314 /* If no config file, we're fine, set default options. */
316 fprintf(stderr
, "Unable to open logger.conf: %s\n", strerror(errno
));
317 if (!(chan
= ast_calloc(1, sizeof(*chan
))))
319 chan
->type
= LOGTYPE_CONSOLE
;
320 chan
->logmask
= 28; /*warning,notice,error */
321 chan
->next
= logchannels
;
323 global_logmask
|= chan
->logmask
;
327 ast_mutex_lock(&loglock
);
328 if ((s
= ast_variable_retrieve(cfg
, "general", "appendhostname"))) {
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");
338 if ((s
= ast_variable_retrieve(cfg
, "general", "dateformat"))) {
339 ast_copy_string(dateformat
, s
, sizeof(dateformat
));
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");
351 chan
= make_logchannel(var
->name
, var
->value
, var
->lineno
);
353 chan
->next
= logchannels
;
355 global_logmask
|= chan
->logmask
;
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
, ...)
367 ast_mutex_lock(&loglock
);
370 fprintf(qlog
, "%ld|%s|%s|%s|%s|", (long)time(NULL
), callid
, queuename
, agent
, event
);
371 vfprintf(qlog
, fmt
, ap
);
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
;
388 ast_mutex_lock(&loglock
);
401 mkdir((char *)ast_config_AST_LOG_DIR
, 0755);
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 */
413 ast_copy_string(old
, f
->filename
, sizeof(old
));
416 snprintf(new, sizeof(new), "%s.%d", f
->filename
, x
);
417 myf
= fopen((char *)new, "r");
427 fprintf(stderr
, "Unable to rename file '%s' to '%s'\n", old
, new);
433 filesize_reload_needed
= 0;
437 if (logfiles
.event_log
) {
438 snprintf(old
, sizeof(old
), "%s/%s", (char *)ast_config_AST_LOG_DIR
, EVENTLOG
);
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 */
451 ast_log(LOG_ERROR
, "Unable to rename file '%s' to '%s'\n", old
, new);
454 eventlog
= fopen(old
, "a");
456 ast_log(LOG_EVENT
, "Restarted Asterisk Event Logger\n");
458 ast_verbose("Asterisk Event Logger restarted\n");
460 ast_log(LOG_ERROR
, "Unable to create event log: %s\n", strerror(errno
));
465 if (logfiles
.queue_log
) {
466 snprintf(old
, sizeof(old
), "%s/%s", (char *)ast_config_AST_LOG_DIR
, QUEUELOG
);
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 */
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");
484 ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
485 ast_log(LOG_EVENT
, "Restarted Asterisk Queue Logger\n");
487 ast_verbose("Asterisk Queue Logger restarted\n");
489 ast_log(LOG_ERROR
, "Unable to create queue log: %s\n", strerror(errno
));
493 ast_mutex_unlock(&loglock
);
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
;
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
;
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
);
525 ast_cli(fd
,FORMATL
, "Channel", "Type", "Status");
526 ast_cli(fd
, "Configuration\n");
527 ast_cli(fd
,FORMATL
, "-------", "----", "------");
528 ast_cli(fd
, "-------------\n");
530 ast_cli(fd
, FORMATL
, chan
->filename
, chan
->type
==LOGTYPE_CONSOLE
? "Console" : (chan
->type
==LOGTYPE_SYSLOG
? "Syslog" : "File"),
531 chan
->disabled
? "Disabled" : "Enabled");
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 ");
552 ast_mutex_unlock(&loglock
);
554 return RESULT_SUCCESS
;
558 void (*verboser
)(const char *string
, int opos
, int replacelast
, int complete
);
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;
597 int init_logger(void)
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 */
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");
621 ast_log(LOG_EVENT
, "Started Asterisk Event Logger\n");
623 ast_verbose("Asterisk Event Logger Started %s\n",(char *)tmp
);
625 ast_log(LOG_ERROR
, "Unable to create event log: %s\n", strerror(errno
));
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", "");
638 void close_logger(void)
640 struct msglist
*m
, *tmp
;
642 ast_mutex_lock(&msglist_lock
);
654 ast_mutex_unlock(&msglist_lock
);
658 static void ast_log_vsyslog(int level
, const char *file
, int line
, const char *function
, const char *fmt
, va_list args
)
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
);
668 if (level
== __LOG_VERBOSE
) {
669 snprintf(buf
, sizeof(buf
), "VERBOSE[%ld]: ", (long)GETTID());
671 } else if (level
== __LOG_DTMF
) {
672 snprintf(buf
, sizeof(buf
), "DTMF[%ld]: ", (long)GETTID());
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
;
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
703 if (!option_verbose
&& !option_debug
&& (level
== __LOG_DEBUG
)) {
707 /* Ignore anything that never gets logged anywhere */
708 if (!(global_logmask
& (1 << level
)))
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
))
715 /* begin critical section */
716 ast_mutex_lock(&loglock
);
719 localtime_r(&t
, &tm
);
720 strftime(date
, sizeof(date
), dateformat
, &tm
);
722 if (logfiles
.event_log
&& level
== __LOG_EVENT
) {
725 fprintf(eventlog
, "%s asterisk[%ld]: ", date
, (long)getpid());
726 vfprintf(eventlog
, fmt
, ap
);
730 ast_mutex_unlock(&loglock
);
736 while(chan
&& !chan
->disabled
) {
737 /* Check syslog channels */
738 if (chan
->type
== LOGTYPE_SYSLOG
&& (chan
->logmask
& (1 << level
))) {
740 ast_log_vsyslog(level
, file
, line
, function
, fmt
, ap
);
742 /* Console channels */
743 } else if ((chan
->logmask
& (1 << level
)) && (chan
->type
== LOGTYPE_CONSOLE
)) {
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: ",
751 term_color(tmp1
, levels
[level
], colors
[level
], 0, sizeof(tmp1
)),
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
);
759 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
761 ast_console_puts(buf
);
764 } else if ((chan
->logmask
& (1 << level
)) && (chan
->fileptr
)) {
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
);
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
));
778 /* No error message, continue printing */
780 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
782 term_strip(buf
, buf
, sizeof(buf
));
783 fputs(buf
, chan
->fileptr
);
784 fflush(chan
->fileptr
);
791 * we don't have the logger chain configured yet,
792 * so just log to stdout
794 if (level
!= __LOG_VERBOSE
) {
796 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
802 ast_mutex_unlock(&loglock
);
803 /* end critical section */
804 if (filesize_reload_needed
) {
806 ast_log(LOG_EVENT
,"Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
808 ast_verbose("Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
812 void ast_backtrace(void)
814 #ifdef STACK_BACKTRACES
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
]);
828 ast_log(LOG_DEBUG
, "Could not allocate memory for backtrace\n");
834 ast_log(LOG_WARNING
, "Must compile with 'make dont-optimize' for stack backtraces\n");
836 ast_log(LOG_WARNING
, "Inline stack backtraces are only available on the Linux platform.\n");
841 void ast_verbose(const char *fmt
, ...)
843 static char stuff
[4096];
845 static int replacelast
= 0;
855 if (ast_opt_timestamp
) {
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
);
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
);
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) {
896 if (msgcnt
< MAX_MSG_QUEUE
) {
897 /* Allocate new structure */
898 if ((m
= ast_malloc(sizeof(*m
))))
901 /* Recycle the oldest entry */
907 if ((m
->msg
= ast_strdup(stuff
))) {
921 for (v
= verboser
; v
; v
= v
->next
)
922 v
->verboser(stuff
, olen
, replacelast
, complete
);
924 ast_log(LOG_VERBOSE
, "%s", stuff
);
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
))
939 ast_mutex_lock(&msglist_lock
);
942 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
946 ast_mutex_unlock(&msglist_lock
);
950 int ast_register_verbose(void (*v
)(const char *string
, int opos
, int replacelast
, int complete
))
954 /* XXX Should be more flexible here, taking > 1 verboser XXX */
955 if ((tmp
= ast_malloc(sizeof(*tmp
)))) {
957 ast_mutex_lock(&msglist_lock
);
958 tmp
->next
= verboser
;
962 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
966 ast_mutex_unlock(&msglist_lock
);
972 int ast_unregister_verbose(void (*v
)(const char *string
, int opos
, int replacelast
, int complete
))
975 struct verb
*tmp
, *tmpl
=NULL
;
976 ast_mutex_lock(&msglist_lock
);
979 if (tmp
->verboser
== v
) {
981 tmpl
->next
= tmp
->next
;
983 verboser
= tmp
->next
;
992 ast_mutex_unlock(&msglist_lock
);