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
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061 #include "gwlib.h"
00062 #include <limits.h>
00063 #include <stdio.h>
00064 #include <stdlib.h>
00065 #include <errno.h>
00066 #include <time.h>
00067 #include <stdarg.h>
00068 #include <string.h>
00069
00070 #ifdef HAVE_EXECINFO_H
00071 #include <execinfo.h>
00072 #endif
00073
00074 #if HAVE_SYSLOG_H
00075 #include <syslog.h>
00076 #else
00077
00078
00079
00080
00081
00082
00083 enum {
00084 LOG_PID, LOG_DAEMON, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR, LOG_ALERT
00085 };
00086
00087 static void openlog(const char *ident, int option, int facility)
00088 {
00089 }
00090
00091 static void syslog(int translog, const char *buf)
00092 {
00093 }
00094
00095 static void closelog(void)
00096 {
00097 }
00098 #endif
00099
00100
00101
00102
00103
00104 #define MAX_LOGFILES 128
00105 static struct {
00106 FILE *file;
00107 int minimum_output_level;
00108 char filename[FILENAME_MAX + 1];
00109 enum excl_state exclusive;
00110 } logfiles[MAX_LOGFILES];
00111 static int num_logfiles = 0;
00112
00113
00114
00115
00116
00117
00118 #define THREADTABLE_SIZE 1024
00119 static unsigned int thread_to[(long)THREADTABLE_SIZE];
00120
00121
00122
00123
00124
00125
00126 #define thread_slot() \
00127 (gwthread_self() % THREADTABLE_SIZE)
00128
00129
00130
00131
00132
00133 #define MAX_LOGGABLE_PLACES (10*1000)
00134 static char *loggable_places[MAX_LOGGABLE_PLACES];
00135 static int num_places = 0;
00136
00137
00138
00139
00140
00141 static RWLock rwlock;
00142
00143
00144
00145
00146 static int sysloglevel;
00147 static int dosyslog = 0;
00148
00149
00150
00151
00152
00153 static void add_stderr(void)
00154 {
00155 int i;
00156
00157 for (i = 0; i < num_logfiles; ++i)
00158 if (logfiles[i].file == stderr)
00159 return;
00160 logfiles[num_logfiles].file = stderr;
00161 logfiles[num_logfiles].minimum_output_level = GW_DEBUG;
00162 logfiles[num_logfiles].exclusive = GW_NON_EXCL;
00163 ++num_logfiles;
00164 }
00165
00166
00167 void log_init(void)
00168 {
00169 unsigned long i;
00170
00171
00172 gw_rwlock_init_static(&rwlock);
00173
00174
00175 for (i = 0; i <= THREADTABLE_SIZE; i++) {
00176 thread_to[i] = 0;
00177 }
00178
00179 add_stderr();
00180 }
00181
00182 void log_shutdown(void)
00183 {
00184 log_close_all();
00185
00186 gw_rwlock_destroy(&rwlock);
00187 }
00188
00189
00190 void log_set_output_level(enum output_level level)
00191 {
00192 int i;
00193
00194 for (i = 0; i < num_logfiles; ++i) {
00195 if (logfiles[i].file == stderr) {
00196 logfiles[i].minimum_output_level = level;
00197 break;
00198 }
00199 }
00200 }
00201
00202 void log_set_log_level(enum output_level level)
00203 {
00204 int i;
00205
00206
00207 for (i = 0; i < num_logfiles; ++i) {
00208 if (logfiles[i].file != stderr) {
00209 logfiles[i].minimum_output_level = level;
00210 info(0, "Changed logfile `%s' to level `%d'.", logfiles[i].filename, level);
00211 }
00212 }
00213 }
00214
00215
00216 void log_set_syslog(const char *ident, int syslog_level)
00217 {
00218 if (ident == NULL)
00219 dosyslog = 0;
00220 else {
00221 dosyslog = 1;
00222 sysloglevel = syslog_level;
00223 openlog(ident, LOG_PID, LOG_DAEMON);
00224 debug("gwlib.log", 0, "Syslog logging enabled.");
00225 }
00226 }
00227
00228
00229 void log_reopen(void)
00230 {
00231 int i, j, found;
00232
00233
00234
00235
00236 gw_rwlock_wrlock(&rwlock);
00237
00238 for (i = 0; i < num_logfiles; ++i) {
00239 if (logfiles[i].file != stderr) {
00240 found = 0;
00241
00242
00243
00244
00245
00246
00247
00248 for (j = i-1; j >= 0 && found == 0; j--) {
00249 if (strcmp(logfiles[i].filename, logfiles[j].filename) == 0) {
00250 logfiles[i].file = logfiles[j].file;
00251 found = 1;
00252 }
00253 }
00254 if (found)
00255 continue;
00256 if (logfiles[i].file != NULL)
00257 fclose(logfiles[i].file);
00258 logfiles[i].file = fopen(logfiles[i].filename, "a");
00259 if (logfiles[i].file == NULL) {
00260 error(errno, "Couldn't re-open logfile `%s'.",
00261 logfiles[i].filename);
00262 }
00263 }
00264 }
00265
00266
00267
00268
00269 gw_rwlock_unlock(&rwlock);
00270 }
00271
00272
00273 void log_close_all(void)
00274 {
00275
00276
00277
00278 gw_rwlock_wrlock(&rwlock);
00279
00280 while (num_logfiles > 0) {
00281 --num_logfiles;
00282 if (logfiles[num_logfiles].file != stderr &&
00283 logfiles[num_logfiles].file != NULL)
00284 fclose(logfiles[num_logfiles].file);
00285 logfiles[num_logfiles].file = NULL;
00286 }
00287
00288
00289
00290
00291 gw_rwlock_unlock(&rwlock);
00292
00293
00294 if (dosyslog) {
00295 closelog();
00296 dosyslog = 0;
00297 }
00298 }
00299
00300
00301 int log_open(char *filename, int level, enum excl_state excl)
00302 {
00303 FILE *f = NULL;
00304 int i;
00305
00306 gw_rwlock_wrlock(&rwlock);
00307
00308 if (num_logfiles == MAX_LOGFILES) {
00309 gw_rwlock_unlock(&rwlock);
00310 error(0, "Too many log files already open, not adding `%s'",
00311 filename);
00312 return -1;
00313 }
00314
00315 if (strlen(filename) > FILENAME_MAX) {
00316 gw_rwlock_unlock(&rwlock);
00317 error(0, "Log filename too long: `%s'.", filename);
00318 return -1;
00319 }
00320
00321
00322
00323
00324
00325
00326 for (i = 0; i < num_logfiles && f == NULL; ++i) {
00327 if (strcmp(logfiles[i].filename, filename) == 0)
00328 f = logfiles[i].file;
00329 }
00330
00331
00332 if (f == NULL) {
00333 f = fopen(filename, "a");
00334 if (f == NULL) {
00335 gw_rwlock_unlock(&rwlock);
00336 error(errno, "Couldn't open logfile `%s'.", filename);
00337 return -1;
00338 }
00339 }
00340
00341 logfiles[num_logfiles].file = f;
00342 logfiles[num_logfiles].minimum_output_level = level;
00343 logfiles[num_logfiles].exclusive = excl;
00344 strcpy(logfiles[num_logfiles].filename, filename);
00345 ++num_logfiles;
00346 i = num_logfiles - 1;
00347 gw_rwlock_unlock(&rwlock);
00348
00349 info(0, "Added logfile `%s' with level `%d'.", filename, level);
00350
00351 return i;
00352 }
00353
00354
00355 #define FORMAT_SIZE (1024)
00356 static void format(char *buf, int level, const char *place, int e,
00357 const char *fmt, int with_timestamp)
00358 {
00359 static char *tab[] = {
00360 "DEBUG: ",
00361 "INFO: ",
00362 "WARNING: ",
00363 "ERROR: ",
00364 "PANIC: ",
00365 "LOG: "
00366 };
00367 static int tab_size = sizeof(tab) / sizeof(tab[0]);
00368 time_t t;
00369 struct tm tm;
00370 char *p, prefix[1024];
00371 long tid, pid;
00372
00373 p = prefix;
00374
00375 if (with_timestamp) {
00376 time(&t);
00377 #if LOG_TIMESTAMP_LOCALTIME
00378 tm = gw_localtime(t);
00379 #else
00380 tm = gw_gmtime(t);
00381 #endif
00382 sprintf(p, "%04d-%02d-%02d %02d:%02d:%02d ",
00383 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
00384 tm.tm_hour, tm.tm_min, tm.tm_sec);
00385
00386 p = strchr(p, '\0');
00387 }
00388
00389 gwthread_self_ids(&tid, &pid);
00390 sprintf(p, "[%ld] [%ld] ", pid, tid);
00391
00392 p = strchr(p, '\0');
00393 if (level < 0 || level >= tab_size)
00394 sprintf(p, "UNKNOWN: ");
00395 else
00396 sprintf(p, "%s", tab[level]);
00397
00398 p = strchr(p, '\0');
00399 if (place != NULL && *place != '\0')
00400 sprintf(p, "%s: ", place);
00401
00402 if (strlen(prefix) + strlen(fmt) > FORMAT_SIZE / 2) {
00403 sprintf(buf, "%s <OUTPUT message too long>\n", prefix);
00404 return;
00405 }
00406
00407 if (e == 0)
00408 sprintf(buf, "%s%s\n", prefix, fmt);
00409 else
00410 sprintf(buf, "%s%s\n%sSystem error %d: %s\n",
00411 prefix, fmt, prefix, e, strerror(e));
00412 }
00413
00414
00415 static void PRINTFLIKE(2,0) output(FILE *f, char *buf, va_list args)
00416 {
00417 vfprintf(f, buf, args);
00418 fflush(f);
00419 }
00420
00421
00422 static void PRINTFLIKE(1,0) kannel_syslog(char *format, va_list args, int level)
00423 {
00424 char buf[4096];
00425 int translog;
00426
00427 if (level >= sysloglevel && dosyslog) {
00428 if (args == NULL) {
00429 strncpy(buf, format, sizeof(buf));
00430 buf[sizeof(buf) - 1] = '\0';
00431 } else {
00432 vsnprintf(buf, sizeof(buf), format, args);
00433
00434 }
00435
00436 switch(level) {
00437 case GW_DEBUG:
00438 translog = LOG_DEBUG;
00439 break;
00440 case GW_INFO:
00441 translog = LOG_INFO;
00442 break;
00443 case GW_WARNING:
00444 translog = LOG_WARNING;
00445 break;
00446 case GW_ERROR:
00447 translog = LOG_ERR;
00448 break;
00449 case GW_PANIC:
00450 translog = LOG_ALERT;
00451 break;
00452 default:
00453 translog = LOG_INFO;
00454 break;
00455 }
00456 syslog(translog, "%s", buf);
00457 }
00458 }
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470 #define FUNCTION_GUTS(level, place) \
00471 do { \
00472 int i; \
00473 char buf[FORMAT_SIZE]; \
00474 va_list args; \
00475 \
00476 format(buf, level, place, err, fmt, 1); \
00477 gw_rwlock_rdlock(&rwlock); \
00478 for (i = 0; i < num_logfiles; ++i) { \
00479 if (logfiles[i].exclusive == GW_NON_EXCL && \
00480 level >= logfiles[i].minimum_output_level && \
00481 logfiles[i].file != NULL) { \
00482 va_start(args, fmt); \
00483 output(logfiles[i].file, buf, args); \
00484 va_end(args); \
00485 } \
00486 } \
00487 gw_rwlock_unlock(&rwlock); \
00488 if (dosyslog) { \
00489 format(buf, level, place, err, fmt, 0); \
00490 va_start(args, fmt); \
00491 kannel_syslog(buf,args,level); \
00492 va_end(args); \
00493 } \
00494 } while (0)
00495
00496 #define FUNCTION_GUTS_EXCL(level, place) \
00497 do { \
00498 char buf[FORMAT_SIZE]; \
00499 va_list args; \
00500 \
00501 format(buf, level, place, err, fmt, 1); \
00502 gw_rwlock_rdlock(&rwlock); \
00503 if (logfiles[e].exclusive == GW_EXCL && \
00504 level >= logfiles[e].minimum_output_level && \
00505 logfiles[e].file != NULL) { \
00506 va_start(args, fmt); \
00507 output(logfiles[e].file, buf, args); \
00508 va_end(args); \
00509 } \
00510 gw_rwlock_unlock(&rwlock); \
00511 } while (0)
00512
00513
00514 #ifdef HAVE_BACKTRACE
00515 static void PRINTFLIKE(2,3) gw_panic_output(int err, const char *fmt, ...)
00516 {
00517 FUNCTION_GUTS(GW_PANIC, "");
00518 }
00519 #endif
00520
00521 void gw_panic(int err, const char *fmt, ...)
00522 {
00523
00524
00525
00526
00527 FUNCTION_GUTS(GW_PANIC, "");
00528
00529 #ifdef HAVE_BACKTRACE
00530 {
00531 void *stack_frames[50];
00532 size_t size, i;
00533 char **strings;
00534
00535 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
00536 strings = backtrace_symbols(stack_frames, size);
00537
00538 if (strings) {
00539 for (i = 0; i < size; i++)
00540 gw_panic_output(0, "%s", strings[i]);
00541 }
00542 else {
00543 for (i = 0; i < size; i++)
00544 gw_panic_output(0, "%p", stack_frames[i]);
00545 }
00546
00547
00548
00549
00550
00551
00552 }
00553 #endif
00554
00555 #ifdef SEGFAULT_PANIC
00556 *((char*)0) = 0;
00557 #endif
00558
00559 exit(EXIT_FAILURE);
00560 }
00561
00562
00563 void error(int err, const char *fmt, ...)
00564 {
00565 int e;
00566
00567 if ((e = thread_to[thread_slot()])) {
00568 FUNCTION_GUTS_EXCL(GW_ERROR, "");
00569 } else {
00570 FUNCTION_GUTS(GW_ERROR, "");
00571 }
00572 }
00573
00574
00575 void warning(int err, const char *fmt, ...)
00576 {
00577 int e;
00578
00579 if ((e = thread_to[thread_slot()])) {
00580 FUNCTION_GUTS_EXCL(GW_WARNING, "");
00581 } else {
00582 FUNCTION_GUTS(GW_WARNING, "");
00583 }
00584 }
00585
00586
00587 void info(int err, const char *fmt, ...)
00588 {
00589 int e;
00590
00591 if ((e = thread_to[thread_slot()])) {
00592 FUNCTION_GUTS_EXCL(GW_INFO, "");
00593 } else {
00594 FUNCTION_GUTS(GW_INFO, "");
00595 }
00596 }
00597
00598
00599 static int place_matches(const char *place, const char *pat)
00600 {
00601 size_t len;
00602
00603 len = strlen(pat);
00604 if (pat[len-1] == '*')
00605 return (strncasecmp(place, pat, len - 1) == 0);
00606 return (strcasecmp(place, pat) == 0);
00607 }
00608
00609
00610 static int place_should_be_logged(const char *place)
00611 {
00612 int i;
00613
00614 if (num_places == 0)
00615 return 1;
00616 for (i = 0; i < num_places; ++i) {
00617 if (*loggable_places[i] != '-' &&
00618 place_matches(place, loggable_places[i]))
00619 return 1;
00620 }
00621 return 0;
00622 }
00623
00624
00625 static int place_is_not_logged(const char *place)
00626 {
00627 int i;
00628
00629 if (num_places == 0)
00630 return 0;
00631 for (i = 0; i < num_places; ++i) {
00632 if (*loggable_places[i] == '-' &&
00633 place_matches(place, loggable_places[i]+1))
00634 return 1;
00635 }
00636 return 0;
00637 }
00638
00639
00640 void debug(const char *place, int err, const char *fmt, ...)
00641 {
00642 int e;
00643
00644 if (place_should_be_logged(place) && place_is_not_logged(place) == 0) {
00645
00646
00647
00648
00649
00650
00651 if ((e = thread_to[thread_slot()])) {
00652 FUNCTION_GUTS_EXCL(GW_DEBUG, "");
00653 } else {
00654 FUNCTION_GUTS(GW_DEBUG, "");
00655 }
00656 }
00657 }
00658
00659
00660 void log_set_debug_places(const char *places)
00661 {
00662 char *p;
00663
00664 p = strtok(gw_strdup(places), " ,");
00665 num_places = 0;
00666 while (p != NULL && num_places < MAX_LOGGABLE_PLACES) {
00667 loggable_places[num_places++] = p;
00668 p = strtok(NULL, " ,");
00669 }
00670 }
00671
00672
00673 void log_thread_to(unsigned int idx)
00674 {
00675 long thread_id = thread_slot();
00676
00677 if (idx > 0)
00678 info(0, "Logging thread `%ld' to logfile `%s' with level `%d'.",
00679 thread_id, logfiles[idx].filename, logfiles[idx].minimum_output_level);
00680 thread_to[thread_id] = idx;
00681 }
00682
See file LICENSE for details about the license agreement for using,
modifying, copying or deriving work from this software.