00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "asterisk.h"
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 298960 $")
00031
00032
00033
00034 #include <syslog.h>
00035
00036 #include "asterisk/_private.h"
00037 #include "asterisk/paths.h"
00038 #include "asterisk/logger.h"
00039 #include "asterisk/lock.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/config.h"
00042 #include "asterisk/term.h"
00043 #include "asterisk/cli.h"
00044 #include "asterisk/utils.h"
00045 #include "asterisk/manager.h"
00046 #include "asterisk/threadstorage.h"
00047 #include "asterisk/strings.h"
00048 #include "asterisk/pbx.h"
00049 #include "asterisk/app.h"
00050 #include "asterisk/syslog.h"
00051
00052 #include <signal.h>
00053 #include <time.h>
00054 #include <sys/stat.h>
00055 #include <fcntl.h>
00056 #ifdef HAVE_BKTR
00057 #include <execinfo.h>
00058 #define MAX_BACKTRACE_FRAMES 20
00059 # if defined(HAVE_DLADDR) && defined(HAVE_BFD) && defined(BETTER_BACKTRACES)
00060 # include <dlfcn.h>
00061 # include <bfd.h>
00062 # endif
00063 #endif
00064
00065 #if defined(__linux__) && !defined(__NR_gettid)
00066 #include <asm/unistd.h>
00067 #endif
00068
00069 #if defined(__linux__) && defined(__NR_gettid)
00070 #define GETTID() syscall(__NR_gettid)
00071 #else
00072 #define GETTID() getpid()
00073 #endif
00074 static char dateformat[256] = "%b %e %T";
00075
00076 static char queue_log_name[256] = QUEUELOG;
00077 static char exec_after_rotate[256] = "";
00078
00079 static int filesize_reload_needed;
00080 static unsigned int global_logmask = 0xFFFF;
00081 static int queuelog_init;
00082
00083 static enum rotatestrategy {
00084 SEQUENTIAL = 1 << 0,
00085 ROTATE = 1 << 1,
00086 TIMESTAMP = 1 << 2,
00087 } rotatestrategy = SEQUENTIAL;
00088
00089 static struct {
00090 unsigned int queue_log:1;
00091 unsigned int queue_log_to_file:1;
00092 unsigned int queue_adaptive_realtime:1;
00093 } logfiles = { 1 };
00094
00095 static char hostname[MAXHOSTNAMELEN];
00096
00097 enum logtypes {
00098 LOGTYPE_SYSLOG,
00099 LOGTYPE_FILE,
00100 LOGTYPE_CONSOLE,
00101 };
00102
00103 struct logchannel {
00104
00105 unsigned int logmask;
00106
00107 int disabled;
00108
00109 int facility;
00110
00111 enum logtypes type;
00112
00113 FILE *fileptr;
00114
00115 char filename[PATH_MAX];
00116
00117 AST_LIST_ENTRY(logchannel) list;
00118
00119 int lineno;
00120
00121 char components[0];
00122 };
00123
00124 static AST_RWLIST_HEAD_STATIC(logchannels, logchannel);
00125
00126 enum logmsgtypes {
00127 LOGMSG_NORMAL = 0,
00128 LOGMSG_VERBOSE,
00129 };
00130
00131 struct logmsg {
00132 enum logmsgtypes type;
00133 int level;
00134 int line;
00135 long process_id;
00136 AST_DECLARE_STRING_FIELDS(
00137 AST_STRING_FIELD(date);
00138 AST_STRING_FIELD(file);
00139 AST_STRING_FIELD(function);
00140 AST_STRING_FIELD(message);
00141 AST_STRING_FIELD(level_name);
00142 );
00143 AST_LIST_ENTRY(logmsg) list;
00144 };
00145
00146 static AST_LIST_HEAD_STATIC(logmsgs, logmsg);
00147 static pthread_t logthread = AST_PTHREADT_NULL;
00148 static ast_cond_t logcond;
00149 static int close_logger_thread = 0;
00150
00151 static FILE *qlog;
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164 static char *levels[32] = {
00165 "DEBUG",
00166 "---EVENT---",
00167 "NOTICE",
00168 "WARNING",
00169 "ERROR",
00170 "VERBOSE",
00171 "DTMF",
00172 };
00173
00174
00175 static const int colors[32] = {
00176 COLOR_BRGREEN,
00177 COLOR_BRBLUE,
00178 COLOR_YELLOW,
00179 COLOR_BRRED,
00180 COLOR_RED,
00181 COLOR_GREEN,
00182 COLOR_BRGREEN,
00183 0,
00184 0,
00185 0,
00186 0,
00187 0,
00188 0,
00189 0,
00190 0,
00191 0,
00192 COLOR_BRBLUE,
00193 COLOR_BRBLUE,
00194 COLOR_BRBLUE,
00195 COLOR_BRBLUE,
00196 COLOR_BRBLUE,
00197 COLOR_BRBLUE,
00198 COLOR_BRBLUE,
00199 COLOR_BRBLUE,
00200 COLOR_BRBLUE,
00201 COLOR_BRBLUE,
00202 COLOR_BRBLUE,
00203 COLOR_BRBLUE,
00204 COLOR_BRBLUE,
00205 COLOR_BRBLUE,
00206 COLOR_BRBLUE,
00207 COLOR_BRBLUE,
00208 };
00209
00210 AST_THREADSTORAGE(verbose_buf);
00211 #define VERBOSE_BUF_INIT_SIZE 256
00212
00213 AST_THREADSTORAGE(log_buf);
00214 #define LOG_BUF_INIT_SIZE 256
00215
00216 static void logger_queue_init(void);
00217
00218 static unsigned int make_components(const char *s, int lineno)
00219 {
00220 char *w;
00221 unsigned int res = 0;
00222 char *stringp = ast_strdupa(s);
00223 unsigned int x;
00224
00225 while ((w = strsep(&stringp, ","))) {
00226 w = ast_skip_blanks(w);
00227
00228 if (!strcmp(w, "*")) {
00229 res = 0xFFFFFFFF;
00230 break;
00231 } else for (x = 0; x < ARRAY_LEN(levels); x++) {
00232 if (levels[x] && !strcasecmp(w, levels[x])) {
00233 res |= (1 << x);
00234 break;
00235 }
00236 }
00237 }
00238
00239 return res;
00240 }
00241
00242 static struct logchannel *make_logchannel(const char *channel, const char *components, int lineno)
00243 {
00244 struct logchannel *chan;
00245 char *facility;
00246
00247 if (ast_strlen_zero(channel) || !(chan = ast_calloc(1, sizeof(*chan) + strlen(components) + 1)))
00248 return NULL;
00249
00250 strcpy(chan->components, components);
00251 chan->lineno = lineno;
00252
00253 if (!strcasecmp(channel, "console")) {
00254 chan->type = LOGTYPE_CONSOLE;
00255 } else if (!strncasecmp(channel, "syslog", 6)) {
00256
00257
00258
00259
00260 facility = strchr(channel, '.');
00261 if (!facility++ || !facility) {
00262 facility = "local0";
00263 }
00264
00265 chan->facility = ast_syslog_facility(facility);
00266
00267 if (chan->facility < 0) {
00268 fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
00269 ast_free(chan);
00270 return NULL;
00271 }
00272
00273 chan->type = LOGTYPE_SYSLOG;
00274 ast_copy_string(chan->filename, channel, sizeof(chan->filename));
00275 openlog("asterisk", LOG_PID, chan->facility);
00276 } else {
00277 if (!ast_strlen_zero(hostname)) {
00278 snprintf(chan->filename, sizeof(chan->filename), "%s/%s.%s",
00279 channel[0] != '/' ? ast_config_AST_LOG_DIR : "", channel, hostname);
00280 } else {
00281 snprintf(chan->filename, sizeof(chan->filename), "%s/%s",
00282 channel[0] != '/' ? ast_config_AST_LOG_DIR : "", channel);
00283 }
00284 if (!(chan->fileptr = fopen(chan->filename, "a"))) {
00285
00286 fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", chan->filename, strerror(errno));
00287 }
00288 chan->type = LOGTYPE_FILE;
00289 }
00290 chan->logmask = make_components(chan->components, lineno);
00291
00292 return chan;
00293 }
00294
00295 static void init_logger_chain(int locked)
00296 {
00297 struct logchannel *chan;
00298 struct ast_config *cfg;
00299 struct ast_variable *var;
00300 const char *s;
00301 struct ast_flags config_flags = { 0 };
00302
00303 if (!(cfg = ast_config_load2("logger.conf", "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
00304 return;
00305 }
00306
00307
00308 if (!locked) {
00309 AST_RWLIST_WRLOCK(&logchannels);
00310 }
00311 while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list))) {
00312 ast_free(chan);
00313 }
00314 global_logmask = 0;
00315 if (!locked) {
00316 AST_RWLIST_UNLOCK(&logchannels);
00317 }
00318
00319 errno = 0;
00320
00321 closelog();
00322
00323
00324 if (!cfg) {
00325 if (errno) {
00326 fprintf(stderr, "Unable to open logger.conf: %s; default settings will be used.\n", strerror(errno));
00327 } else {
00328 fprintf(stderr, "Errors detected in logger.conf: see above; default settings will be used.\n");
00329 }
00330 if (!(chan = ast_calloc(1, sizeof(*chan)))) {
00331 return;
00332 }
00333 chan->type = LOGTYPE_CONSOLE;
00334 chan->logmask = __LOG_WARNING | __LOG_NOTICE | __LOG_ERROR;
00335 if (!locked) {
00336 AST_RWLIST_WRLOCK(&logchannels);
00337 }
00338 AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
00339 global_logmask |= chan->logmask;
00340 if (!locked) {
00341 AST_RWLIST_UNLOCK(&logchannels);
00342 }
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 fprintf(stderr, "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 }
00363 if ((s = ast_variable_retrieve(cfg, "general", "queue_log_to_file"))) {
00364 logfiles.queue_log_to_file = ast_true(s);
00365 }
00366 if ((s = ast_variable_retrieve(cfg, "general", "queue_log_name"))) {
00367 ast_copy_string(queue_log_name, s, sizeof(queue_log_name));
00368 }
00369 if ((s = ast_variable_retrieve(cfg, "general", "exec_after_rotate"))) {
00370 ast_copy_string(exec_after_rotate, s, sizeof(exec_after_rotate));
00371 }
00372 if ((s = ast_variable_retrieve(cfg, "general", "rotatestrategy"))) {
00373 if (strcasecmp(s, "timestamp") == 0) {
00374 rotatestrategy = TIMESTAMP;
00375 } else if (strcasecmp(s, "rotate") == 0) {
00376 rotatestrategy = ROTATE;
00377 } else if (strcasecmp(s, "sequential") == 0) {
00378 rotatestrategy = SEQUENTIAL;
00379 } else {
00380 fprintf(stderr, "Unknown rotatestrategy: %s\n", s);
00381 }
00382 } else {
00383 if ((s = ast_variable_retrieve(cfg, "general", "rotatetimestamp"))) {
00384 rotatestrategy = ast_true(s) ? TIMESTAMP : SEQUENTIAL;
00385 fprintf(stderr, "rotatetimestamp option has been deprecated. Please use rotatestrategy instead.\n");
00386 }
00387 }
00388
00389 if (!locked) {
00390 AST_RWLIST_WRLOCK(&logchannels);
00391 }
00392 var = ast_variable_browse(cfg, "logfiles");
00393 for (; var; var = var->next) {
00394 if (!(chan = make_logchannel(var->name, var->value, var->lineno))) {
00395 continue;
00396 }
00397 AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
00398 global_logmask |= chan->logmask;
00399 }
00400 if (qlog) {
00401 char tmp[4096];
00402 fclose(qlog);
00403 snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
00404 qlog = fopen(tmp, "a");
00405 }
00406
00407 if (!locked) {
00408 AST_RWLIST_UNLOCK(&logchannels);
00409 }
00410
00411 ast_config_destroy(cfg);
00412 }
00413
00414 void ast_child_verbose(int level, const char *fmt, ...)
00415 {
00416 char *msg = NULL, *emsg = NULL, *sptr, *eptr;
00417 va_list ap, aq;
00418 int size;
00419
00420
00421 if (option_verbose < level) {
00422 return;
00423 }
00424
00425 va_start(ap, fmt);
00426 va_copy(aq, ap);
00427 if ((size = vsnprintf(msg, 0, fmt, ap)) < 0) {
00428 va_end(ap);
00429 va_end(aq);
00430 return;
00431 }
00432 va_end(ap);
00433
00434 if (!(msg = ast_malloc(size + 1))) {
00435 va_end(aq);
00436 return;
00437 }
00438
00439 vsnprintf(msg, size + 1, fmt, aq);
00440 va_end(aq);
00441
00442 if (!(emsg = ast_malloc(size * 2 + 1))) {
00443 ast_free(msg);
00444 return;
00445 }
00446
00447 for (sptr = msg, eptr = emsg; ; sptr++) {
00448 if (*sptr == '"') {
00449 *eptr++ = '\\';
00450 }
00451 *eptr++ = *sptr;
00452 if (*sptr == '\0') {
00453 break;
00454 }
00455 }
00456 ast_free(msg);
00457
00458 fprintf(stdout, "verbose \"%s\" %d\n", emsg, level);
00459 fflush(stdout);
00460 ast_free(emsg);
00461 }
00462
00463 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
00464 {
00465 va_list ap;
00466 struct timeval tv;
00467 struct ast_tm tm;
00468 char qlog_msg[8192];
00469 int qlog_len;
00470 char time_str[30];
00471
00472 if (!queuelog_init) {
00473 queuelog_init = 1;
00474 logger_queue_init();
00475 }
00476
00477 if (ast_check_realtime("queue_log")) {
00478 tv = ast_tvnow();
00479 ast_localtime(&tv, &tm, NULL);
00480 ast_strftime(time_str, sizeof(time_str), "%F %T.%6q", &tm);
00481 va_start(ap, fmt);
00482 vsnprintf(qlog_msg, sizeof(qlog_msg), fmt, ap);
00483 va_end(ap);
00484 if (logfiles.queue_adaptive_realtime) {
00485 AST_DECLARE_APP_ARGS(args,
00486 AST_APP_ARG(data)[5];
00487 );
00488 AST_NONSTANDARD_APP_ARGS(args, qlog_msg, '|');
00489
00490 ast_realtime_require_field("queue_log", "data1", RQ_CHAR, strlen(S_OR(args.data[0], "")),
00491 "data2", RQ_CHAR, strlen(S_OR(args.data[1], "")),
00492 "data3", RQ_CHAR, strlen(S_OR(args.data[2], "")),
00493 "data4", RQ_CHAR, strlen(S_OR(args.data[3], "")),
00494 "data5", RQ_CHAR, strlen(S_OR(args.data[4], "")),
00495 SENTINEL);
00496
00497
00498 ast_store_realtime("queue_log", "time", time_str,
00499 "callid", callid,
00500 "queuename", queuename,
00501 "agent", agent,
00502 "event", event,
00503 "data1", S_OR(args.data[0], ""),
00504 "data2", S_OR(args.data[1], ""),
00505 "data3", S_OR(args.data[2], ""),
00506 "data4", S_OR(args.data[3], ""),
00507 "data5", S_OR(args.data[4], ""),
00508 SENTINEL);
00509 } else {
00510 ast_store_realtime("queue_log", "time", time_str,
00511 "callid", callid,
00512 "queuename", queuename,
00513 "agent", agent,
00514 "event", event,
00515 "data", qlog_msg,
00516 SENTINEL);
00517 }
00518
00519 if (!logfiles.queue_log_to_file) {
00520 return;
00521 }
00522 }
00523
00524 if (qlog) {
00525 va_start(ap, fmt);
00526 qlog_len = snprintf(qlog_msg, sizeof(qlog_msg), "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
00527 vsnprintf(qlog_msg + qlog_len, sizeof(qlog_msg) - qlog_len, fmt, ap);
00528 va_end(ap);
00529 AST_RWLIST_RDLOCK(&logchannels);
00530 if (qlog) {
00531 fprintf(qlog, "%s\n", qlog_msg);
00532 fflush(qlog);
00533 }
00534 AST_RWLIST_UNLOCK(&logchannels);
00535 }
00536 }
00537
00538 static int rotate_file(const char *filename)
00539 {
00540 char old[PATH_MAX];
00541 char new[PATH_MAX];
00542 int x, y, which, found, res = 0, fd;
00543 char *suffixes[4] = { "", ".gz", ".bz2", ".Z" };
00544
00545 switch (rotatestrategy) {
00546 case SEQUENTIAL:
00547 for (x = 0; ; x++) {
00548 snprintf(new, sizeof(new), "%s.%d", filename, x);
00549 fd = open(new, O_RDONLY);
00550 if (fd > -1)
00551 close(fd);
00552 else
00553 break;
00554 }
00555 if (rename(filename, new)) {
00556 fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
00557 res = -1;
00558 } else {
00559 filename = new;
00560 }
00561 break;
00562 case TIMESTAMP:
00563 snprintf(new, sizeof(new), "%s.%ld", filename, (long)time(NULL));
00564 if (rename(filename, new)) {
00565 fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
00566 res = -1;
00567 } else {
00568 filename = new;
00569 }
00570 break;
00571 case ROTATE:
00572
00573 for (x = 0; ; x++) {
00574 found = 0;
00575 for (which = 0; which < ARRAY_LEN(suffixes); which++) {
00576 snprintf(new, sizeof(new), "%s.%d%s", filename, x, suffixes[which]);
00577 fd = open(new, O_RDONLY);
00578 if (fd > -1) {
00579 close(fd);
00580 found = 1;
00581 break;
00582 }
00583 }
00584 if (!found) {
00585 break;
00586 }
00587 }
00588
00589
00590 for (y = x; y > 0; y--) {
00591 for (which = 0; which < ARRAY_LEN(suffixes); which++) {
00592 snprintf(old, sizeof(old), "%s.%d%s", filename, y - 1, suffixes[which]);
00593 fd = open(old, O_RDONLY);
00594 if (fd > -1) {
00595
00596 close(fd);
00597 snprintf(new, sizeof(new), "%s.%d%s", filename, y, suffixes[which]);
00598 if (rename(old, new)) {
00599 fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
00600 res = -1;
00601 }
00602 break;
00603 }
00604 }
00605 }
00606
00607
00608 snprintf(new, sizeof(new), "%s.0", filename);
00609 if (rename(filename, new)) {
00610 fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
00611 res = -1;
00612 }
00613 }
00614
00615 if (!ast_strlen_zero(exec_after_rotate)) {
00616 struct ast_channel *c = ast_dummy_channel_alloc();
00617 char buf[512];
00618 pbx_builtin_setvar_helper(c, "filename", filename);
00619 pbx_substitute_variables_helper(c, exec_after_rotate, buf, sizeof(buf));
00620 if (ast_safe_system(buf) == -1) {
00621 ast_log(LOG_WARNING, "error executing '%s'\n", buf);
00622 }
00623 c = ast_channel_release(c);
00624 }
00625 return res;
00626 }
00627
00628 static int reload_logger(int rotate)
00629 {
00630 char old[PATH_MAX] = "";
00631 int queue_rotate = rotate;
00632 struct logchannel *f;
00633 int res = 0;
00634
00635 AST_RWLIST_WRLOCK(&logchannels);
00636
00637 if (qlog) {
00638 if (rotate < 0) {
00639
00640 if (ftello(qlog) > 0x40000000) {
00641 fclose(qlog);
00642 qlog = NULL;
00643 } else {
00644 queue_rotate = 0;
00645 }
00646 } else {
00647 fclose(qlog);
00648 qlog = NULL;
00649 }
00650 } else {
00651 queue_rotate = 0;
00652 }
00653
00654 ast_mkdir(ast_config_AST_LOG_DIR, 0777);
00655
00656 AST_RWLIST_TRAVERSE(&logchannels, f, list) {
00657 if (f->disabled) {
00658 f->disabled = 0;
00659 manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
00660 }
00661 if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
00662 int rotate_this = 0;
00663 if (ftello(f->fileptr) > 0x40000000) {
00664
00665 rotate_this = 1;
00666 }
00667 fclose(f->fileptr);
00668 f->fileptr = NULL;
00669 if (rotate || rotate_this) {
00670 rotate_file(f->filename);
00671 }
00672 }
00673 }
00674
00675 filesize_reload_needed = 0;
00676
00677 init_logger_chain(1 );
00678
00679 if (logfiles.queue_log) {
00680 do {
00681 ast_unload_realtime("queue_log");
00682 if (ast_check_realtime("queue_log")) {
00683 if (!ast_realtime_require_field("queue_log",
00684 "time", RQ_DATETIME, 26, "data1", RQ_CHAR, 20,
00685 "data2", RQ_CHAR, 20, "data3", RQ_CHAR, 20,
00686 "data4", RQ_CHAR, 20, "data5", RQ_CHAR, 20, SENTINEL)) {
00687 logfiles.queue_adaptive_realtime = 1;
00688 } else {
00689 logfiles.queue_adaptive_realtime = 0;
00690 }
00691
00692 if (!logfiles.queue_log_to_file) {
00693
00694 break;
00695 }
00696 }
00697 if (qlog) {
00698 fclose(qlog);
00699 qlog = NULL;
00700 }
00701 snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
00702 if (queue_rotate) {
00703 rotate_file(old);
00704 }
00705
00706 qlog = fopen(old, "a");
00707 if (qlog) {
00708 AST_RWLIST_UNLOCK(&logchannels);
00709 ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
00710 AST_RWLIST_WRLOCK(&logchannels);
00711 ast_verb(1, "Asterisk Queue Logger restarted\n");
00712 } else {
00713 ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
00714 res = -1;
00715 }
00716 } while (0);
00717 }
00718
00719 AST_RWLIST_UNLOCK(&logchannels);
00720
00721 return res;
00722 }
00723
00724
00725
00726 int logger_reload(void)
00727 {
00728 if (reload_logger(0)) {
00729 return RESULT_FAILURE;
00730 }
00731 return RESULT_SUCCESS;
00732 }
00733
00734 static char *handle_logger_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00735 {
00736 switch (cmd) {
00737 case CLI_INIT:
00738 e->command = "logger reload";
00739 e->usage =
00740 "Usage: logger reload\n"
00741 " Reloads the logger subsystem state. Use after restarting syslogd(8) if you are using syslog logging.\n";
00742 return NULL;
00743 case CLI_GENERATE:
00744 return NULL;
00745 }
00746 if (reload_logger(0)) {
00747 ast_cli(a->fd, "Failed to reload the logger\n");
00748 return CLI_FAILURE;
00749 }
00750 return CLI_SUCCESS;
00751 }
00752
00753 static char *handle_logger_rotate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00754 {
00755 switch (cmd) {
00756 case CLI_INIT:
00757 e->command = "logger rotate";
00758 e->usage =
00759 "Usage: logger rotate\n"
00760 " Rotates and Reopens the log files.\n";
00761 return NULL;
00762 case CLI_GENERATE:
00763 return NULL;
00764 }
00765 if (reload_logger(1)) {
00766 ast_cli(a->fd, "Failed to reload the logger and rotate log files\n");
00767 return CLI_FAILURE;
00768 }
00769 return CLI_SUCCESS;
00770 }
00771
00772 static char *handle_logger_set_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00773 {
00774 int x;
00775 int state;
00776 int level = -1;
00777
00778 switch (cmd) {
00779 case CLI_INIT:
00780 e->command = "logger set level {DEBUG|NOTICE|WARNING|ERROR|VERBOSE|DTMF} {on|off}";
00781 e->usage =
00782 "Usage: logger set level {DEBUG|NOTICE|WARNING|ERROR|VERBOSE|DTMF} {on|off}\n"
00783 " Set a specific log level to enabled/disabled for this console.\n";
00784 return NULL;
00785 case CLI_GENERATE:
00786 return NULL;
00787 }
00788
00789 if (a->argc < 5)
00790 return CLI_SHOWUSAGE;
00791
00792 AST_RWLIST_WRLOCK(&logchannels);
00793
00794 for (x = 0; x < ARRAY_LEN(levels); x++) {
00795 if (levels[x] && !strcasecmp(a->argv[3], levels[x])) {
00796 level = x;
00797 break;
00798 }
00799 }
00800
00801 AST_RWLIST_UNLOCK(&logchannels);
00802
00803 state = ast_true(a->argv[4]) ? 1 : 0;
00804
00805 if (level != -1) {
00806 ast_console_toggle_loglevel(a->fd, level, state);
00807 ast_cli(a->fd, "Logger status for '%s' has been set to '%s'.\n", levels[level], state ? "on" : "off");
00808 } else
00809 return CLI_SHOWUSAGE;
00810
00811 return CLI_SUCCESS;
00812 }
00813
00814
00815 static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00816 {
00817 #define FORMATL "%-35.35s %-8.8s %-9.9s "
00818 struct logchannel *chan;
00819 switch (cmd) {
00820 case CLI_INIT:
00821 e->command = "logger show channels";
00822 e->usage =
00823 "Usage: logger show channels\n"
00824 " List configured logger channels.\n";
00825 return NULL;
00826 case CLI_GENERATE:
00827 return NULL;
00828 }
00829 ast_cli(a->fd, FORMATL, "Channel", "Type", "Status");
00830 ast_cli(a->fd, "Configuration\n");
00831 ast_cli(a->fd, FORMATL, "-------", "----", "------");
00832 ast_cli(a->fd, "-------------\n");
00833 AST_RWLIST_RDLOCK(&logchannels);
00834 AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
00835 unsigned int level;
00836
00837 ast_cli(a->fd, FORMATL, chan->filename, chan->type == LOGTYPE_CONSOLE ? "Console" : (chan->type == LOGTYPE_SYSLOG ? "Syslog" : "File"),
00838 chan->disabled ? "Disabled" : "Enabled");
00839 ast_cli(a->fd, " - ");
00840 for (level = 0; level < ARRAY_LEN(levels); level++) {
00841 if ((chan->logmask & (1 << level)) && levels[level]) {
00842 ast_cli(a->fd, "%s ", levels[level]);
00843 }
00844 }
00845 ast_cli(a->fd, "\n");
00846 }
00847 AST_RWLIST_UNLOCK(&logchannels);
00848 ast_cli(a->fd, "\n");
00849
00850 return CLI_SUCCESS;
00851 }
00852
00853 struct verb {
00854 void (*verboser)(const char *string);
00855 AST_LIST_ENTRY(verb) list;
00856 };
00857
00858 static AST_RWLIST_HEAD_STATIC(verbosers, verb);
00859
00860 static struct ast_cli_entry cli_logger[] = {
00861 AST_CLI_DEFINE(handle_logger_show_channels, "List configured log channels"),
00862 AST_CLI_DEFINE(handle_logger_reload, "Reopens the log files"),
00863 AST_CLI_DEFINE(handle_logger_rotate, "Rotates and reopens the log files"),
00864 AST_CLI_DEFINE(handle_logger_set_level, "Enables/Disables a specific logging level for this console")
00865 };
00866
00867 static void _handle_SIGXFSZ(int sig)
00868 {
00869
00870 filesize_reload_needed = 1;
00871 }
00872
00873 static struct sigaction handle_SIGXFSZ = {
00874 .sa_handler = _handle_SIGXFSZ,
00875 .sa_flags = SA_RESTART,
00876 };
00877
00878 static void ast_log_vsyslog(struct logmsg *msg)
00879 {
00880 char buf[BUFSIZ];
00881 int syslog_level = ast_syslog_priority_from_loglevel(msg->level);
00882
00883 if (syslog_level < 0) {
00884
00885 fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", msg->level);
00886 return;
00887 }
00888
00889 snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: %s",
00890 levels[msg->level], msg->process_id, msg->file, msg->line, msg->function, msg->message);
00891
00892 term_strip(buf, buf, strlen(buf) + 1);
00893 syslog(syslog_level, "%s", buf);
00894 }
00895
00896
00897 static void logger_print_normal(struct logmsg *logmsg)
00898 {
00899 struct logchannel *chan = NULL;
00900 char buf[BUFSIZ];
00901 struct verb *v = NULL;
00902
00903 if (logmsg->level == __LOG_VERBOSE) {
00904 char *tmpmsg = ast_strdupa(logmsg->message + 1);
00905
00906 AST_RWLIST_RDLOCK(&verbosers);
00907 AST_RWLIST_TRAVERSE(&verbosers, v, list)
00908 v->verboser(logmsg->message);
00909 AST_RWLIST_UNLOCK(&verbosers);
00910 ast_string_field_set(logmsg, message, tmpmsg);
00911 }
00912
00913 AST_RWLIST_RDLOCK(&logchannels);
00914
00915 if (!AST_RWLIST_EMPTY(&logchannels)) {
00916 AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
00917
00918 if (chan->disabled)
00919 continue;
00920
00921 if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << logmsg->level))) {
00922 ast_log_vsyslog(logmsg);
00923
00924 } else if (chan->type == LOGTYPE_CONSOLE && (chan->logmask & (1 << logmsg->level))) {
00925 char linestr[128];
00926 char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
00927
00928
00929 if (logmsg->level == __LOG_VERBOSE)
00930 continue;
00931
00932
00933 snprintf(linestr, sizeof(linestr), "%d", logmsg->line);
00934
00935 snprintf(buf, sizeof(buf), "[%s] %s[%ld]: %s:%s %s: %s",
00936 logmsg->date,
00937 term_color(tmp1, logmsg->level_name, colors[logmsg->level], 0, sizeof(tmp1)),
00938 logmsg->process_id,
00939 term_color(tmp2, logmsg->file, COLOR_BRWHITE, 0, sizeof(tmp2)),
00940 term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
00941 term_color(tmp4, logmsg->function, COLOR_BRWHITE, 0, sizeof(tmp4)),
00942 logmsg->message);
00943
00944 ast_console_puts_mutable(buf, logmsg->level);
00945
00946 } else if (chan->type == LOGTYPE_FILE && (chan->logmask & (1 << logmsg->level))) {
00947 int res = 0;
00948
00949
00950 if (!chan->fileptr) {
00951 continue;
00952 }
00953
00954
00955 res = fprintf(chan->fileptr, "[%s] %s[%ld] %s: %s",
00956 logmsg->date, logmsg->level_name, logmsg->process_id, logmsg->file, term_strip(buf, logmsg->message, BUFSIZ));
00957 if (res <= 0 && !ast_strlen_zero(logmsg->message)) {
00958 fprintf(stderr, "**** Asterisk Logging Error: ***********\n");
00959 if (errno == ENOMEM || errno == ENOSPC)
00960 fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename);
00961 else
00962 fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno));
00963 manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno));
00964 chan->disabled = 1;
00965 } else if (res > 0) {
00966 fflush(chan->fileptr);
00967 }
00968 }
00969 }
00970 } else if (logmsg->level != __LOG_VERBOSE) {
00971 fputs(logmsg->message, stdout);
00972 }
00973
00974 AST_RWLIST_UNLOCK(&logchannels);
00975
00976
00977 if (filesize_reload_needed) {
00978 reload_logger(-1);
00979 ast_verb(1, "Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
00980 }
00981
00982 return;
00983 }
00984
00985
00986 static void *logger_thread(void *data)
00987 {
00988 struct logmsg *next = NULL, *msg = NULL;
00989
00990 for (;;) {
00991
00992 AST_LIST_LOCK(&logmsgs);
00993 if (AST_LIST_EMPTY(&logmsgs)) {
00994 if (close_logger_thread) {
00995 break;
00996 } else {
00997 ast_cond_wait(&logcond, &logmsgs.lock);
00998 }
00999 }
01000 next = AST_LIST_FIRST(&logmsgs);
01001 AST_LIST_HEAD_INIT_NOLOCK(&logmsgs);
01002 AST_LIST_UNLOCK(&logmsgs);
01003
01004
01005 while ((msg = next)) {
01006
01007 next = AST_LIST_NEXT(msg, list);
01008
01009
01010 logger_print_normal(msg);
01011
01012
01013 ast_free(msg);
01014 }
01015
01016
01017 if (close_logger_thread)
01018 break;
01019 }
01020
01021 return NULL;
01022 }
01023
01024 static void logger_queue_init(void)
01025 {
01026
01027 ast_unload_realtime("queue_log");
01028 if (logfiles.queue_log && ast_check_realtime("queue_log")) {
01029 if (!ast_realtime_require_field("queue_log",
01030 "time", RQ_DATETIME, 26, "data1", RQ_CHAR, 20,
01031 "data2", RQ_CHAR, 20, "data3", RQ_CHAR, 20,
01032 "data4", RQ_CHAR, 20, "data5", RQ_CHAR, 20, SENTINEL)) {
01033 logfiles.queue_adaptive_realtime = 1;
01034 } else {
01035 logfiles.queue_adaptive_realtime = 0;
01036 }
01037 }
01038
01039 ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
01040 }
01041
01042 int init_logger(void)
01043 {
01044
01045 sigaction(SIGXFSZ, &handle_SIGXFSZ, NULL);
01046
01047
01048 ast_cond_init(&logcond, NULL);
01049 if (ast_pthread_create(&logthread, NULL, logger_thread, NULL) < 0) {
01050 ast_cond_destroy(&logcond);
01051 return -1;
01052 }
01053
01054
01055 ast_cli_register_multiple(cli_logger, ARRAY_LEN(cli_logger));
01056
01057 ast_mkdir(ast_config_AST_LOG_DIR, 0777);
01058
01059
01060 init_logger_chain(0 );
01061
01062 return 0;
01063 }
01064
01065 void close_logger(void)
01066 {
01067 struct logchannel *f = NULL;
01068
01069
01070 AST_LIST_LOCK(&logmsgs);
01071 close_logger_thread = 1;
01072 ast_cond_signal(&logcond);
01073 AST_LIST_UNLOCK(&logmsgs);
01074
01075 if (logthread != AST_PTHREADT_NULL)
01076 pthread_join(logthread, NULL);
01077
01078 AST_RWLIST_WRLOCK(&logchannels);
01079
01080 if (qlog) {
01081 fclose(qlog);
01082 qlog = NULL;
01083 }
01084
01085 AST_RWLIST_TRAVERSE(&logchannels, f, list) {
01086 if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
01087 fclose(f->fileptr);
01088 f->fileptr = NULL;
01089 }
01090 }
01091
01092 closelog();
01093
01094 AST_RWLIST_UNLOCK(&logchannels);
01095
01096 return;
01097 }
01098
01099
01100
01101
01102 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
01103 {
01104 struct logmsg *logmsg = NULL;
01105 struct ast_str *buf = NULL;
01106 struct ast_tm tm;
01107 struct timeval now = ast_tvnow();
01108 int res = 0;
01109 va_list ap;
01110 char datestring[256];
01111
01112 if (!(buf = ast_str_thread_get(&log_buf, LOG_BUF_INIT_SIZE)))
01113 return;
01114
01115 if (AST_RWLIST_EMPTY(&logchannels)) {
01116
01117
01118
01119
01120 if (level != __LOG_VERBOSE) {
01121 int result;
01122 va_start(ap, fmt);
01123 result = ast_str_set_va(&buf, BUFSIZ, fmt, ap);
01124 va_end(ap);
01125 if (result != AST_DYNSTR_BUILD_FAILED) {
01126 term_filter_escapes(ast_str_buffer(buf));
01127 fputs(ast_str_buffer(buf), stdout);
01128 }
01129 }
01130 return;
01131 }
01132
01133
01134
01135
01136
01137
01138
01139 if (!option_verbose && !option_debug && (level == __LOG_DEBUG))
01140 return;
01141
01142
01143 if (level != __LOG_VERBOSE && !(global_logmask & (1 << level)))
01144 return;
01145
01146
01147 va_start(ap, fmt);
01148 res = ast_str_set_va(&buf, BUFSIZ, fmt, ap);
01149 va_end(ap);
01150
01151
01152 if (res == AST_DYNSTR_BUILD_FAILED)
01153 return;
01154
01155
01156 if (!(logmsg = ast_calloc_with_stringfields(1, struct logmsg, res + 128)))
01157 return;
01158
01159
01160 ast_string_field_set(logmsg, message, ast_str_buffer(buf));
01161
01162
01163 if (level == __LOG_VERBOSE) {
01164 logmsg->type = LOGMSG_VERBOSE;
01165 } else {
01166 logmsg->type = LOGMSG_NORMAL;
01167 }
01168
01169
01170 ast_localtime(&now, &tm, NULL);
01171 ast_strftime(datestring, sizeof(datestring), dateformat, &tm);
01172 ast_string_field_set(logmsg, date, datestring);
01173
01174
01175 logmsg->level = level;
01176 logmsg->line = line;
01177 ast_string_field_set(logmsg, level_name, levels[level]);
01178 ast_string_field_set(logmsg, file, file);
01179 ast_string_field_set(logmsg, function, function);
01180 logmsg->process_id = (long) GETTID();
01181
01182
01183 if (logthread != AST_PTHREADT_NULL) {
01184 AST_LIST_LOCK(&logmsgs);
01185 AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
01186 ast_cond_signal(&logcond);
01187 AST_LIST_UNLOCK(&logmsgs);
01188 } else {
01189 logger_print_normal(logmsg);
01190 ast_free(logmsg);
01191 }
01192
01193 return;
01194 }
01195
01196 #ifdef HAVE_BKTR
01197
01198 struct ast_bt *ast_bt_create(void)
01199 {
01200 struct ast_bt *bt = ast_calloc(1, sizeof(*bt));
01201 if (!bt) {
01202 ast_log(LOG_ERROR, "Unable to allocate memory for backtrace structure!\n");
01203 return NULL;
01204 }
01205
01206 bt->alloced = 1;
01207
01208 ast_bt_get_addresses(bt);
01209
01210 return bt;
01211 }
01212
01213 int ast_bt_get_addresses(struct ast_bt *bt)
01214 {
01215 bt->num_frames = backtrace(bt->addresses, AST_MAX_BT_FRAMES);
01216
01217 return 0;
01218 }
01219
01220 void *ast_bt_destroy(struct ast_bt *bt)
01221 {
01222 if (bt->alloced) {
01223 ast_free(bt);
01224 }
01225
01226 return NULL;
01227 }
01228
01229 char **ast_bt_get_symbols(void **addresses, size_t num_frames)
01230 {
01231 char **strings = NULL;
01232 #if defined(BETTER_BACKTRACES)
01233 int stackfr;
01234 bfd *bfdobj;
01235 Dl_info dli;
01236 long allocsize;
01237 asymbol **syms = NULL;
01238 bfd_vma offset;
01239 const char *lastslash;
01240 asection *section;
01241 const char *file, *func;
01242 unsigned int line;
01243 char address_str[128];
01244 char msg[1024];
01245 size_t strings_size;
01246 size_t *eachlen;
01247 #endif
01248
01249 #if defined(BETTER_BACKTRACES)
01250 strings_size = num_frames * sizeof(*strings);
01251 eachlen = ast_calloc(num_frames, sizeof(*eachlen));
01252
01253 if (!(strings = ast_calloc(num_frames, sizeof(*strings)))) {
01254 return NULL;
01255 }
01256
01257 for (stackfr = 0; stackfr < num_frames; stackfr++) {
01258 int found = 0, symbolcount;
01259
01260 msg[0] = '\0';
01261
01262 if (!dladdr(addresses[stackfr], &dli)) {
01263 continue;
01264 }
01265
01266 if (strcmp(dli.dli_fname, "asterisk") == 0) {
01267 char asteriskpath[256];
01268 if (!(dli.dli_fname = ast_utils_which("asterisk", asteriskpath, sizeof(asteriskpath)))) {
01269
01270 ast_log(LOG_DEBUG, "Failed to find asterisk binary for debug symbols.\n");
01271 dli.dli_fname = "asterisk";
01272 }
01273 }
01274
01275 lastslash = strrchr(dli.dli_fname, '/');
01276 if ( (bfdobj = bfd_openr(dli.dli_fname, NULL)) &&
01277 bfd_check_format(bfdobj, bfd_object) &&
01278 (allocsize = bfd_get_symtab_upper_bound(bfdobj)) > 0 &&
01279 (syms = ast_malloc(allocsize)) &&
01280 (symbolcount = bfd_canonicalize_symtab(bfdobj, syms))) {
01281
01282 if (bfdobj->flags & DYNAMIC) {
01283 offset = addresses[stackfr] - dli.dli_fbase;
01284 } else {
01285 offset = addresses[stackfr] - (void *) 0;
01286 }
01287
01288 for (section = bfdobj->sections; section; section = section->next) {
01289 if ( !bfd_get_section_flags(bfdobj, section) & SEC_ALLOC ||
01290 section->vma > offset ||
01291 section->size + section->vma < offset) {
01292 continue;
01293 }
01294
01295 if (!bfd_find_nearest_line(bfdobj, section, syms, offset - section->vma, &file, &func, &line)) {
01296 continue;
01297 }
01298
01299
01300 found++;
01301 if ((lastslash = strrchr(file, '/'))) {
01302 const char *prevslash;
01303 for (prevslash = lastslash - 1; *prevslash != '/' && prevslash >= file; prevslash--);
01304 if (prevslash >= file) {
01305 lastslash = prevslash;
01306 }
01307 }
01308 if (dli.dli_saddr == NULL) {
01309 address_str[0] = '\0';
01310 } else {
01311 snprintf(address_str, sizeof(address_str), " (%p+%lX)",
01312 dli.dli_saddr,
01313 (unsigned long) (addresses[stackfr] - dli.dli_saddr));
01314 }
01315 snprintf(msg, sizeof(msg), "%s:%u %s()%s",
01316 lastslash ? lastslash + 1 : file, line,
01317 S_OR(func, "???"),
01318 address_str);
01319
01320 break;
01321 }
01322 }
01323 if (bfdobj) {
01324 bfd_close(bfdobj);
01325 if (syms) {
01326 ast_free(syms);
01327 }
01328 }
01329
01330
01331 if (!found) {
01332 if (dli.dli_saddr == NULL) {
01333 address_str[0] = '\0';
01334 } else {
01335 snprintf(address_str, sizeof(address_str), " (%p+%lX)",
01336 dli.dli_saddr,
01337 (unsigned long) (addresses[stackfr] - dli.dli_saddr));
01338 }
01339 snprintf(msg, sizeof(msg), "%s %s()%s",
01340 lastslash ? lastslash + 1 : dli.dli_fname,
01341 S_OR(dli.dli_sname, "<unknown>"),
01342 address_str);
01343 }
01344
01345 if (!ast_strlen_zero(msg)) {
01346 char **tmp;
01347 eachlen[stackfr] = strlen(msg);
01348 if (!(tmp = ast_realloc(strings, strings_size + eachlen[stackfr] + 1))) {
01349 ast_free(strings);
01350 strings = NULL;
01351 break;
01352 }
01353 strings = tmp;
01354 strings[stackfr] = (char *) strings + strings_size;
01355 ast_copy_string(strings[stackfr], msg, eachlen[stackfr] + 1);
01356 strings_size += eachlen[stackfr] + 1;
01357 }
01358 }
01359
01360 if (strings) {
01361
01362 strings[0] = (char *) strings + num_frames * sizeof(*strings);
01363 for (stackfr = 1; stackfr < num_frames; stackfr++) {
01364 strings[stackfr] = strings[stackfr - 1] + eachlen[stackfr - 1] + 1;
01365 }
01366 }
01367 #else
01368 strings = backtrace_symbols(addresses, num_frames);
01369 #endif
01370 return strings;
01371 }
01372
01373 #endif
01374
01375 void ast_backtrace(void)
01376 {
01377 #ifdef HAVE_BKTR
01378 struct ast_bt *bt;
01379 int i = 0;
01380 char **strings;
01381
01382 if (!(bt = ast_bt_create())) {
01383 ast_log(LOG_WARNING, "Unable to allocate space for backtrace structure\n");
01384 return;
01385 }
01386
01387 if ((strings = ast_bt_get_symbols(bt->addresses, bt->num_frames))) {
01388 ast_debug(1, "Got %d backtrace record%c\n", bt->num_frames, bt->num_frames != 1 ? 's' : ' ');
01389 for (i = 3; i < bt->num_frames - 2; i++) {
01390 ast_log(LOG_DEBUG, "#%d: [%p] %s\n", i - 3, bt->addresses[i], strings[i]);
01391 }
01392
01393
01394 #undef free
01395 free(strings);
01396 } else {
01397 ast_debug(1, "Could not allocate memory for backtrace\n");
01398 }
01399 ast_bt_destroy(bt);
01400 #else
01401 ast_log(LOG_WARNING, "Must run configure with '--with-execinfo' for stack backtraces.\n");
01402 #endif
01403 }
01404
01405 void __ast_verbose_ap(const char *file, int line, const char *func, const char *fmt, va_list ap)
01406 {
01407 struct ast_str *buf = NULL;
01408 int res = 0;
01409
01410 if (!(buf = ast_str_thread_get(&verbose_buf, VERBOSE_BUF_INIT_SIZE)))
01411 return;
01412
01413 if (ast_opt_timestamp) {
01414 struct timeval now;
01415 struct ast_tm tm;
01416 char date[40];
01417 char *datefmt;
01418
01419 now = ast_tvnow();
01420 ast_localtime(&now, &tm, NULL);
01421 ast_strftime(date, sizeof(date), dateformat, &tm);
01422 datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1);
01423 sprintf(datefmt, "%c[%s] %s", 127, date, fmt);
01424 fmt = datefmt;
01425 } else {
01426 char *tmp = alloca(strlen(fmt) + 2);
01427 sprintf(tmp, "%c%s", 127, fmt);
01428 fmt = tmp;
01429 }
01430
01431
01432 res = ast_str_set_va(&buf, 0, fmt, ap);
01433
01434
01435 if (res == AST_DYNSTR_BUILD_FAILED)
01436 return;
01437
01438 ast_log(__LOG_VERBOSE, file, line, func, "%s", ast_str_buffer(buf));
01439 }
01440
01441 void __ast_verbose(const char *file, int line, const char *func, const char *fmt, ...)
01442 {
01443 va_list ap;
01444
01445 va_start(ap, fmt);
01446 __ast_verbose_ap(file, line, func, fmt, ap);
01447 va_end(ap);
01448 }
01449
01450
01451 #undef ast_verbose
01452 void __attribute__((format(printf, 1,2))) ast_verbose(const char *fmt, ...);
01453 void ast_verbose(const char *fmt, ...)
01454 {
01455 va_list ap;
01456
01457 va_start(ap, fmt);
01458 __ast_verbose_ap("", 0, "", fmt, ap);
01459 va_end(ap);
01460 }
01461
01462 int ast_register_verbose(void (*v)(const char *string))
01463 {
01464 struct verb *verb;
01465
01466 if (!(verb = ast_malloc(sizeof(*verb))))
01467 return -1;
01468
01469 verb->verboser = v;
01470
01471 AST_RWLIST_WRLOCK(&verbosers);
01472 AST_RWLIST_INSERT_HEAD(&verbosers, verb, list);
01473 AST_RWLIST_UNLOCK(&verbosers);
01474
01475 return 0;
01476 }
01477
01478 int ast_unregister_verbose(void (*v)(const char *string))
01479 {
01480 struct verb *cur;
01481
01482 AST_RWLIST_WRLOCK(&verbosers);
01483 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&verbosers, cur, list) {
01484 if (cur->verboser == v) {
01485 AST_RWLIST_REMOVE_CURRENT(list);
01486 ast_free(cur);
01487 break;
01488 }
01489 }
01490 AST_RWLIST_TRAVERSE_SAFE_END;
01491 AST_RWLIST_UNLOCK(&verbosers);
01492
01493 return cur ? 0 : -1;
01494 }
01495
01496 static void update_logchannels(void)
01497 {
01498 struct logchannel *cur;
01499
01500 AST_RWLIST_WRLOCK(&logchannels);
01501
01502 global_logmask = 0;
01503
01504 AST_RWLIST_TRAVERSE(&logchannels, cur, list) {
01505 cur->logmask = make_components(cur->components, cur->lineno);
01506 global_logmask |= cur->logmask;
01507 }
01508
01509 AST_RWLIST_UNLOCK(&logchannels);
01510 }
01511
01512 int ast_logger_register_level(const char *name)
01513 {
01514 unsigned int level;
01515 unsigned int available = 0;
01516
01517 AST_RWLIST_WRLOCK(&logchannels);
01518
01519 for (level = 0; level < ARRAY_LEN(levels); level++) {
01520 if ((level >= 16) && !available && !levels[level]) {
01521 available = level;
01522 continue;
01523 }
01524
01525 if (levels[level] && !strcasecmp(levels[level], name)) {
01526 ast_log(LOG_WARNING,
01527 "Unable to register dynamic logger level '%s': a standard logger level uses that name.\n",
01528 name);
01529 AST_RWLIST_UNLOCK(&logchannels);
01530
01531 return -1;
01532 }
01533 }
01534
01535 if (!available) {
01536 ast_log(LOG_WARNING,
01537 "Unable to register dynamic logger level '%s'; maximum number of levels registered.\n",
01538 name);
01539 AST_RWLIST_UNLOCK(&logchannels);
01540
01541 return -1;
01542 }
01543
01544 levels[available] = ast_strdup(name);
01545
01546 AST_RWLIST_UNLOCK(&logchannels);
01547
01548 ast_debug(1, "Registered dynamic logger level '%s' with index %d.\n", name, available);
01549
01550 update_logchannels();
01551
01552 return available;
01553 }
01554
01555 void ast_logger_unregister_level(const char *name)
01556 {
01557 unsigned int found = 0;
01558 unsigned int x;
01559
01560 AST_RWLIST_WRLOCK(&logchannels);
01561
01562 for (x = 16; x < ARRAY_LEN(levels); x++) {
01563 if (!levels[x]) {
01564 continue;
01565 }
01566
01567 if (strcasecmp(levels[x], name)) {
01568 continue;
01569 }
01570
01571 found = 1;
01572 break;
01573 }
01574
01575 if (found) {
01576
01577
01578
01579
01580 global_logmask &= ~(1 << x);
01581
01582 free(levels[x]);
01583 levels[x] = NULL;
01584 AST_RWLIST_UNLOCK(&logchannels);
01585
01586 ast_debug(1, "Unregistered dynamic logger level '%s' with index %d.\n", name, x);
01587
01588 update_logchannels();
01589 } else {
01590 AST_RWLIST_UNLOCK(&logchannels);
01591 }
01592 }
01593