![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
logger.c
Go to the documentation of this file.
00001 /* 00002 * Asterisk -- An open source telephony toolkit. 00003 * 00004 * Copyright (C) 1999 - 2006, Digium, Inc. 00005 * 00006 * Mark Spencer <markster@digium.com> 00007 * 00008 * See http://www.asterisk.org for more information about 00009 * the Asterisk project. Please do not directly contact 00010 * any of the maintainers of this project for assistance; 00011 * the project provides a web site, mailing lists and IRC 00012 * channels for your use. 00013 * 00014 * This program is free software, distributed under the terms of 00015 * the GNU General Public License Version 2. See the LICENSE file 00016 * at the top of the source tree. 00017 */ 00018 00019 /*! \file 00020 * 00021 * \brief Asterisk Logger 00022 * 00023 * Logging routines 00024 * 00025 * \author Mark Spencer <markster@digium.com> 00026 */ 00027 00028 #include "asterisk.h" 00029 00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 51499 $") 00031 00032 #include <signal.h> 00033 #include <stdarg.h> 00034 #include <stdio.h> 00035 #include <unistd.h> 00036 #include <time.h> 00037 #include <string.h> 00038 #include <stdlib.h> 00039 #include <errno.h> 00040 #include <sys/stat.h> 00041 #ifdef STACK_BACKTRACES 00042 #include <execinfo.h> 00043 #define MAX_BACKTRACE_FRAMES 20 00044 #endif 00045 00046 #define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values, 00047 from <syslog.h> which is included by logger.h */ 00048 #include <syslog.h> 00049 00050 static int syslog_level_map[] = { 00051 LOG_DEBUG, 00052 LOG_INFO, /* arbitrary equivalent of LOG_EVENT */ 00053 LOG_NOTICE, 00054 LOG_WARNING, 00055 LOG_ERR, 00056 LOG_DEBUG, 00057 LOG_DEBUG 00058 }; 00059 00060 #define SYSLOG_NLEVELS sizeof(syslog_level_map) / sizeof(int) 00061 00062 #include "asterisk/logger.h" 00063 #include "asterisk/lock.h" 00064 #include "asterisk/options.h" 00065 #include "asterisk/channel.h" 00066 #include "asterisk/config.h" 00067 #include "asterisk/term.h" 00068 #include "asterisk/cli.h" 00069 #include "asterisk/utils.h" 00070 #include "asterisk/manager.h" 00071 #include "asterisk/threadstorage.h" 00072 #include "asterisk/strings.h" 00073 00074 #if defined(__linux__) && !defined(__NR_gettid) 00075 #include <asm/unistd.h> 00076 #endif 00077 00078 #if defined(__linux__) && defined(__NR_gettid) 00079 #define GETTID() syscall(__NR_gettid) 00080 #else 00081 #define GETTID() getpid() 00082 #endif 00083 00084 00085 static char dateformat[256] = "%b %e %T"; /* Original Asterisk Format */ 00086 00087 static char queue_log_name[256] = QUEUELOG; 00088 00089 static int filesize_reload_needed; 00090 static int global_logmask = -1; 00091 static int rotatetimestamp; 00092 00093 static struct { 00094 unsigned int queue_log:1; 00095 unsigned int event_log:1; 00096 } logfiles = { 1, 1 }; 00097 00098 static char hostname[MAXHOSTNAMELEN]; 00099 00100 enum logtypes { 00101 LOGTYPE_SYSLOG, 00102 LOGTYPE_FILE, 00103 LOGTYPE_CONSOLE, 00104 }; 00105 00106 struct logchannel { 00107 int logmask; /* What to log to this channel */ 00108 int disabled; /* If this channel is disabled or not */ 00109 int facility; /* syslog facility */ 00110 enum logtypes type; /* Type of log channel */ 00111 FILE *fileptr; /* logfile logging file pointer */ 00112 char filename[256]; /* Filename */ 00113 AST_LIST_ENTRY(logchannel) list; 00114 }; 00115 00116 static AST_LIST_HEAD_STATIC(logchannels, logchannel); 00117 00118 enum logmsgtypes { 00119 LOGMSG_NORMAL = 0, 00120 LOGMSG_VERBOSE, 00121 }; 00122 00123 struct logmsg { 00124 enum logmsgtypes type; 00125 char date[256]; 00126 int level; 00127 const char *file; 00128 int line; 00129 const char *function; 00130 AST_LIST_ENTRY(logmsg) list; 00131 char str[0]; 00132 }; 00133 00134 static AST_LIST_HEAD_STATIC(logmsgs, logmsg); 00135 static pthread_t logthread = AST_PTHREADT_NULL; 00136 static ast_cond_t logcond; 00137 static int close_logger_thread; 00138 00139 static FILE *eventlog; 00140 static FILE *qlog; 00141 00142 static char *levels[] = { 00143 "DEBUG", 00144 "EVENT", 00145 "NOTICE", 00146 "WARNING", 00147 "ERROR", 00148 "VERBOSE", 00149 "DTMF" 00150 }; 00151 00152 static int colors[] = { 00153 COLOR_BRGREEN, 00154 COLOR_BRBLUE, 00155 COLOR_YELLOW, 00156 COLOR_BRRED, 00157 COLOR_RED, 00158 COLOR_GREEN, 00159 COLOR_BRGREEN 00160 }; 00161 00162 AST_THREADSTORAGE(verbose_buf); 00163 #define VERBOSE_BUF_INIT_SIZE 256 00164 00165 AST_THREADSTORAGE(log_buf); 00166 #define LOG_BUF_INIT_SIZE 256 00167 00168 static int make_components(char *s, int lineno) 00169 { 00170 char *w; 00171 int res = 0; 00172 char *stringp = s; 00173 00174 while ((w = strsep(&stringp, ","))) { 00175 w = ast_skip_blanks(w); 00176 if (!strcasecmp(w, "error")) 00177 res |= (1 << __LOG_ERROR); 00178 else if (!strcasecmp(w, "warning")) 00179 res |= (1 << __LOG_WARNING); 00180 else if (!strcasecmp(w, "notice")) 00181 res |= (1 << __LOG_NOTICE); 00182 else if (!strcasecmp(w, "event")) 00183 res |= (1 << __LOG_EVENT); 00184 else if (!strcasecmp(w, "debug")) 00185 res |= (1 << __LOG_DEBUG); 00186 else if (!strcasecmp(w, "verbose")) 00187 res |= (1 << __LOG_VERBOSE); 00188 else if (!strcasecmp(w, "dtmf")) 00189 res |= (1 << __LOG_DTMF); 00190 else { 00191 fprintf(stderr, "Logfile Warning: Unknown keyword '%s' at line %d of logger.conf\n", w, lineno); 00192 } 00193 } 00194 00195 return res; 00196 } 00197 00198 static struct logchannel *make_logchannel(char *channel, char *components, int lineno) 00199 { 00200 struct logchannel *chan; 00201 char *facility; 00202 #ifndef SOLARIS 00203 CODE *cptr; 00204 #endif 00205 00206 if (ast_strlen_zero(channel) || !(chan = ast_calloc(1, sizeof(*chan)))) 00207 return NULL; 00208 00209 if (!strcasecmp(channel, "console")) { 00210 chan->type = LOGTYPE_CONSOLE; 00211 } else if (!strncasecmp(channel, "syslog", 6)) { 00212 /* 00213 * syntax is: 00214 * syslog.facility => level,level,level 00215 */ 00216 facility = strchr(channel, '.'); 00217 if (!facility++ || !facility) { 00218 facility = "local0"; 00219 } 00220 00221 #ifndef SOLARIS 00222 /* 00223 * Walk through the list of facilitynames (defined in sys/syslog.h) 00224 * to see if we can find the one we have been given 00225 */ 00226 chan->facility = -1; 00227 cptr = facilitynames; 00228 while (cptr->c_name) { 00229 if (!strcasecmp(facility, cptr->c_name)) { 00230 chan->facility = cptr->c_val; 00231 break; 00232 } 00233 cptr++; 00234 } 00235 #else 00236 chan->facility = -1; 00237 if (!strcasecmp(facility, "kern")) 00238 chan->facility = LOG_KERN; 00239 else if (!strcasecmp(facility, "USER")) 00240 chan->facility = LOG_USER; 00241 else if (!strcasecmp(facility, "MAIL")) 00242 chan->facility = LOG_MAIL; 00243 else if (!strcasecmp(facility, "DAEMON")) 00244 chan->facility = LOG_DAEMON; 00245 else if (!strcasecmp(facility, "AUTH")) 00246 chan->facility = LOG_AUTH; 00247 else if (!strcasecmp(facility, "SYSLOG")) 00248 chan->facility = LOG_SYSLOG; 00249 else if (!strcasecmp(facility, "LPR")) 00250 chan->facility = LOG_LPR; 00251 else if (!strcasecmp(facility, "NEWS")) 00252 chan->facility = LOG_NEWS; 00253 else if (!strcasecmp(facility, "UUCP")) 00254 chan->facility = LOG_UUCP; 00255 else if (!strcasecmp(facility, "CRON")) 00256 chan->facility = LOG_CRON; 00257 else if (!strcasecmp(facility, "LOCAL0")) 00258 chan->facility = LOG_LOCAL0; 00259 else if (!strcasecmp(facility, "LOCAL1")) 00260 chan->facility = LOG_LOCAL1; 00261 else if (!strcasecmp(facility, "LOCAL2")) 00262 chan->facility = LOG_LOCAL2; 00263 else if (!strcasecmp(facility, "LOCAL3")) 00264 chan->facility = LOG_LOCAL3; 00265 else if (!strcasecmp(facility, "LOCAL4")) 00266 chan->facility = LOG_LOCAL4; 00267 else if (!strcasecmp(facility, "LOCAL5")) 00268 chan->facility = LOG_LOCAL5; 00269 else if (!strcasecmp(facility, "LOCAL6")) 00270 chan->facility = LOG_LOCAL6; 00271 else if (!strcasecmp(facility, "LOCAL7")) 00272 chan->facility = LOG_LOCAL7; 00273 #endif /* Solaris */ 00274 00275 if (0 > chan->facility) { 00276 fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n"); 00277 free(chan); 00278 return NULL; 00279 } 00280 00281 chan->type = LOGTYPE_SYSLOG; 00282 snprintf(chan->filename, sizeof(chan->filename), "%s", channel); 00283 openlog("asterisk", LOG_PID, chan->facility); 00284 } else { 00285 if (channel[0] == '/') { 00286 if (!ast_strlen_zero(hostname)) { 00287 snprintf(chan->filename, sizeof(chan->filename) - 1,"%s.%s", channel, hostname); 00288 } else { 00289 ast_copy_string(chan->filename, channel, sizeof(chan->filename)); 00290 } 00291 } 00292 00293 if (!ast_strlen_zero(hostname)) { 00294 snprintf(chan->filename, sizeof(chan->filename), "%s/%s.%s", ast_config_AST_LOG_DIR, channel, hostname); 00295 } else { 00296 snprintf(chan->filename, sizeof(chan->filename), "%s/%s", ast_config_AST_LOG_DIR, channel); 00297 } 00298 chan->fileptr = fopen(chan->filename, "a"); 00299 if (!chan->fileptr) { 00300 /* Can't log here, since we're called with a lock */ 00301 fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", chan->filename, strerror(errno)); 00302 } 00303 chan->type = LOGTYPE_FILE; 00304 } 00305 chan->logmask = make_components(components, lineno); 00306 return chan; 00307 } 00308 00309 static void init_logger_chain(void) 00310 { 00311 struct logchannel *chan; 00312 struct ast_config *cfg; 00313 struct ast_variable *var; 00314 const char *s; 00315 00316 /* delete our list of log channels */ 00317 AST_LIST_LOCK(&logchannels); 00318 while ((chan = AST_LIST_REMOVE_HEAD(&logchannels, list))) 00319 free(chan); 00320 AST_LIST_UNLOCK(&logchannels); 00321 00322 global_logmask = 0; 00323 errno = 0; 00324 /* close syslog */ 00325 closelog(); 00326 00327 cfg = ast_config_load("logger.conf"); 00328 00329 /* If no config file, we're fine, set default options. */ 00330 if (!cfg) { 00331 if (errno) 00332 fprintf(stderr, "Unable to open logger.conf: %s; default settings will be used.\n", strerror(errno)); 00333 else 00334 fprintf(stderr, "Errors detected in logger.conf: see above; default settings will be used.\n"); 00335 if (!(chan = ast_calloc(1, sizeof(*chan)))) 00336 return; 00337 chan->type = LOGTYPE_CONSOLE; 00338 chan->logmask = 28; /*warning,notice,error */ 00339 AST_LIST_LOCK(&logchannels); 00340 AST_LIST_INSERT_HEAD(&logchannels, chan, list); 00341 AST_LIST_UNLOCK(&logchannels); 00342 global_logmask |= chan->logmask; 00343 return; 00344 } 00345 00346 if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) { 00347 if (ast_true(s)) { 00348 if (gethostname(hostname, sizeof(hostname) - 1)) { 00349 ast_copy_string(hostname, "unknown", sizeof(hostname)); 00350 ast_log(LOG_WARNING, "What box has no hostname???\n"); 00351 } 00352 } else 00353 hostname[0] = '\0'; 00354 } else 00355 hostname[0] = '\0'; 00356 if ((s = ast_variable_retrieve(cfg, "general", "dateformat"))) 00357 ast_copy_string(dateformat, s, sizeof(dateformat)); 00358 else 00359 ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat)); 00360 if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) 00361 logfiles.queue_log = ast_true(s); 00362 if ((s = ast_variable_retrieve(cfg, "general", "event_log"))) 00363 logfiles.event_log = ast_true(s); 00364 if ((s = ast_variable_retrieve(cfg, "general", "queue_log_name"))) 00365 ast_copy_string(queue_log_name, s, sizeof(queue_log_name)); 00366 if ((s = ast_variable_retrieve(cfg, "general", "rotatetimestamp"))) 00367 rotatetimestamp = ast_true(s); 00368 00369 AST_LIST_LOCK(&logchannels); 00370 var = ast_variable_browse(cfg, "logfiles"); 00371 for (; var; var = var->next) { 00372 if (!(chan = make_logchannel(var->name, var->value, var->lineno))) 00373 continue; 00374 AST_LIST_INSERT_HEAD(&logchannels, chan, list); 00375 global_logmask |= chan->logmask; 00376 } 00377 AST_LIST_UNLOCK(&logchannels); 00378 00379 ast_config_destroy(cfg); 00380 } 00381 00382 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...) 00383 { 00384 va_list ap; 00385 AST_LIST_LOCK(&logchannels); 00386 if (qlog) { 00387 va_start(ap, fmt); 00388 fprintf(qlog, "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event); 00389 vfprintf(qlog, fmt, ap); 00390 fprintf(qlog, "\n"); 00391 va_end(ap); 00392 fflush(qlog); 00393 } 00394 AST_LIST_UNLOCK(&logchannels); 00395 } 00396 00397 int reload_logger(int rotate) 00398 { 00399 char old[PATH_MAX] = ""; 00400 char new[PATH_MAX]; 00401 int event_rotate = rotate, queue_rotate = rotate; 00402 struct logchannel *f; 00403 FILE *myf; 00404 int x, res = 0; 00405 00406 AST_LIST_LOCK(&logchannels); 00407 00408 if (eventlog) 00409 fclose(eventlog); 00410 else 00411 event_rotate = 0; 00412 eventlog = NULL; 00413 00414 if (qlog) 00415 fclose(qlog); 00416 else 00417 queue_rotate = 0; 00418 qlog = NULL; 00419 00420 mkdir(ast_config_AST_LOG_DIR, 0755); 00421 00422 AST_LIST_TRAVERSE(&logchannels, f, list) { 00423 if (f->disabled) { 00424 f->disabled = 0; /* Re-enable logging at reload */ 00425 manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename); 00426 } 00427 if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) { 00428 fclose(f->fileptr); /* Close file */ 00429 f->fileptr = NULL; 00430 if (rotate) { 00431 ast_copy_string(old, f->filename, sizeof(old)); 00432 00433 if (!rotatetimestamp) { 00434 for (x = 0; ; x++) { 00435 snprintf(new, sizeof(new), "%s.%d", f->filename, x); 00436 myf = fopen(new, "r"); 00437 if (myf) 00438 fclose(myf); 00439 else 00440 break; 00441 } 00442 } else 00443 snprintf(new, sizeof(new), "%s.%ld", f->filename, (long)time(NULL)); 00444 00445 /* do it */ 00446 if (rename(old,new)) 00447 fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new); 00448 } 00449 } 00450 } 00451 00452 filesize_reload_needed = 0; 00453 00454 init_logger_chain(); 00455 00456 if (logfiles.event_log) { 00457 snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, EVENTLOG); 00458 if (event_rotate) { 00459 if (!rotatetimestamp) { 00460 for (x=0;;x++) { 00461 snprintf(new, sizeof(new), "%s/%s.%d", ast_config_AST_LOG_DIR, EVENTLOG,x); 00462 myf = fopen(new, "r"); 00463 if (myf) /* File exists */ 00464 fclose(myf); 00465 else 00466 break; 00467 } 00468 } else 00469 snprintf(new, sizeof(new), "%s/%s.%ld", ast_config_AST_LOG_DIR, EVENTLOG,(long)time(NULL)); 00470 00471 /* do it */ 00472 if (rename(old,new)) 00473 ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new); 00474 } 00475 00476 eventlog = fopen(old, "a"); 00477 if (eventlog) { 00478 ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n"); 00479 if (option_verbose) 00480 ast_verbose("Asterisk Event Logger restarted\n"); 00481 } else { 00482 ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno)); 00483 res = -1; 00484 } 00485 } 00486 00487 if (logfiles.queue_log) { 00488 snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name); 00489 if (queue_rotate) { 00490 if (!rotatetimestamp) { 00491 for (x = 0; ; x++) { 00492 snprintf(new, sizeof(new), "%s/%s.%d", ast_config_AST_LOG_DIR, queue_log_name, x); 00493 myf = fopen(new, "r"); 00494 if (myf) /* File exists */ 00495 fclose(myf); 00496 else 00497 break; 00498 } 00499 00500 } else 00501 snprintf(new, sizeof(new), "%s/%s.%ld", ast_config_AST_LOG_DIR, queue_log_name,(long)time(NULL)); 00502 /* do it */ 00503 if (rename(old, new)) 00504 ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new); 00505 } 00506 00507 qlog = fopen(old, "a"); 00508 if (qlog) { 00509 ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", ""); 00510 ast_log(LOG_EVENT, "Restarted Asterisk Queue Logger\n"); 00511 if (option_verbose) 00512 ast_verbose("Asterisk Queue Logger restarted\n"); 00513 } else { 00514 ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno)); 00515 res = -1; 00516 } 00517 } 00518 00519 AST_LIST_UNLOCK(&logchannels); 00520 00521 return res; 00522 } 00523 00524 static int handle_logger_reload(int fd, int argc, char *argv[]) 00525 { 00526 if (reload_logger(0)) { 00527 ast_cli(fd, "Failed to reload the logger\n"); 00528 return RESULT_FAILURE; 00529 } else 00530 return RESULT_SUCCESS; 00531 } 00532 00533 static int handle_logger_rotate(int fd, int argc, char *argv[]) 00534 { 00535 if (reload_logger(1)) { 00536 ast_cli(fd, "Failed to reload the logger and rotate log files\n"); 00537 return RESULT_FAILURE; 00538 } else 00539 return RESULT_SUCCESS; 00540 } 00541 00542 /*! \brief CLI command to show logging system configuration */ 00543 static int handle_logger_show_channels(int fd, int argc, char *argv[]) 00544 { 00545 #define FORMATL "%-35.35s %-8.8s %-9.9s " 00546 struct logchannel *chan; 00547 00548 ast_cli(fd,FORMATL, "Channel", "Type", "Status"); 00549 ast_cli(fd, "Configuration\n"); 00550 ast_cli(fd,FORMATL, "-------", "----", "------"); 00551 ast_cli(fd, "-------------\n"); 00552 AST_LIST_LOCK(&logchannels); 00553 AST_LIST_TRAVERSE(&logchannels, chan, list) { 00554 ast_cli(fd, FORMATL, chan->filename, chan->type==LOGTYPE_CONSOLE ? "Console" : (chan->type==LOGTYPE_SYSLOG ? "Syslog" : "File"), 00555 chan->disabled ? "Disabled" : "Enabled"); 00556 ast_cli(fd, " - "); 00557 if (chan->logmask & (1 << __LOG_DEBUG)) 00558 ast_cli(fd, "Debug "); 00559 if (chan->logmask & (1 << __LOG_DTMF)) 00560 ast_cli(fd, "DTMF "); 00561 if (chan->logmask & (1 << __LOG_VERBOSE)) 00562 ast_cli(fd, "Verbose "); 00563 if (chan->logmask & (1 << __LOG_WARNING)) 00564 ast_cli(fd, "Warning "); 00565 if (chan->logmask & (1 << __LOG_NOTICE)) 00566 ast_cli(fd, "Notice "); 00567 if (chan->logmask & (1 << __LOG_ERROR)) 00568 ast_cli(fd, "Error "); 00569 if (chan->logmask & (1 << __LOG_EVENT)) 00570 ast_cli(fd, "Event "); 00571 ast_cli(fd, "\n"); 00572 } 00573 AST_LIST_UNLOCK(&logchannels); 00574 ast_cli(fd, "\n"); 00575 00576 return RESULT_SUCCESS; 00577 } 00578 00579 struct verb { 00580 void (*verboser)(const char *string); 00581 AST_LIST_ENTRY(verb) list; 00582 }; 00583 00584 static AST_LIST_HEAD_STATIC(verbosers, verb); 00585 00586 static char logger_reload_help[] = 00587 "Usage: logger reload\n" 00588 " Reloads the logger subsystem state. Use after restarting syslogd(8) if you are using syslog logging.\n"; 00589 00590 static char logger_rotate_help[] = 00591 "Usage: logger rotate\n" 00592 " Rotates and Reopens the log files.\n"; 00593 00594 static char logger_show_channels_help[] = 00595 "Usage: logger show channels\n" 00596 " List configured logger channels.\n"; 00597 00598 static struct ast_cli_entry cli_logger[] = { 00599 { { "logger", "show", "channels", NULL }, 00600 handle_logger_show_channels, "List configured log channels", 00601 logger_show_channels_help }, 00602 00603 { { "logger", "reload", NULL }, 00604 handle_logger_reload, "Reopens the log files", 00605 logger_reload_help }, 00606 00607 { { "logger", "rotate", NULL }, 00608 handle_logger_rotate, "Rotates and reopens the log files", 00609 logger_rotate_help }, 00610 }; 00611 00612 static int handle_SIGXFSZ(int sig) 00613 { 00614 /* Indicate need to reload */ 00615 filesize_reload_needed = 1; 00616 return 0; 00617 } 00618 00619 static void ast_log_vsyslog(int level, const char *file, int line, const char *function, char *str) 00620 { 00621 char buf[BUFSIZ]; 00622 00623 if (level >= SYSLOG_NLEVELS) { 00624 /* we are locked here, so cannot ast_log() */ 00625 fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level); 00626 return; 00627 } 00628 00629 if (level == __LOG_VERBOSE) { 00630 snprintf(buf, sizeof(buf), "VERBOSE[%ld]: %s", (long)GETTID(), str); 00631 level = __LOG_DEBUG; 00632 } else if (level == __LOG_DTMF) { 00633 snprintf(buf, sizeof(buf), "DTMF[%ld]: %s", (long)GETTID(), str); 00634 level = __LOG_DEBUG; 00635 } else { 00636 snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: %s", 00637 levels[level], (long)GETTID(), file, line, function, str); 00638 } 00639 00640 term_strip(buf, buf, strlen(buf) + 1); 00641 syslog(syslog_level_map[level], "%s", buf); 00642 } 00643 00644 /* Print a normal log message to the channels */ 00645 static void logger_print_normal(struct logmsg *logmsg) 00646 { 00647 struct logchannel *chan = NULL; 00648 char buf[BUFSIZ]; 00649 00650 AST_LIST_LOCK(&logchannels); 00651 00652 if (logfiles.event_log && logmsg->level == __LOG_EVENT) { 00653 fprintf(eventlog, "%s asterisk[%ld]: %s", logmsg->date, (long)getpid(), logmsg->str); 00654 fflush(eventlog); 00655 AST_LIST_UNLOCK(&logchannels); 00656 return; 00657 } 00658 00659 if (!AST_LIST_EMPTY(&logchannels)) { 00660 AST_LIST_TRAVERSE(&logchannels, chan, list) { 00661 /* If the channel is disabled, then move on to the next one */ 00662 if (chan->disabled) 00663 continue; 00664 /* Check syslog channels */ 00665 if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << logmsg->level))) { 00666 ast_log_vsyslog(logmsg->level, logmsg->file, logmsg->line, logmsg->function, logmsg->str); 00667 /* Console channels */ 00668 } else if (chan->type == LOGTYPE_CONSOLE && (chan->logmask & (1 << logmsg->level))) { 00669 char linestr[128]; 00670 char tmp1[80], tmp2[80], tmp3[80], tmp4[80]; 00671 00672 /* If the level is verbose, then skip it */ 00673 if (logmsg->level == __LOG_VERBOSE) 00674 continue; 00675 00676 /* Turn the numerical line number into a string */ 00677 snprintf(linestr, sizeof(linestr), "%d", logmsg->line); 00678 /* Build string to print out */ 00679 snprintf(buf, sizeof(buf), "[%s] %s[%ld]: %s:%s %s: %s", 00680 logmsg->date, 00681 term_color(tmp1, levels[logmsg->level], colors[logmsg->level], 0, sizeof(tmp1)), 00682 (long)GETTID(), 00683 term_color(tmp2, logmsg->file, COLOR_BRWHITE, 0, sizeof(tmp2)), 00684 term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)), 00685 term_color(tmp4, logmsg->function, COLOR_BRWHITE, 0, sizeof(tmp4)), 00686 logmsg->str); 00687 /* Print out */ 00688 ast_console_puts_mutable(buf); 00689 /* File channels */ 00690 } else if (chan->type == LOGTYPE_FILE && (chan->logmask & (1 << logmsg->level))) { 00691 int res = 0; 00692 00693 /* If no file pointer exists, skip it */ 00694 if (!chan->fileptr) 00695 continue; 00696 00697 /* Print out to the file */ 00698 res = fprintf(chan->fileptr, "[%s] %s[%ld] %s: %s", 00699 logmsg->date, levels[logmsg->level], (long)GETTID(), logmsg->file, logmsg->str); 00700 if (res <= 0 && !ast_strlen_zero(logmsg->str)) { 00701 fprintf(stderr, "**** Asterisk Logging Error: ***********\n"); 00702 if (errno == ENOMEM || errno == ENOSPC) 00703 fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename); 00704 else 00705 fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno)); 00706 manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno)); 00707 chan->disabled = 1; 00708 } 00709 } 00710 } 00711 } else if (logmsg->level != __LOG_VERBOSE) { 00712 fputs(logmsg->str, stdout); 00713 } 00714 00715 AST_LIST_UNLOCK(&logchannels); 00716 00717 /* If we need to reload because of the file size, then do so */ 00718 if (filesize_reload_needed) { 00719 reload_logger(1); 00720 ast_log(LOG_EVENT, "Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n"); 00721 if (option_verbose) 00722 ast_verbose("Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n"); 00723 } 00724 00725 return; 00726 } 00727 00728 /* Print a verbose message to the verbosers */ 00729 static void logger_print_verbose(struct logmsg *logmsg) 00730 { 00731 struct verb *v = NULL; 00732 00733 /* Iterate through the list of verbosers and pass them the log message string */ 00734 AST_LIST_LOCK(&verbosers); 00735 AST_LIST_TRAVERSE(&verbosers, v, list) 00736 v->verboser(logmsg->str); 00737 AST_LIST_UNLOCK(&verbosers); 00738 00739 return; 00740 } 00741 00742 /* Actual logging thread */ 00743 static void *logger_thread(void *data) 00744 { 00745 struct logmsg *next = NULL, *msg = NULL; 00746 00747 for (;;) { 00748 /* We lock the message list, and see if any message exists... if not we wait on the condition to be signalled */ 00749 AST_LIST_LOCK(&logmsgs); 00750 if (AST_LIST_EMPTY(&logmsgs)) 00751 ast_cond_wait(&logcond, &logmsgs.lock); 00752 next = AST_LIST_FIRST(&logmsgs); 00753 AST_LIST_HEAD_INIT_NOLOCK(&logmsgs); 00754 AST_LIST_UNLOCK(&logmsgs); 00755 00756 /* If we should stop, then stop */ 00757 if (close_logger_thread) 00758 break; 00759 00760 /* Otherwise go through and process each message in the order added */ 00761 while ((msg = next)) { 00762 /* Get the next entry now so that we can free our current structure later */ 00763 next = AST_LIST_NEXT(msg, list); 00764 00765 /* Depending on the type, send it to the proper function */ 00766 if (msg->type == LOGMSG_NORMAL) 00767 logger_print_normal(msg); 00768 else if (msg->type == LOGMSG_VERBOSE) 00769 logger_print_verbose(msg); 00770 00771 /* Free the data since we are done */ 00772 free(msg); 00773 } 00774 } 00775 00776 return NULL; 00777 } 00778 00779 int init_logger(void) 00780 { 00781 char tmp[256]; 00782 int res = 0; 00783 00784 /* auto rotate if sig SIGXFSZ comes a-knockin */ 00785 (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ); 00786 00787 /* start logger thread */ 00788 ast_cond_init(&logcond, NULL); 00789 if (ast_pthread_create(&logthread, NULL, logger_thread, NULL) < 0) { 00790 ast_cond_destroy(&logcond); 00791 return -1; 00792 } 00793 00794 /* register the logger cli commands */ 00795 ast_cli_register_multiple(cli_logger, sizeof(cli_logger) / sizeof(struct ast_cli_entry)); 00796 00797 mkdir(ast_config_AST_LOG_DIR, 0755); 00798 00799 /* create log channels */ 00800 init_logger_chain(); 00801 00802 /* create the eventlog */ 00803 if (logfiles.event_log) { 00804 mkdir(ast_config_AST_LOG_DIR, 0755); 00805 snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, EVENTLOG); 00806 eventlog = fopen(tmp, "a"); 00807 if (eventlog) { 00808 ast_log(LOG_EVENT, "Started Asterisk Event Logger\n"); 00809 if (option_verbose) 00810 ast_verbose("Asterisk Event Logger Started %s\n", tmp); 00811 } else { 00812 ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno)); 00813 res = -1; 00814 } 00815 } 00816 00817 if (logfiles.queue_log) { 00818 snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name); 00819 qlog = fopen(tmp, "a"); 00820 ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", ""); 00821 } 00822 return res; 00823 } 00824 00825 void close_logger(void) 00826 { 00827 struct logchannel *f = NULL; 00828 00829 /* Stop logger thread */ 00830 AST_LIST_LOCK(&logmsgs); 00831 close_logger_thread = 1; 00832 ast_cond_signal(&logcond); 00833 AST_LIST_UNLOCK(&logmsgs); 00834 00835 AST_LIST_LOCK(&logchannels); 00836 00837 if (eventlog) { 00838 fclose(eventlog); 00839 eventlog = NULL; 00840 } 00841 00842 if (qlog) { 00843 fclose(qlog); 00844 qlog = NULL; 00845 } 00846 00847 AST_LIST_TRAVERSE(&logchannels, f, list) { 00848 if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) { 00849 fclose(f->fileptr); 00850 f->fileptr = NULL; 00851 } 00852 } 00853 00854 closelog(); /* syslog */ 00855 00856 AST_LIST_UNLOCK(&logchannels); 00857 00858 return; 00859 } 00860 00861 /*! 00862 * \brief send log messages to syslog and/or the console 00863 */ 00864 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) 00865 { 00866 struct logmsg *logmsg = NULL; 00867 struct ast_str *buf = NULL; 00868 struct tm tm; 00869 time_t t; 00870 int res = 0; 00871 va_list ap; 00872 00873 if (!(buf = ast_str_thread_get(&log_buf, LOG_BUF_INIT_SIZE))) 00874 return; 00875 00876 if (AST_LIST_EMPTY(&logchannels)) { 00877 /* 00878 * we don't have the logger chain configured yet, 00879 * so just log to stdout 00880 */ 00881 if (level != __LOG_VERBOSE) { 00882 int res; 00883 va_start(ap, fmt); 00884 res = ast_str_set_va(&buf, BUFSIZ, fmt, ap); /* XXX BUFSIZ ? */ 00885 va_end(ap); 00886 if (res != AST_DYNSTR_BUILD_FAILED) { 00887 term_filter_escapes(buf->str); 00888 fputs(buf->str, stdout); 00889 } 00890 } 00891 return; 00892 } 00893 00894 /* don't display LOG_DEBUG messages unless option_verbose _or_ option_debug 00895 are non-zero; LOG_DEBUG messages can still be displayed if option_debug 00896 is zero, if option_verbose is non-zero (this allows for 'level zero' 00897 LOG_DEBUG messages to be displayed, if the logmask on any channel 00898 allows it) 00899 */ 00900 if (!option_verbose && !option_debug && (level == __LOG_DEBUG)) 00901 return; 00902 00903 /* Ignore anything that never gets logged anywhere */ 00904 if (!(global_logmask & (1 << level))) 00905 return; 00906 00907 /* Ignore anything other than the currently debugged file if there is one */ 00908 if ((level == __LOG_DEBUG) && !ast_strlen_zero(debug_filename) && strcasecmp(debug_filename, file)) 00909 return; 00910 00911 /* Build string */ 00912 va_start(ap, fmt); 00913 res = ast_str_set_va(&buf, BUFSIZ, fmt, ap); 00914 va_end(ap); 00915 00916 /* If the build failed, then abort and free this structure */ 00917 if (res == AST_DYNSTR_BUILD_FAILED) 00918 return; 00919 00920 /* Create a new logging message */ 00921 if (!(logmsg = ast_calloc(1, sizeof(*logmsg) + res + 1))) 00922 return; 00923 00924 /* Copy string over */ 00925 strcpy(logmsg->str, buf->str); 00926 00927 /* Set type to be normal */ 00928 logmsg->type = LOGMSG_NORMAL; 00929 00930 /* Create our date/time */ 00931 time(&t); 00932 localtime_r(&t, &tm); 00933 strftime(logmsg->date, sizeof(logmsg->date), dateformat, &tm); 00934 00935 /* Copy over data */ 00936 logmsg->level = level; 00937 logmsg->file = file; 00938 logmsg->line = line; 00939 logmsg->function = function; 00940 00941 /* If the logger thread is active, append it to the tail end of the list - otherwise skip that step */ 00942 if (logthread != AST_PTHREADT_NULL) { 00943 AST_LIST_LOCK(&logmsgs); 00944 AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list); 00945 ast_cond_signal(&logcond); 00946 AST_LIST_UNLOCK(&logmsgs); 00947 } else { 00948 logger_print_normal(logmsg); 00949 free(logmsg); 00950 } 00951 00952 return; 00953 } 00954 00955 void ast_backtrace(void) 00956 { 00957 #ifdef STACK_BACKTRACES 00958 int count=0, i=0; 00959 void **addresses; 00960 char **strings; 00961 00962 if ((addresses = ast_calloc(MAX_BACKTRACE_FRAMES, sizeof(*addresses)))) { 00963 count = backtrace(addresses, MAX_BACKTRACE_FRAMES); 00964 if ((strings = backtrace_symbols(addresses, count))) { 00965 if (option_debug) 00966 ast_log(LOG_DEBUG, "Got %d backtrace record%c\n", count, count != 1 ? 's' : ' '); 00967 for (i=0; i < count ; i++) { 00968 if (option_debug) 00969 ast_log(LOG_DEBUG, "#%d: [%08X] %s\n", i, (unsigned int)addresses[i], strings[i]); 00970 } 00971 free(strings); 00972 } else { 00973 if (option_debug) 00974 ast_log(LOG_DEBUG, "Could not allocate memory for backtrace\n"); 00975 } 00976 free(addresses); 00977 } 00978 #else 00979 #ifdef Linux 00980 ast_log(LOG_WARNING, "Must compile with 'make dont-optimize' for stack backtraces\n"); 00981 #else 00982 ast_log(LOG_WARNING, "Inline stack backtraces are only available on the Linux platform.\n"); 00983 #endif 00984 #endif 00985 } 00986 00987 void ast_verbose(const char *fmt, ...) 00988 { 00989 struct logmsg *logmsg = NULL; 00990 struct ast_str *buf = NULL; 00991 int res = 0; 00992 va_list ap; 00993 00994 if (!(buf = ast_str_thread_get(&verbose_buf, VERBOSE_BUF_INIT_SIZE))) 00995 return; 00996 00997 /* Build string */ 00998 va_start(ap, fmt); 00999 res = ast_str_set_va(&buf, 0, fmt, ap); 01000 va_end(ap); 01001 01002 /* If the build failed then we can drop this allocated message */ 01003 if (res == AST_DYNSTR_BUILD_FAILED) 01004 return; 01005 01006 if (!(logmsg = ast_calloc(1, sizeof(*logmsg) + res + 1))) 01007 return; 01008 01009 strcpy(logmsg->str, buf->str); 01010 01011 /* Set type */ 01012 logmsg->type = LOGMSG_VERBOSE; 01013 01014 if (ast_opt_timestamp) { 01015 time_t t; 01016 struct tm tm; 01017 char date[40]; 01018 char *datefmt; 01019 01020 time(&t); 01021 localtime_r(&t, &tm); 01022 strftime(date, sizeof(date), dateformat, &tm); 01023 datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1); 01024 sprintf(datefmt, "[%s] %s", date, fmt); 01025 fmt = datefmt; 01026 } 01027 01028 /* Add to the list and poke the thread if possible */ 01029 if (logthread != AST_PTHREADT_NULL) { 01030 AST_LIST_LOCK(&logmsgs); 01031 AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list); 01032 ast_cond_signal(&logcond); 01033 AST_LIST_UNLOCK(&logmsgs); 01034 } else { 01035 logger_print_verbose(logmsg); 01036 free(logmsg); 01037 } 01038 } 01039 01040 int ast_register_verbose(void (*v)(const char *string)) 01041 { 01042 struct verb *verb; 01043 01044 if (!(verb = ast_malloc(sizeof(*verb)))) 01045 return -1; 01046 01047 verb->verboser = v; 01048 01049 AST_LIST_LOCK(&verbosers); 01050 AST_LIST_INSERT_HEAD(&verbosers, verb, list); 01051 AST_LIST_UNLOCK(&verbosers); 01052 01053 return 0; 01054 } 01055 01056 int ast_unregister_verbose(void (*v)(const char *string)) 01057 { 01058 struct verb *cur; 01059 01060 AST_LIST_LOCK(&verbosers); 01061 AST_LIST_TRAVERSE_SAFE_BEGIN(&verbosers, cur, list) { 01062 if (cur->verboser == v) { 01063 AST_LIST_REMOVE_CURRENT(&verbosers, list); 01064 free(cur); 01065 break; 01066 } 01067 } 01068 AST_LIST_TRAVERSE_SAFE_END 01069 AST_LIST_UNLOCK(&verbosers); 01070 01071 return cur ? 0 : -1; 01072 }