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>
30 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
41 #ifdef STACK_BACKTRACES
43 #define MAX_BACKTRACE_FRAMES 20
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 */
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
[PATH_MAX
] = "";
383 int event_rotate
= rotate
, queue_rotate
= rotate
;
384 struct logchannel
*f
;
388 ast_mutex_lock(&msglist_lock
); /* to avoid deadlock */
389 ast_mutex_lock(&loglock
);
402 mkdir((char *)ast_config_AST_LOG_DIR
, 0755);
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 */
414 ast_copy_string(old
, f
->filename
, sizeof(old
));
417 snprintf(new, sizeof(new), "%s.%d", f
->filename
, x
);
418 myf
= fopen((char *)new, "r");
428 fprintf(stderr
, "Unable to rename file '%s' to '%s'\n", old
, new);
434 filesize_reload_needed
= 0;
438 if (logfiles
.event_log
) {
439 snprintf(old
, sizeof(old
), "%s/%s", (char *)ast_config_AST_LOG_DIR
, EVENTLOG
);
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 */
452 ast_log(LOG_ERROR
, "Unable to rename file '%s' to '%s'\n", old
, new);
455 eventlog
= fopen(old
, "a");
457 ast_log(LOG_EVENT
, "Restarted Asterisk Event Logger\n");
459 ast_verbose("Asterisk Event Logger restarted\n");
461 ast_log(LOG_ERROR
, "Unable to create event log: %s\n", strerror(errno
));
466 if (logfiles
.queue_log
) {
467 snprintf(old
, sizeof(old
), "%s/%s", (char *)ast_config_AST_LOG_DIR
, QUEUELOG
);
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 */
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");
485 ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
486 ast_log(LOG_EVENT
, "Restarted Asterisk Queue Logger\n");
488 ast_verbose("Asterisk Queue Logger restarted\n");
490 ast_log(LOG_ERROR
, "Unable to create queue log: %s\n", strerror(errno
));
494 ast_mutex_unlock(&loglock
);
495 ast_mutex_unlock(&msglist_lock
);
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
;
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
;
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
);
527 ast_cli(fd
,FORMATL
, "Channel", "Type", "Status");
528 ast_cli(fd
, "Configuration\n");
529 ast_cli(fd
,FORMATL
, "-------", "----", "------");
530 ast_cli(fd
, "-------------\n");
532 ast_cli(fd
, FORMATL
, chan
->filename
, chan
->type
==LOGTYPE_CONSOLE
? "Console" : (chan
->type
==LOGTYPE_SYSLOG
? "Syslog" : "File"),
533 chan
->disabled
? "Disabled" : "Enabled");
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 ");
554 ast_mutex_unlock(&loglock
);
556 return RESULT_SUCCESS
;
560 void (*verboser
)(const char *string
, int opos
, int replacelast
, int complete
);
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;
599 int init_logger(void)
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 */
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");
623 ast_log(LOG_EVENT
, "Started Asterisk Event Logger\n");
625 ast_verbose("Asterisk Event Logger Started %s\n",(char *)tmp
);
627 ast_log(LOG_ERROR
, "Unable to create event log: %s\n", strerror(errno
));
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", "");
640 void close_logger(void)
642 struct msglist
*m
, *tmp
;
644 ast_mutex_lock(&msglist_lock
);
656 ast_mutex_unlock(&msglist_lock
);
660 static void ast_log_vsyslog(int level
, const char *file
, int line
, const char *function
, const char *fmt
, va_list args
)
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
);
670 if (level
== __LOG_VERBOSE
) {
671 snprintf(buf
, sizeof(buf
), "VERBOSE[%ld]: ", (long)GETTID());
673 } else if (level
== __LOG_DTMF
) {
674 snprintf(buf
, sizeof(buf
), "DTMF[%ld]: ", (long)GETTID());
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
;
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
705 if (!option_verbose
&& !option_debug
&& (level
== __LOG_DEBUG
)) {
709 /* Ignore anything that never gets logged anywhere */
710 if (!(global_logmask
& (1 << level
)))
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
))
717 /* begin critical section */
718 ast_mutex_lock(&loglock
);
721 localtime_r(&t
, &tm
);
722 strftime(date
, sizeof(date
), dateformat
, &tm
);
724 if (logfiles
.event_log
&& level
== __LOG_EVENT
) {
727 fprintf(eventlog
, "%s asterisk[%ld]: ", date
, (long)getpid());
728 vfprintf(eventlog
, fmt
, ap
);
732 ast_mutex_unlock(&loglock
);
738 while(chan
&& !chan
->disabled
) {
739 /* Check syslog channels */
740 if (chan
->type
== LOGTYPE_SYSLOG
&& (chan
->logmask
& (1 << level
))) {
742 ast_log_vsyslog(level
, file
, line
, function
, fmt
, ap
);
744 /* Console channels */
745 } else if ((chan
->logmask
& (1 << level
)) && (chan
->type
== LOGTYPE_CONSOLE
)) {
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: ",
753 term_color(tmp1
, levels
[level
], colors
[level
], 0, sizeof(tmp1
)),
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
);
761 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
763 ast_console_puts_mutable(buf
);
766 } else if ((chan
->logmask
& (1 << level
)) && (chan
->fileptr
)) {
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
);
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
));
780 /* No error message, continue printing */
782 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
784 term_strip(buf
, buf
, sizeof(buf
));
785 fputs(buf
, chan
->fileptr
);
786 fflush(chan
->fileptr
);
793 * we don't have the logger chain configured yet,
794 * so just log to stdout
796 if (level
!= __LOG_VERBOSE
) {
798 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
804 ast_mutex_unlock(&loglock
);
805 /* end critical section */
806 if (filesize_reload_needed
) {
808 ast_log(LOG_EVENT
,"Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
810 ast_verbose("Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
814 void ast_backtrace(void)
816 #ifdef STACK_BACKTRACES
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
]);
830 ast_log(LOG_DEBUG
, "Could not allocate memory for backtrace\n");
836 ast_log(LOG_WARNING
, "Must compile with 'make dont-optimize' for stack backtraces\n");
838 ast_log(LOG_WARNING
, "Inline stack backtraces are only available on the Linux platform.\n");
843 void ast_verbose(const char *fmt
, ...)
845 static char stuff
[4096];
847 static int replacelast
= 0;
857 if (ast_opt_timestamp
) {
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
);
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) {
895 if (msgcnt
< MAX_MSG_QUEUE
) {
896 /* Allocate new structure */
897 if ((m
= ast_malloc(sizeof(*m
))))
900 /* Recycle the oldest entry */
906 if ((m
->msg
= ast_strdup(stuff
))) {
920 for (v
= verboser
; v
; v
= v
->next
)
921 v
->verboser(stuff
, olen
, replacelast
, complete
);
923 ast_log(LOG_VERBOSE
, "%s", stuff
);
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
))
938 ast_mutex_lock(&msglist_lock
);
941 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
945 ast_mutex_unlock(&msglist_lock
);
949 int ast_register_verbose(void (*v
)(const char *string
, int opos
, int replacelast
, int complete
))
953 /* XXX Should be more flexible here, taking > 1 verboser XXX */
954 if ((tmp
= ast_malloc(sizeof(*tmp
)))) {
956 ast_mutex_lock(&msglist_lock
);
957 tmp
->next
= verboser
;
961 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
965 ast_mutex_unlock(&msglist_lock
);
971 int ast_unregister_verbose(void (*v
)(const char *string
, int opos
, int replacelast
, int complete
))
974 struct verb
*tmp
, *tmpl
=NULL
;
975 ast_mutex_lock(&msglist_lock
);
978 if (tmp
->verboser
== v
) {
980 tmpl
->next
= tmp
->next
;
982 verboser
= tmp
->next
;
991 ast_mutex_unlock(&msglist_lock
);