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: 307092 $")
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 (level != __LOG_VERBOSE && AST_RWLIST_EMPTY(&logchannels)) {
01116
01117
01118
01119
01120 int result;
01121 va_start(ap, fmt);
01122 result = ast_str_set_va(&buf, BUFSIZ, fmt, ap);
01123 va_end(ap);
01124 if (result != AST_DYNSTR_BUILD_FAILED) {
01125 term_filter_escapes(ast_str_buffer(buf));
01126 fputs(ast_str_buffer(buf), stdout);
01127 }
01128 return;
01129 }
01130
01131
01132
01133
01134
01135
01136
01137 if (!option_verbose && !option_debug && (level == __LOG_DEBUG))
01138 return;
01139
01140
01141 if (level != __LOG_VERBOSE && !(global_logmask & (1 << level)))
01142 return;
01143
01144
01145 va_start(ap, fmt);
01146 res = ast_str_set_va(&buf, BUFSIZ, fmt, ap);
01147 va_end(ap);
01148
01149
01150 if (res == AST_DYNSTR_BUILD_FAILED)
01151 return;
01152
01153
01154 if (!(logmsg = ast_calloc_with_stringfields(1, struct logmsg, res + 128)))
01155 return;
01156
01157
01158 ast_string_field_set(logmsg, message, ast_str_buffer(buf));
01159
01160
01161 if (level == __LOG_VERBOSE) {
01162 logmsg->type = LOGMSG_VERBOSE;
01163 } else {
01164 logmsg->type = LOGMSG_NORMAL;
01165 }
01166
01167
01168 ast_localtime(&now, &tm, NULL);
01169 ast_strftime(datestring, sizeof(datestring), dateformat, &tm);
01170 ast_string_field_set(logmsg, date, datestring);
01171
01172
01173 logmsg->level = level;
01174 logmsg->line = line;
01175 ast_string_field_set(logmsg, level_name, levels[level]);
01176 ast_string_field_set(logmsg, file, file);
01177 ast_string_field_set(logmsg, function, function);
01178 logmsg->process_id = (long) GETTID();
01179
01180
01181 if (logthread != AST_PTHREADT_NULL) {
01182 AST_LIST_LOCK(&logmsgs);
01183 AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
01184 ast_cond_signal(&logcond);
01185 AST_LIST_UNLOCK(&logmsgs);
01186 } else {
01187 logger_print_normal(logmsg);
01188 ast_free(logmsg);
01189 }
01190
01191 return;
01192 }
01193
01194 #ifdef HAVE_BKTR
01195
01196 struct ast_bt *ast_bt_create(void)
01197 {
01198 struct ast_bt *bt = ast_calloc(1, sizeof(*bt));
01199 if (!bt) {
01200 ast_log(LOG_ERROR, "Unable to allocate memory for backtrace structure!\n");
01201 return NULL;
01202 }
01203
01204 bt->alloced = 1;
01205
01206 ast_bt_get_addresses(bt);
01207
01208 return bt;
01209 }
01210
01211 int ast_bt_get_addresses(struct ast_bt *bt)
01212 {
01213 bt->num_frames = backtrace(bt->addresses, AST_MAX_BT_FRAMES);
01214
01215 return 0;
01216 }
01217
01218 void *ast_bt_destroy(struct ast_bt *bt)
01219 {
01220 if (bt->alloced) {
01221 ast_free(bt);
01222 }
01223
01224 return NULL;
01225 }
01226
01227 char **ast_bt_get_symbols(void **addresses, size_t num_frames)
01228 {
01229 char **strings = NULL;
01230 #if defined(BETTER_BACKTRACES)
01231 int stackfr;
01232 bfd *bfdobj;
01233 Dl_info dli;
01234 long allocsize;
01235 asymbol **syms = NULL;
01236 bfd_vma offset;
01237 const char *lastslash;
01238 asection *section;
01239 const char *file, *func;
01240 unsigned int line;
01241 char address_str[128];
01242 char msg[1024];
01243 size_t strings_size;
01244 size_t *eachlen;
01245 #endif
01246
01247 #if defined(BETTER_BACKTRACES)
01248 strings_size = num_frames * sizeof(*strings);
01249 eachlen = ast_calloc(num_frames, sizeof(*eachlen));
01250
01251 if (!(strings = ast_calloc(num_frames, sizeof(*strings)))) {
01252 return NULL;
01253 }
01254
01255 for (stackfr = 0; stackfr < num_frames; stackfr++) {
01256 int found = 0, symbolcount;
01257
01258 msg[0] = '\0';
01259
01260 if (!dladdr(addresses[stackfr], &dli)) {
01261 continue;
01262 }
01263
01264 if (strcmp(dli.dli_fname, "asterisk") == 0) {
01265 char asteriskpath[256];
01266 if (!(dli.dli_fname = ast_utils_which("asterisk", asteriskpath, sizeof(asteriskpath)))) {
01267
01268 ast_log(LOG_DEBUG, "Failed to find asterisk binary for debug symbols.\n");
01269 dli.dli_fname = "asterisk";
01270 }
01271 }
01272
01273 lastslash = strrchr(dli.dli_fname, '/');
01274 if ( (bfdobj = bfd_openr(dli.dli_fname, NULL)) &&
01275 bfd_check_format(bfdobj, bfd_object) &&
01276 (allocsize = bfd_get_symtab_upper_bound(bfdobj)) > 0 &&
01277 (syms = ast_malloc(allocsize)) &&
01278 (symbolcount = bfd_canonicalize_symtab(bfdobj, syms))) {
01279
01280 if (bfdobj->flags & DYNAMIC) {
01281 offset = addresses[stackfr] - dli.dli_fbase;
01282 } else {
01283 offset = addresses[stackfr] - (void *) 0;
01284 }
01285
01286 for (section = bfdobj->sections; section; section = section->next) {
01287 if ( !bfd_get_section_flags(bfdobj, section) & SEC_ALLOC ||
01288 section->vma > offset ||
01289 section->size + section->vma < offset) {
01290 continue;
01291 }
01292
01293 if (!bfd_find_nearest_line(bfdobj, section, syms, offset - section->vma, &file, &func, &line)) {
01294 continue;
01295 }
01296
01297
01298 found++;
01299 if ((lastslash = strrchr(file, '/'))) {
01300 const char *prevslash;
01301 for (prevslash = lastslash - 1; *prevslash != '/' && prevslash >= file; prevslash--);
01302 if (prevslash >= file) {
01303 lastslash = prevslash;
01304 }
01305 }
01306 if (dli.dli_saddr == NULL) {
01307 address_str[0] = '\0';
01308 } else {
01309 snprintf(address_str, sizeof(address_str), " (%p+%lX)",
01310 dli.dli_saddr,
01311 (unsigned long) (addresses[stackfr] - dli.dli_saddr));
01312 }
01313 snprintf(msg, sizeof(msg), "%s:%u %s()%s",
01314 lastslash ? lastslash + 1 : file, line,
01315 S_OR(func, "???"),
01316 address_str);
01317
01318 break;
01319 }
01320 }
01321 if (bfdobj) {
01322 bfd_close(bfdobj);
01323 if (syms) {
01324 ast_free(syms);
01325 }
01326 }
01327
01328
01329 if (!found) {
01330 if (dli.dli_saddr == NULL) {
01331 address_str[0] = '\0';
01332 } else {
01333 snprintf(address_str, sizeof(address_str), " (%p+%lX)",
01334 dli.dli_saddr,
01335 (unsigned long) (addresses[stackfr] - dli.dli_saddr));
01336 }
01337 snprintf(msg, sizeof(msg), "%s %s()%s",
01338 lastslash ? lastslash + 1 : dli.dli_fname,
01339 S_OR(dli.dli_sname, "<unknown>"),
01340 address_str);
01341 }
01342
01343 if (!ast_strlen_zero(msg)) {
01344 char **tmp;
01345 eachlen[stackfr] = strlen(msg);
01346 if (!(tmp = ast_realloc(strings, strings_size + eachlen[stackfr] + 1))) {
01347 ast_free(strings);
01348 strings = NULL;
01349 break;
01350 }
01351 strings = tmp;
01352 strings[stackfr] = (char *) strings + strings_size;
01353 ast_copy_string(strings[stackfr], msg, eachlen[stackfr] + 1);
01354 strings_size += eachlen[stackfr] + 1;
01355 }
01356 }
01357
01358 if (strings) {
01359
01360 strings[0] = (char *) strings + num_frames * sizeof(*strings);
01361 for (stackfr = 1; stackfr < num_frames; stackfr++) {
01362 strings[stackfr] = strings[stackfr - 1] + eachlen[stackfr - 1] + 1;
01363 }
01364 }
01365 #else
01366 strings = backtrace_symbols(addresses, num_frames);
01367 #endif
01368 return strings;
01369 }
01370
01371 #endif
01372
01373 void ast_backtrace(void)
01374 {
01375 #ifdef HAVE_BKTR
01376 struct ast_bt *bt;
01377 int i = 0;
01378 char **strings;
01379
01380 if (!(bt = ast_bt_create())) {
01381 ast_log(LOG_WARNING, "Unable to allocate space for backtrace structure\n");
01382 return;
01383 }
01384
01385 if ((strings = ast_bt_get_symbols(bt->addresses, bt->num_frames))) {
01386 ast_debug(1, "Got %d backtrace record%c\n", bt->num_frames, bt->num_frames != 1 ? 's' : ' ');
01387 for (i = 3; i < bt->num_frames - 2; i++) {
01388 ast_log(LOG_DEBUG, "#%d: [%p] %s\n", i - 3, bt->addresses[i], strings[i]);
01389 }
01390
01391
01392 #undef free
01393 free(strings);
01394 } else {
01395 ast_debug(1, "Could not allocate memory for backtrace\n");
01396 }
01397 ast_bt_destroy(bt);
01398 #else
01399 ast_log(LOG_WARNING, "Must run configure with '--with-execinfo' for stack backtraces.\n");
01400 #endif
01401 }
01402
01403 void __ast_verbose_ap(const char *file, int line, const char *func, const char *fmt, va_list ap)
01404 {
01405 struct ast_str *buf = NULL;
01406 int res = 0;
01407
01408 if (!(buf = ast_str_thread_get(&verbose_buf, VERBOSE_BUF_INIT_SIZE)))
01409 return;
01410
01411 if (ast_opt_timestamp) {
01412 struct timeval now;
01413 struct ast_tm tm;
01414 char date[40];
01415 char *datefmt;
01416
01417 now = ast_tvnow();
01418 ast_localtime(&now, &tm, NULL);
01419 ast_strftime(date, sizeof(date), dateformat, &tm);
01420 datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1);
01421 sprintf(datefmt, "%c[%s] %s", 127, date, fmt);
01422 fmt = datefmt;
01423 } else {
01424 char *tmp = alloca(strlen(fmt) + 2);
01425 sprintf(tmp, "%c%s", 127, fmt);
01426 fmt = tmp;
01427 }
01428
01429
01430 res = ast_str_set_va(&buf, 0, fmt, ap);
01431
01432
01433 if (res == AST_DYNSTR_BUILD_FAILED)
01434 return;
01435
01436 ast_log(__LOG_VERBOSE, file, line, func, "%s", ast_str_buffer(buf));
01437 }
01438
01439 void __ast_verbose(const char *file, int line, const char *func, const char *fmt, ...)
01440 {
01441 va_list ap;
01442
01443 va_start(ap, fmt);
01444 __ast_verbose_ap(file, line, func, fmt, ap);
01445 va_end(ap);
01446 }
01447
01448
01449 #undef ast_verbose
01450 void __attribute__((format(printf, 1,2))) ast_verbose(const char *fmt, ...);
01451 void ast_verbose(const char *fmt, ...)
01452 {
01453 va_list ap;
01454
01455 va_start(ap, fmt);
01456 __ast_verbose_ap("", 0, "", fmt, ap);
01457 va_end(ap);
01458 }
01459
01460 int ast_register_verbose(void (*v)(const char *string))
01461 {
01462 struct verb *verb;
01463
01464 if (!(verb = ast_malloc(sizeof(*verb))))
01465 return -1;
01466
01467 verb->verboser = v;
01468
01469 AST_RWLIST_WRLOCK(&verbosers);
01470 AST_RWLIST_INSERT_HEAD(&verbosers, verb, list);
01471 AST_RWLIST_UNLOCK(&verbosers);
01472
01473 return 0;
01474 }
01475
01476 int ast_unregister_verbose(void (*v)(const char *string))
01477 {
01478 struct verb *cur;
01479
01480 AST_RWLIST_WRLOCK(&verbosers);
01481 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&verbosers, cur, list) {
01482 if (cur->verboser == v) {
01483 AST_RWLIST_REMOVE_CURRENT(list);
01484 ast_free(cur);
01485 break;
01486 }
01487 }
01488 AST_RWLIST_TRAVERSE_SAFE_END;
01489 AST_RWLIST_UNLOCK(&verbosers);
01490
01491 return cur ? 0 : -1;
01492 }
01493
01494 static void update_logchannels(void)
01495 {
01496 struct logchannel *cur;
01497
01498 AST_RWLIST_WRLOCK(&logchannels);
01499
01500 global_logmask = 0;
01501
01502 AST_RWLIST_TRAVERSE(&logchannels, cur, list) {
01503 cur->logmask = make_components(cur->components, cur->lineno);
01504 global_logmask |= cur->logmask;
01505 }
01506
01507 AST_RWLIST_UNLOCK(&logchannels);
01508 }
01509
01510 int ast_logger_register_level(const char *name)
01511 {
01512 unsigned int level;
01513 unsigned int available = 0;
01514
01515 AST_RWLIST_WRLOCK(&logchannels);
01516
01517 for (level = 0; level < ARRAY_LEN(levels); level++) {
01518 if ((level >= 16) && !available && !levels[level]) {
01519 available = level;
01520 continue;
01521 }
01522
01523 if (levels[level] && !strcasecmp(levels[level], name)) {
01524 ast_log(LOG_WARNING,
01525 "Unable to register dynamic logger level '%s': a standard logger level uses that name.\n",
01526 name);
01527 AST_RWLIST_UNLOCK(&logchannels);
01528
01529 return -1;
01530 }
01531 }
01532
01533 if (!available) {
01534 ast_log(LOG_WARNING,
01535 "Unable to register dynamic logger level '%s'; maximum number of levels registered.\n",
01536 name);
01537 AST_RWLIST_UNLOCK(&logchannels);
01538
01539 return -1;
01540 }
01541
01542 levels[available] = ast_strdup(name);
01543
01544 AST_RWLIST_UNLOCK(&logchannels);
01545
01546 ast_debug(1, "Registered dynamic logger level '%s' with index %d.\n", name, available);
01547
01548 update_logchannels();
01549
01550 return available;
01551 }
01552
01553 void ast_logger_unregister_level(const char *name)
01554 {
01555 unsigned int found = 0;
01556 unsigned int x;
01557
01558 AST_RWLIST_WRLOCK(&logchannels);
01559
01560 for (x = 16; x < ARRAY_LEN(levels); x++) {
01561 if (!levels[x]) {
01562 continue;
01563 }
01564
01565 if (strcasecmp(levels[x], name)) {
01566 continue;
01567 }
01568
01569 found = 1;
01570 break;
01571 }
01572
01573 if (found) {
01574
01575
01576
01577
01578 global_logmask &= ~(1 << x);
01579
01580 free(levels[x]);
01581 levels[x] = NULL;
01582 AST_RWLIST_UNLOCK(&logchannels);
01583
01584 ast_debug(1, "Unregistered dynamic logger level '%s' with index %d.\n", name, x);
01585
01586 update_logchannels();
01587 } else {
01588 AST_RWLIST_UNLOCK(&logchannels);
01589 }
01590 }
01591