Return to log.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / lib / isc |
1.1 ! misho 1: /* ! 2: * Copyright (C) 2004-2007, 2009 Internet Systems Consortium, Inc. ("ISC") ! 3: * Copyright (C) 1999-2003 Internet Software Consortium. ! 4: * ! 5: * Permission to use, copy, modify, and/or distribute this software for any ! 6: * purpose with or without fee is hereby granted, provided that the above ! 7: * copyright notice and this permission notice appear in all copies. ! 8: * ! 9: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH ! 10: * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY ! 11: * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, ! 12: * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM ! 13: * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE ! 14: * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ! 15: * PERFORMANCE OF THIS SOFTWARE. ! 16: */ ! 17: ! 18: /* $Id: log.c,v 1.94.332.5 2009/02/16 02:04:05 marka Exp $ */ ! 19: ! 20: /*! \file ! 21: * \author Principal Authors: DCL */ ! 22: ! 23: #include <config.h> ! 24: ! 25: #include <errno.h> ! 26: #include <stdlib.h> ! 27: #include <limits.h> ! 28: #include <time.h> ! 29: ! 30: #include <sys/types.h> /* dev_t FreeBSD 2.1 */ ! 31: ! 32: #include <isc/dir.h> ! 33: #include <isc/file.h> ! 34: #include <isc/log.h> ! 35: #include <isc/magic.h> ! 36: #include <isc/mem.h> ! 37: #include <isc/msgs.h> ! 38: #include <isc/print.h> ! 39: #include <isc/stat.h> ! 40: #include <isc/stdio.h> ! 41: #include <isc/string.h> ! 42: #include <isc/time.h> ! 43: #include <isc/util.h> ! 44: ! 45: #define LCTX_MAGIC ISC_MAGIC('L', 'c', 't', 'x') ! 46: #define VALID_CONTEXT(lctx) ISC_MAGIC_VALID(lctx, LCTX_MAGIC) ! 47: ! 48: #define LCFG_MAGIC ISC_MAGIC('L', 'c', 'f', 'g') ! 49: #define VALID_CONFIG(lcfg) ISC_MAGIC_VALID(lcfg, LCFG_MAGIC) ! 50: ! 51: /* ! 52: * XXXDCL make dynamic? ! 53: */ ! 54: #define LOG_BUFFER_SIZE (8 * 1024) ! 55: ! 56: #ifndef PATH_MAX ! 57: #define PATH_MAX 1024 /* AIX and others don't define this. */ ! 58: #endif ! 59: ! 60: /*! ! 61: * This is the structure that holds each named channel. A simple linked ! 62: * list chains all of the channels together, so an individual channel is ! 63: * found by doing strcmp()s with the names down the list. Their should ! 64: * be no performance penalty from this as it is expected that the number ! 65: * of named channels will be no more than a dozen or so, and name lookups ! 66: * from the head of the list are only done when isc_log_usechannel() is ! 67: * called, which should also be very infrequent. ! 68: */ ! 69: typedef struct isc_logchannel isc_logchannel_t; ! 70: ! 71: struct isc_logchannel { ! 72: char * name; ! 73: unsigned int type; ! 74: int level; ! 75: unsigned int flags; ! 76: isc_logdestination_t destination; ! 77: ISC_LINK(isc_logchannel_t) link; ! 78: }; ! 79: ! 80: /*! ! 81: * The logchannellist structure associates categories and modules with ! 82: * channels. First the appropriate channellist is found based on the ! 83: * category, and then each structure in the linked list is checked for ! 84: * a matching module. It is expected that the number of channels ! 85: * associated with any given category will be very short, no more than ! 86: * three or four in the more unusual cases. ! 87: */ ! 88: typedef struct isc_logchannellist isc_logchannellist_t; ! 89: ! 90: struct isc_logchannellist { ! 91: const isc_logmodule_t * module; ! 92: isc_logchannel_t * channel; ! 93: ISC_LINK(isc_logchannellist_t) link; ! 94: }; ! 95: ! 96: /*! ! 97: * This structure is used to remember messages for pruning via ! 98: * isc_log_[v]write1(). ! 99: */ ! 100: typedef struct isc_logmessage isc_logmessage_t; ! 101: ! 102: struct isc_logmessage { ! 103: char * text; ! 104: isc_time_t time; ! 105: ISC_LINK(isc_logmessage_t) link; ! 106: }; ! 107: ! 108: /*! ! 109: * The isc_logconfig structure is used to store the configurable information ! 110: * about where messages are actually supposed to be sent -- the information ! 111: * that could changed based on some configuration file, as opposed to the ! 112: * the category/module specification of isc_log_[v]write[1] that is compiled ! 113: * into a program, or the debug_level which is dynamic state information. ! 114: */ ! 115: struct isc_logconfig { ! 116: unsigned int magic; ! 117: isc_log_t * lctx; ! 118: ISC_LIST(isc_logchannel_t) channels; ! 119: ISC_LIST(isc_logchannellist_t) *channellists; ! 120: unsigned int channellist_count; ! 121: unsigned int duplicate_interval; ! 122: int highest_level; ! 123: char * tag; ! 124: isc_boolean_t dynamic; ! 125: }; ! 126: ! 127: /*! ! 128: * This isc_log structure provides the context for the isc_log functions. ! 129: * The log context locks itself in isc_log_doit, the internal backend to ! 130: * isc_log_write. The locking is necessary both to provide exclusive access ! 131: * to the buffer into which the message is formatted and to guard against ! 132: * competing threads trying to write to the same syslog resource. (On ! 133: * some systems, such as BSD/OS, stdio is thread safe but syslog is not.) ! 134: * Unfortunately, the lock cannot guard against a _different_ logging ! 135: * context in the same program competing for syslog's attention. Thus ! 136: * There Can Be Only One, but this is not enforced. ! 137: * XXXDCL enforce it? ! 138: * ! 139: * Note that the category and module information is not locked. ! 140: * This is because in the usual case, only one isc_log_t is ever created ! 141: * in a program, and the category/module registration happens only once. ! 142: * XXXDCL it might be wise to add more locking overall. ! 143: */ ! 144: struct isc_log { ! 145: /* Not locked. */ ! 146: unsigned int magic; ! 147: isc_mem_t * mctx; ! 148: isc_logcategory_t * categories; ! 149: unsigned int category_count; ! 150: isc_logmodule_t * modules; ! 151: unsigned int module_count; ! 152: int debug_level; ! 153: isc_mutex_t lock; ! 154: /* Locked by isc_log lock. */ ! 155: isc_logconfig_t * logconfig; ! 156: char buffer[LOG_BUFFER_SIZE]; ! 157: ISC_LIST(isc_logmessage_t) messages; ! 158: }; ! 159: ! 160: /*! ! 161: * Used when ISC_LOG_PRINTLEVEL is enabled for a channel. ! 162: */ ! 163: static const char *log_level_strings[] = { ! 164: "debug", ! 165: "info", ! 166: "notice", ! 167: "warning", ! 168: "error", ! 169: "critical" ! 170: }; ! 171: ! 172: /*! ! 173: * Used to convert ISC_LOG_* priorities into syslog priorities. ! 174: * XXXDCL This will need modification for NT. ! 175: */ ! 176: static const int syslog_map[] = { ! 177: LOG_DEBUG, ! 178: LOG_INFO, ! 179: LOG_NOTICE, ! 180: LOG_WARNING, ! 181: LOG_ERR, ! 182: LOG_CRIT ! 183: }; ! 184: ! 185: /*! ! 186: * When adding new categories, a corresponding ISC_LOGCATEGORY_foo ! 187: * definition needs to be added to <isc/log.h>. ! 188: * ! 189: * The default category is provided so that the internal default can ! 190: * be overridden. Since the default is always looked up as the first ! 191: * channellist in the log context, it must come first in isc_categories[]. ! 192: */ ! 193: LIBISC_EXTERNAL_DATA isc_logcategory_t isc_categories[] = { ! 194: { "default", 0 }, /* "default" must come first. */ ! 195: { "general", 0 }, ! 196: { NULL, 0 } ! 197: }; ! 198: ! 199: /*! ! 200: * See above comment for categories on LIBISC_EXTERNAL_DATA, and apply it to modules. ! 201: */ ! 202: LIBISC_EXTERNAL_DATA isc_logmodule_t isc_modules[] = { ! 203: { "socket", 0 }, ! 204: { "time", 0 }, ! 205: { "interface", 0 }, ! 206: { "timer", 0 }, ! 207: { "file", 0 }, ! 208: { NULL, 0 } ! 209: }; ! 210: ! 211: /*! ! 212: * This essentially constant structure must be filled in at run time, ! 213: * because its channel member is pointed to a channel that is created ! 214: * dynamically with isc_log_createchannel. ! 215: */ ! 216: static isc_logchannellist_t default_channel; ! 217: ! 218: /*! ! 219: * libisc logs to this context. ! 220: */ ! 221: LIBISC_EXTERNAL_DATA isc_log_t *isc_lctx = NULL; ! 222: ! 223: /*! ! 224: * Forward declarations. ! 225: */ ! 226: static isc_result_t ! 227: assignchannel(isc_logconfig_t *lcfg, unsigned int category_id, ! 228: const isc_logmodule_t *module, isc_logchannel_t *channel); ! 229: ! 230: static isc_result_t ! 231: sync_channellist(isc_logconfig_t *lcfg); ! 232: ! 233: static isc_result_t ! 234: greatest_version(isc_logchannel_t *channel, int *greatest); ! 235: ! 236: static isc_result_t ! 237: roll_log(isc_logchannel_t *channel); ! 238: ! 239: static void ! 240: isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category, ! 241: isc_logmodule_t *module, int level, isc_boolean_t write_once, ! 242: isc_msgcat_t *msgcat, int msgset, int msg, ! 243: const char *format, va_list args) ! 244: ISC_FORMAT_PRINTF(9, 0); ! 245: ! 246: /*@{*/ ! 247: /*! ! 248: * Convenience macros. ! 249: */ ! 250: ! 251: #define FACILITY(channel) (channel->destination.facility) ! 252: #define FILE_NAME(channel) (channel->destination.file.name) ! 253: #define FILE_STREAM(channel) (channel->destination.file.stream) ! 254: #define FILE_VERSIONS(channel) (channel->destination.file.versions) ! 255: #define FILE_MAXSIZE(channel) (channel->destination.file.maximum_size) ! 256: #define FILE_MAXREACHED(channel) (channel->destination.file.maximum_reached) ! 257: ! 258: /*@}*/ ! 259: /**** ! 260: **** Public interfaces. ! 261: ****/ ! 262: ! 263: /* ! 264: * Establish a new logging context, with default channels. ! 265: */ ! 266: isc_result_t ! 267: isc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp) { ! 268: isc_log_t *lctx; ! 269: isc_logconfig_t *lcfg = NULL; ! 270: isc_result_t result; ! 271: ! 272: REQUIRE(mctx != NULL); ! 273: REQUIRE(lctxp != NULL && *lctxp == NULL); ! 274: REQUIRE(lcfgp == NULL || *lcfgp == NULL); ! 275: ! 276: lctx = isc_mem_get(mctx, sizeof(*lctx)); ! 277: if (lctx != NULL) { ! 278: lctx->mctx = mctx; ! 279: lctx->categories = NULL; ! 280: lctx->category_count = 0; ! 281: lctx->modules = NULL; ! 282: lctx->module_count = 0; ! 283: lctx->debug_level = 0; ! 284: ! 285: ISC_LIST_INIT(lctx->messages); ! 286: ! 287: result = isc_mutex_init(&lctx->lock); ! 288: if (result != ISC_R_SUCCESS) { ! 289: isc_mem_put(mctx, lctx, sizeof(*lctx)); ! 290: return (result); ! 291: } ! 292: ! 293: /* ! 294: * Normally setting the magic number is the last step done ! 295: * in a creation function, but a valid log context is needed ! 296: * by isc_log_registercategories and isc_logconfig_create. ! 297: * If either fails, the lctx is destroyed and not returned ! 298: * to the caller. ! 299: */ ! 300: lctx->magic = LCTX_MAGIC; ! 301: ! 302: isc_log_registercategories(lctx, isc_categories); ! 303: isc_log_registermodules(lctx, isc_modules); ! 304: result = isc_logconfig_create(lctx, &lcfg); ! 305: ! 306: } else ! 307: result = ISC_R_NOMEMORY; ! 308: ! 309: if (result == ISC_R_SUCCESS) ! 310: result = sync_channellist(lcfg); ! 311: ! 312: if (result == ISC_R_SUCCESS) { ! 313: lctx->logconfig = lcfg; ! 314: ! 315: *lctxp = lctx; ! 316: if (lcfgp != NULL) ! 317: *lcfgp = lcfg; ! 318: ! 319: } else { ! 320: if (lcfg != NULL) ! 321: isc_logconfig_destroy(&lcfg); ! 322: if (lctx != NULL) ! 323: isc_log_destroy(&lctx); ! 324: } ! 325: ! 326: return (result); ! 327: } ! 328: ! 329: isc_result_t ! 330: isc_logconfig_create(isc_log_t *lctx, isc_logconfig_t **lcfgp) { ! 331: isc_logconfig_t *lcfg; ! 332: isc_logdestination_t destination; ! 333: isc_result_t result = ISC_R_SUCCESS; ! 334: int level = ISC_LOG_INFO; ! 335: ! 336: REQUIRE(lcfgp != NULL && *lcfgp == NULL); ! 337: REQUIRE(VALID_CONTEXT(lctx)); ! 338: ! 339: lcfg = isc_mem_get(lctx->mctx, sizeof(*lcfg)); ! 340: ! 341: if (lcfg != NULL) { ! 342: lcfg->lctx = lctx; ! 343: lcfg->channellists = NULL; ! 344: lcfg->channellist_count = 0; ! 345: lcfg->duplicate_interval = 0; ! 346: lcfg->highest_level = level; ! 347: lcfg->tag = NULL; ! 348: lcfg->dynamic = ISC_FALSE; ! 349: ! 350: ISC_LIST_INIT(lcfg->channels); ! 351: ! 352: /* ! 353: * Normally the magic number is the last thing set in the ! 354: * structure, but isc_log_createchannel() needs a valid ! 355: * config. If the channel creation fails, the lcfg is not ! 356: * returned to the caller. ! 357: */ ! 358: lcfg->magic = LCFG_MAGIC; ! 359: ! 360: } else ! 361: result = ISC_R_NOMEMORY; ! 362: ! 363: /* ! 364: * Create the default channels: ! 365: * default_syslog, default_stderr, default_debug and null. ! 366: */ ! 367: if (result == ISC_R_SUCCESS) { ! 368: destination.facility = LOG_DAEMON; ! 369: result = isc_log_createchannel(lcfg, "default_syslog", ! 370: ISC_LOG_TOSYSLOG, level, ! 371: &destination, 0); ! 372: } ! 373: ! 374: if (result == ISC_R_SUCCESS) { ! 375: destination.file.stream = stderr; ! 376: destination.file.name = NULL; ! 377: destination.file.versions = ISC_LOG_ROLLNEVER; ! 378: destination.file.maximum_size = 0; ! 379: result = isc_log_createchannel(lcfg, "default_stderr", ! 380: ISC_LOG_TOFILEDESC, ! 381: level, ! 382: &destination, ! 383: ISC_LOG_PRINTTIME); ! 384: } ! 385: ! 386: if (result == ISC_R_SUCCESS) { ! 387: /* ! 388: * Set the default category's channel to default_stderr, ! 389: * which is at the head of the channels list because it was ! 390: * just created. ! 391: */ ! 392: default_channel.channel = ISC_LIST_HEAD(lcfg->channels); ! 393: ! 394: destination.file.stream = stderr; ! 395: destination.file.name = NULL; ! 396: destination.file.versions = ISC_LOG_ROLLNEVER; ! 397: destination.file.maximum_size = 0; ! 398: result = isc_log_createchannel(lcfg, "default_debug", ! 399: ISC_LOG_TOFILEDESC, ! 400: ISC_LOG_DYNAMIC, ! 401: &destination, ! 402: ISC_LOG_PRINTTIME); ! 403: } ! 404: ! 405: if (result == ISC_R_SUCCESS) ! 406: result = isc_log_createchannel(lcfg, "null", ! 407: ISC_LOG_TONULL, ! 408: ISC_LOG_DYNAMIC, ! 409: NULL, 0); ! 410: ! 411: if (result == ISC_R_SUCCESS) ! 412: *lcfgp = lcfg; ! 413: ! 414: else ! 415: if (lcfg != NULL) ! 416: isc_logconfig_destroy(&lcfg); ! 417: ! 418: return (result); ! 419: } ! 420: ! 421: isc_logconfig_t * ! 422: isc_logconfig_get(isc_log_t *lctx) { ! 423: REQUIRE(VALID_CONTEXT(lctx)); ! 424: ! 425: ENSURE(lctx->logconfig != NULL); ! 426: ! 427: return (lctx->logconfig); ! 428: } ! 429: ! 430: isc_result_t ! 431: isc_logconfig_use(isc_log_t *lctx, isc_logconfig_t *lcfg) { ! 432: isc_logconfig_t *old_cfg; ! 433: isc_result_t result; ! 434: ! 435: REQUIRE(VALID_CONTEXT(lctx)); ! 436: REQUIRE(VALID_CONFIG(lcfg)); ! 437: REQUIRE(lcfg->lctx == lctx); ! 438: ! 439: /* ! 440: * Ensure that lcfg->channellist_count == lctx->category_count. ! 441: * They won't be equal if isc_log_usechannel has not been called ! 442: * since any call to isc_log_registercategories. ! 443: */ ! 444: result = sync_channellist(lcfg); ! 445: if (result != ISC_R_SUCCESS) ! 446: return (result); ! 447: ! 448: LOCK(&lctx->lock); ! 449: ! 450: old_cfg = lctx->logconfig; ! 451: lctx->logconfig = lcfg; ! 452: ! 453: UNLOCK(&lctx->lock); ! 454: ! 455: isc_logconfig_destroy(&old_cfg); ! 456: ! 457: return (ISC_R_SUCCESS); ! 458: } ! 459: ! 460: void ! 461: isc_log_destroy(isc_log_t **lctxp) { ! 462: isc_log_t *lctx; ! 463: isc_logconfig_t *lcfg; ! 464: isc_mem_t *mctx; ! 465: isc_logmessage_t *message; ! 466: ! 467: REQUIRE(lctxp != NULL && VALID_CONTEXT(*lctxp)); ! 468: ! 469: lctx = *lctxp; ! 470: mctx = lctx->mctx; ! 471: ! 472: if (lctx->logconfig != NULL) { ! 473: lcfg = lctx->logconfig; ! 474: lctx->logconfig = NULL; ! 475: isc_logconfig_destroy(&lcfg); ! 476: } ! 477: ! 478: DESTROYLOCK(&lctx->lock); ! 479: ! 480: while ((message = ISC_LIST_HEAD(lctx->messages)) != NULL) { ! 481: ISC_LIST_UNLINK(lctx->messages, message, link); ! 482: ! 483: isc_mem_put(mctx, message, ! 484: sizeof(*message) + strlen(message->text) + 1); ! 485: } ! 486: ! 487: lctx->buffer[0] = '\0'; ! 488: lctx->debug_level = 0; ! 489: lctx->categories = NULL; ! 490: lctx->category_count = 0; ! 491: lctx->modules = NULL; ! 492: lctx->module_count = 0; ! 493: lctx->mctx = NULL; ! 494: lctx->magic = 0; ! 495: ! 496: isc_mem_put(mctx, lctx, sizeof(*lctx)); ! 497: ! 498: *lctxp = NULL; ! 499: } ! 500: ! 501: void ! 502: isc_logconfig_destroy(isc_logconfig_t **lcfgp) { ! 503: isc_logconfig_t *lcfg; ! 504: isc_mem_t *mctx; ! 505: isc_logchannel_t *channel; ! 506: isc_logchannellist_t *item; ! 507: char *filename; ! 508: unsigned int i; ! 509: ! 510: REQUIRE(lcfgp != NULL && VALID_CONFIG(*lcfgp)); ! 511: ! 512: lcfg = *lcfgp; ! 513: ! 514: /* ! 515: * This function cannot be called with a logconfig that is in ! 516: * use by a log context. ! 517: */ ! 518: REQUIRE(lcfg->lctx != NULL && lcfg->lctx->logconfig != lcfg); ! 519: ! 520: mctx = lcfg->lctx->mctx; ! 521: ! 522: while ((channel = ISC_LIST_HEAD(lcfg->channels)) != NULL) { ! 523: ISC_LIST_UNLINK(lcfg->channels, channel, link); ! 524: ! 525: if (channel->type == ISC_LOG_TOFILE) { ! 526: /* ! 527: * The filename for the channel may have ultimately ! 528: * started its life in user-land as a const string, ! 529: * but in isc_log_createchannel it gets copied ! 530: * into writable memory and is not longer truly const. ! 531: */ ! 532: DE_CONST(FILE_NAME(channel), filename); ! 533: isc_mem_free(mctx, filename); ! 534: ! 535: if (FILE_STREAM(channel) != NULL) ! 536: (void)fclose(FILE_STREAM(channel)); ! 537: } ! 538: ! 539: isc_mem_free(mctx, channel->name); ! 540: isc_mem_put(mctx, channel, sizeof(*channel)); ! 541: } ! 542: ! 543: for (i = 0; i < lcfg->channellist_count; i++) ! 544: while ((item = ISC_LIST_HEAD(lcfg->channellists[i])) != NULL) { ! 545: ISC_LIST_UNLINK(lcfg->channellists[i], item, link); ! 546: isc_mem_put(mctx, item, sizeof(*item)); ! 547: } ! 548: ! 549: if (lcfg->channellist_count > 0) ! 550: isc_mem_put(mctx, lcfg->channellists, ! 551: lcfg->channellist_count * ! 552: sizeof(ISC_LIST(isc_logchannellist_t))); ! 553: ! 554: lcfg->dynamic = ISC_FALSE; ! 555: if (lcfg->tag != NULL) ! 556: isc_mem_free(lcfg->lctx->mctx, lcfg->tag); ! 557: lcfg->tag = NULL; ! 558: lcfg->highest_level = 0; ! 559: lcfg->duplicate_interval = 0; ! 560: lcfg->magic = 0; ! 561: ! 562: isc_mem_put(mctx, lcfg, sizeof(*lcfg)); ! 563: ! 564: *lcfgp = NULL; ! 565: } ! 566: ! 567: void ! 568: isc_log_registercategories(isc_log_t *lctx, isc_logcategory_t categories[]) { ! 569: isc_logcategory_t *catp; ! 570: ! 571: REQUIRE(VALID_CONTEXT(lctx)); ! 572: REQUIRE(categories != NULL && categories[0].name != NULL); ! 573: ! 574: /* ! 575: * XXXDCL This somewhat sleazy situation of using the last pointer ! 576: * in one category array to point to the next array exists because ! 577: * this registration function returns void and I didn't want to have ! 578: * change everything that used it by making it return an isc_result_t. ! 579: * It would need to do that if it had to allocate memory to store ! 580: * pointers to each array passed in. ! 581: */ ! 582: if (lctx->categories == NULL) ! 583: lctx->categories = categories; ! 584: ! 585: else { ! 586: /* ! 587: * Adjust the last (NULL) pointer of the already registered ! 588: * categories to point to the incoming array. ! 589: */ ! 590: for (catp = lctx->categories; catp->name != NULL; ) ! 591: if (catp->id == UINT_MAX) ! 592: /* ! 593: * The name pointer points to the next array. ! 594: * Ick. ! 595: */ ! 596: DE_CONST(catp->name, catp); ! 597: else ! 598: catp++; ! 599: ! 600: catp->name = (void *)categories; ! 601: catp->id = UINT_MAX; ! 602: } ! 603: ! 604: /* ! 605: * Update the id number of the category with its new global id. ! 606: */ ! 607: for (catp = categories; catp->name != NULL; catp++) ! 608: catp->id = lctx->category_count++; ! 609: } ! 610: ! 611: isc_logcategory_t * ! 612: isc_log_categorybyname(isc_log_t *lctx, const char *name) { ! 613: isc_logcategory_t *catp; ! 614: ! 615: REQUIRE(VALID_CONTEXT(lctx)); ! 616: REQUIRE(name != NULL); ! 617: ! 618: for (catp = lctx->categories; catp->name != NULL; ) ! 619: if (catp->id == UINT_MAX) ! 620: /* ! 621: * catp is neither modified nor returned to the ! 622: * caller, so removing its const qualifier is ok. ! 623: */ ! 624: DE_CONST(catp->name, catp); ! 625: else { ! 626: if (strcmp(catp->name, name) == 0) ! 627: return (catp); ! 628: catp++; ! 629: } ! 630: ! 631: return (NULL); ! 632: } ! 633: ! 634: void ! 635: isc_log_registermodules(isc_log_t *lctx, isc_logmodule_t modules[]) { ! 636: isc_logmodule_t *modp; ! 637: ! 638: REQUIRE(VALID_CONTEXT(lctx)); ! 639: REQUIRE(modules != NULL && modules[0].name != NULL); ! 640: ! 641: /* ! 642: * XXXDCL This somewhat sleazy situation of using the last pointer ! 643: * in one category array to point to the next array exists because ! 644: * this registration function returns void and I didn't want to have ! 645: * change everything that used it by making it return an isc_result_t. ! 646: * It would need to do that if it had to allocate memory to store ! 647: * pointers to each array passed in. ! 648: */ ! 649: if (lctx->modules == NULL) ! 650: lctx->modules = modules; ! 651: ! 652: else { ! 653: /* ! 654: * Adjust the last (NULL) pointer of the already registered ! 655: * modules to point to the incoming array. ! 656: */ ! 657: for (modp = lctx->modules; modp->name != NULL; ) ! 658: if (modp->id == UINT_MAX) ! 659: /* ! 660: * The name pointer points to the next array. ! 661: * Ick. ! 662: */ ! 663: DE_CONST(modp->name, modp); ! 664: else ! 665: modp++; ! 666: ! 667: modp->name = (void *)modules; ! 668: modp->id = UINT_MAX; ! 669: } ! 670: ! 671: /* ! 672: * Update the id number of the module with its new global id. ! 673: */ ! 674: for (modp = modules; modp->name != NULL; modp++) ! 675: modp->id = lctx->module_count++; ! 676: } ! 677: ! 678: isc_logmodule_t * ! 679: isc_log_modulebyname(isc_log_t *lctx, const char *name) { ! 680: isc_logmodule_t *modp; ! 681: ! 682: REQUIRE(VALID_CONTEXT(lctx)); ! 683: REQUIRE(name != NULL); ! 684: ! 685: for (modp = lctx->modules; modp->name != NULL; ) ! 686: if (modp->id == UINT_MAX) ! 687: /* ! 688: * modp is neither modified nor returned to the ! 689: * caller, so removing its const qualifier is ok. ! 690: */ ! 691: DE_CONST(modp->name, modp); ! 692: else { ! 693: if (strcmp(modp->name, name) == 0) ! 694: return (modp); ! 695: modp++; ! 696: } ! 697: ! 698: return (NULL); ! 699: } ! 700: ! 701: isc_result_t ! 702: isc_log_createchannel(isc_logconfig_t *lcfg, const char *name, ! 703: unsigned int type, int level, ! 704: const isc_logdestination_t *destination, ! 705: unsigned int flags) ! 706: { ! 707: isc_logchannel_t *channel; ! 708: isc_mem_t *mctx; ! 709: ! 710: REQUIRE(VALID_CONFIG(lcfg)); ! 711: REQUIRE(name != NULL); ! 712: REQUIRE(type == ISC_LOG_TOSYSLOG || type == ISC_LOG_TOFILE || ! 713: type == ISC_LOG_TOFILEDESC || type == ISC_LOG_TONULL); ! 714: REQUIRE(destination != NULL || type == ISC_LOG_TONULL); ! 715: REQUIRE(level >= ISC_LOG_CRITICAL); ! 716: REQUIRE((flags & ! 717: (unsigned int)~(ISC_LOG_PRINTALL | ISC_LOG_DEBUGONLY)) == 0); ! 718: ! 719: /* XXXDCL find duplicate names? */ ! 720: ! 721: mctx = lcfg->lctx->mctx; ! 722: ! 723: channel = isc_mem_get(mctx, sizeof(*channel)); ! 724: if (channel == NULL) ! 725: return (ISC_R_NOMEMORY); ! 726: ! 727: channel->name = isc_mem_strdup(mctx, name); ! 728: if (channel->name == NULL) { ! 729: isc_mem_put(mctx, channel, sizeof(*channel)); ! 730: return (ISC_R_NOMEMORY); ! 731: } ! 732: ! 733: channel->type = type; ! 734: channel->level = level; ! 735: channel->flags = flags; ! 736: ISC_LINK_INIT(channel, link); ! 737: ! 738: switch (type) { ! 739: case ISC_LOG_TOSYSLOG: ! 740: FACILITY(channel) = destination->facility; ! 741: break; ! 742: ! 743: case ISC_LOG_TOFILE: ! 744: /* ! 745: * The file name is copied because greatest_version wants ! 746: * to scribble on it, so it needs to be definitely in ! 747: * writable memory. ! 748: */ ! 749: FILE_NAME(channel) = ! 750: isc_mem_strdup(mctx, destination->file.name); ! 751: FILE_STREAM(channel) = NULL; ! 752: FILE_VERSIONS(channel) = destination->file.versions; ! 753: FILE_MAXSIZE(channel) = destination->file.maximum_size; ! 754: FILE_MAXREACHED(channel) = ISC_FALSE; ! 755: break; ! 756: ! 757: case ISC_LOG_TOFILEDESC: ! 758: FILE_NAME(channel) = NULL; ! 759: FILE_STREAM(channel) = destination->file.stream; ! 760: FILE_MAXSIZE(channel) = 0; ! 761: FILE_VERSIONS(channel) = ISC_LOG_ROLLNEVER; ! 762: break; ! 763: ! 764: case ISC_LOG_TONULL: ! 765: /* Nothing. */ ! 766: break; ! 767: ! 768: default: ! 769: isc_mem_put(mctx, channel->name, strlen(channel->name) + 1); ! 770: isc_mem_put(mctx, channel, sizeof(*channel)); ! 771: return (ISC_R_UNEXPECTED); ! 772: } ! 773: ! 774: ISC_LIST_PREPEND(lcfg->channels, channel, link); ! 775: ! 776: /* ! 777: * If default_stderr was redefined, make the default category ! 778: * point to the new default_stderr. ! 779: */ ! 780: if (strcmp(name, "default_stderr") == 0) ! 781: default_channel.channel = channel; ! 782: ! 783: return (ISC_R_SUCCESS); ! 784: } ! 785: ! 786: isc_result_t ! 787: isc_log_usechannel(isc_logconfig_t *lcfg, const char *name, ! 788: const isc_logcategory_t *category, ! 789: const isc_logmodule_t *module) ! 790: { ! 791: isc_log_t *lctx; ! 792: isc_logchannel_t *channel; ! 793: isc_result_t result = ISC_R_SUCCESS; ! 794: unsigned int i; ! 795: ! 796: REQUIRE(VALID_CONFIG(lcfg)); ! 797: REQUIRE(name != NULL); ! 798: ! 799: lctx = lcfg->lctx; ! 800: ! 801: REQUIRE(category == NULL || category->id < lctx->category_count); ! 802: REQUIRE(module == NULL || module->id < lctx->module_count); ! 803: ! 804: for (channel = ISC_LIST_HEAD(lcfg->channels); channel != NULL; ! 805: channel = ISC_LIST_NEXT(channel, link)) ! 806: if (strcmp(name, channel->name) == 0) ! 807: break; ! 808: ! 809: if (channel == NULL) ! 810: return (ISC_R_NOTFOUND); ! 811: ! 812: if (category != NULL) ! 813: result = assignchannel(lcfg, category->id, module, channel); ! 814: ! 815: else ! 816: /* ! 817: * Assign to all categories. Note that this includes ! 818: * the default channel. ! 819: */ ! 820: for (i = 0; i < lctx->category_count; i++) { ! 821: result = assignchannel(lcfg, i, module, channel); ! 822: if (result != ISC_R_SUCCESS) ! 823: break; ! 824: } ! 825: ! 826: return (result); ! 827: } ! 828: ! 829: void ! 830: isc_log_write(isc_log_t *lctx, isc_logcategory_t *category, ! 831: isc_logmodule_t *module, int level, const char *format, ...) ! 832: { ! 833: va_list args; ! 834: ! 835: /* ! 836: * Contract checking is done in isc_log_doit(). ! 837: */ ! 838: ! 839: va_start(args, format); ! 840: isc_log_doit(lctx, category, module, level, ISC_FALSE, ! 841: NULL, 0, 0, format, args); ! 842: va_end(args); ! 843: } ! 844: ! 845: void ! 846: isc_log_vwrite(isc_log_t *lctx, isc_logcategory_t *category, ! 847: isc_logmodule_t *module, int level, ! 848: const char *format, va_list args) ! 849: { ! 850: /* ! 851: * Contract checking is done in isc_log_doit(). ! 852: */ ! 853: isc_log_doit(lctx, category, module, level, ISC_FALSE, ! 854: NULL, 0, 0, format, args); ! 855: } ! 856: ! 857: void ! 858: isc_log_write1(isc_log_t *lctx, isc_logcategory_t *category, ! 859: isc_logmodule_t *module, int level, const char *format, ...) ! 860: { ! 861: va_list args; ! 862: ! 863: /* ! 864: * Contract checking is done in isc_log_doit(). ! 865: */ ! 866: ! 867: va_start(args, format); ! 868: isc_log_doit(lctx, category, module, level, ISC_TRUE, ! 869: NULL, 0, 0, format, args); ! 870: va_end(args); ! 871: } ! 872: ! 873: void ! 874: isc_log_vwrite1(isc_log_t *lctx, isc_logcategory_t *category, ! 875: isc_logmodule_t *module, int level, ! 876: const char *format, va_list args) ! 877: { ! 878: /* ! 879: * Contract checking is done in isc_log_doit(). ! 880: */ ! 881: isc_log_doit(lctx, category, module, level, ISC_TRUE, ! 882: NULL, 0, 0, format, args); ! 883: } ! 884: ! 885: void ! 886: isc_log_iwrite(isc_log_t *lctx, isc_logcategory_t *category, ! 887: isc_logmodule_t *module, int level, ! 888: isc_msgcat_t *msgcat, int msgset, int msg, ! 889: const char *format, ...) ! 890: { ! 891: va_list args; ! 892: ! 893: /* ! 894: * Contract checking is done in isc_log_doit(). ! 895: */ ! 896: ! 897: va_start(args, format); ! 898: isc_log_doit(lctx, category, module, level, ISC_FALSE, ! 899: msgcat, msgset, msg, format, args); ! 900: va_end(args); ! 901: } ! 902: ! 903: void ! 904: isc_log_ivwrite(isc_log_t *lctx, isc_logcategory_t *category, ! 905: isc_logmodule_t *module, int level, ! 906: isc_msgcat_t *msgcat, int msgset, int msg, ! 907: const char *format, va_list args) ! 908: { ! 909: /* ! 910: * Contract checking is done in isc_log_doit(). ! 911: */ ! 912: isc_log_doit(lctx, category, module, level, ISC_FALSE, ! 913: msgcat, msgset, msg, format, args); ! 914: } ! 915: ! 916: void ! 917: isc_log_iwrite1(isc_log_t *lctx, isc_logcategory_t *category, ! 918: isc_logmodule_t *module, int level, ! 919: isc_msgcat_t *msgcat, int msgset, int msg, ! 920: const char *format, ...) ! 921: { ! 922: va_list args; ! 923: ! 924: /* ! 925: * Contract checking is done in isc_log_doit(). ! 926: */ ! 927: ! 928: va_start(args, format); ! 929: isc_log_doit(lctx, category, module, level, ISC_TRUE, ! 930: msgcat, msgset, msg, format, args); ! 931: va_end(args); ! 932: } ! 933: ! 934: void ! 935: isc_log_ivwrite1(isc_log_t *lctx, isc_logcategory_t *category, ! 936: isc_logmodule_t *module, int level, ! 937: isc_msgcat_t *msgcat, int msgset, int msg, ! 938: const char *format, va_list args) ! 939: { ! 940: /* ! 941: * Contract checking is done in isc_log_doit(). ! 942: */ ! 943: isc_log_doit(lctx, category, module, level, ISC_TRUE, ! 944: msgcat, msgset, msg, format, args); ! 945: } ! 946: ! 947: void ! 948: isc_log_setcontext(isc_log_t *lctx) { ! 949: isc_lctx = lctx; ! 950: } ! 951: ! 952: void ! 953: isc_log_setdebuglevel(isc_log_t *lctx, unsigned int level) { ! 954: isc_logchannel_t *channel; ! 955: ! 956: REQUIRE(VALID_CONTEXT(lctx)); ! 957: ! 958: LOCK(&lctx->lock); ! 959: ! 960: lctx->debug_level = level; ! 961: /* ! 962: * Close ISC_LOG_DEBUGONLY channels if level is zero. ! 963: */ ! 964: if (lctx->debug_level == 0) ! 965: for (channel = ISC_LIST_HEAD(lctx->logconfig->channels); ! 966: channel != NULL; ! 967: channel = ISC_LIST_NEXT(channel, link)) ! 968: if (channel->type == ISC_LOG_TOFILE && ! 969: (channel->flags & ISC_LOG_DEBUGONLY) != 0 && ! 970: FILE_STREAM(channel) != NULL) { ! 971: (void)fclose(FILE_STREAM(channel)); ! 972: FILE_STREAM(channel) = NULL; ! 973: } ! 974: UNLOCK(&lctx->lock); ! 975: } ! 976: ! 977: unsigned int ! 978: isc_log_getdebuglevel(isc_log_t *lctx) { ! 979: REQUIRE(VALID_CONTEXT(lctx)); ! 980: ! 981: return (lctx->debug_level); ! 982: } ! 983: ! 984: void ! 985: isc_log_setduplicateinterval(isc_logconfig_t *lcfg, unsigned int interval) { ! 986: REQUIRE(VALID_CONFIG(lcfg)); ! 987: ! 988: lcfg->duplicate_interval = interval; ! 989: } ! 990: ! 991: unsigned int ! 992: isc_log_getduplicateinterval(isc_logconfig_t *lcfg) { ! 993: REQUIRE(VALID_CONTEXT(lcfg)); ! 994: ! 995: return (lcfg->duplicate_interval); ! 996: } ! 997: ! 998: isc_result_t ! 999: isc_log_settag(isc_logconfig_t *lcfg, const char *tag) { ! 1000: REQUIRE(VALID_CONFIG(lcfg)); ! 1001: ! 1002: if (tag != NULL && *tag != '\0') { ! 1003: if (lcfg->tag != NULL) ! 1004: isc_mem_free(lcfg->lctx->mctx, lcfg->tag); ! 1005: lcfg->tag = isc_mem_strdup(lcfg->lctx->mctx, tag); ! 1006: if (lcfg->tag == NULL) ! 1007: return (ISC_R_NOMEMORY); ! 1008: ! 1009: } else { ! 1010: if (lcfg->tag != NULL) ! 1011: isc_mem_free(lcfg->lctx->mctx, lcfg->tag); ! 1012: lcfg->tag = NULL; ! 1013: } ! 1014: ! 1015: return (ISC_R_SUCCESS); ! 1016: } ! 1017: ! 1018: char * ! 1019: isc_log_gettag(isc_logconfig_t *lcfg) { ! 1020: REQUIRE(VALID_CONFIG(lcfg)); ! 1021: ! 1022: return (lcfg->tag); ! 1023: } ! 1024: ! 1025: /* XXXDCL NT -- This interface will assuredly be changing. */ ! 1026: void ! 1027: isc_log_opensyslog(const char *tag, int options, int facility) { ! 1028: (void)openlog(tag, options, facility); ! 1029: } ! 1030: ! 1031: void ! 1032: isc_log_closefilelogs(isc_log_t *lctx) { ! 1033: isc_logchannel_t *channel; ! 1034: ! 1035: REQUIRE(VALID_CONTEXT(lctx)); ! 1036: ! 1037: LOCK(&lctx->lock); ! 1038: for (channel = ISC_LIST_HEAD(lctx->logconfig->channels); ! 1039: channel != NULL; ! 1040: channel = ISC_LIST_NEXT(channel, link)) ! 1041: ! 1042: if (channel->type == ISC_LOG_TOFILE && ! 1043: FILE_STREAM(channel) != NULL) { ! 1044: (void)fclose(FILE_STREAM(channel)); ! 1045: FILE_STREAM(channel) = NULL; ! 1046: } ! 1047: UNLOCK(&lctx->lock); ! 1048: } ! 1049: ! 1050: /**** ! 1051: **** Internal functions ! 1052: ****/ ! 1053: ! 1054: static isc_result_t ! 1055: assignchannel(isc_logconfig_t *lcfg, unsigned int category_id, ! 1056: const isc_logmodule_t *module, isc_logchannel_t *channel) ! 1057: { ! 1058: isc_logchannellist_t *new_item; ! 1059: isc_log_t *lctx; ! 1060: isc_result_t result; ! 1061: ! 1062: REQUIRE(VALID_CONFIG(lcfg)); ! 1063: ! 1064: lctx = lcfg->lctx; ! 1065: ! 1066: REQUIRE(category_id < lctx->category_count); ! 1067: REQUIRE(module == NULL || module->id < lctx->module_count); ! 1068: REQUIRE(channel != NULL); ! 1069: ! 1070: /* ! 1071: * Ensure lcfg->channellist_count == lctx->category_count. ! 1072: */ ! 1073: result = sync_channellist(lcfg); ! 1074: if (result != ISC_R_SUCCESS) ! 1075: return (result); ! 1076: ! 1077: new_item = isc_mem_get(lctx->mctx, sizeof(*new_item)); ! 1078: if (new_item == NULL) ! 1079: return (ISC_R_NOMEMORY); ! 1080: ! 1081: new_item->channel = channel; ! 1082: new_item->module = module; ! 1083: ISC_LIST_INITANDPREPEND(lcfg->channellists[category_id], ! 1084: new_item, link); ! 1085: ! 1086: /* ! 1087: * Remember the highest logging level set by any channel in the ! 1088: * logging config, so isc_log_doit() can quickly return if the ! 1089: * message is too high to be logged by any channel. ! 1090: */ ! 1091: if (channel->type != ISC_LOG_TONULL) { ! 1092: if (lcfg->highest_level < channel->level) ! 1093: lcfg->highest_level = channel->level; ! 1094: if (channel->level == ISC_LOG_DYNAMIC) ! 1095: lcfg->dynamic = ISC_TRUE; ! 1096: } ! 1097: ! 1098: return (ISC_R_SUCCESS); ! 1099: } ! 1100: ! 1101: /* ! 1102: * This would ideally be part of isc_log_registercategories(), except then ! 1103: * that function would have to return isc_result_t instead of void. ! 1104: */ ! 1105: static isc_result_t ! 1106: sync_channellist(isc_logconfig_t *lcfg) { ! 1107: unsigned int bytes; ! 1108: isc_log_t *lctx; ! 1109: void *lists; ! 1110: ! 1111: REQUIRE(VALID_CONFIG(lcfg)); ! 1112: ! 1113: lctx = lcfg->lctx; ! 1114: ! 1115: REQUIRE(lctx->category_count != 0); ! 1116: ! 1117: if (lctx->category_count == lcfg->channellist_count) ! 1118: return (ISC_R_SUCCESS); ! 1119: ! 1120: bytes = lctx->category_count * sizeof(ISC_LIST(isc_logchannellist_t)); ! 1121: ! 1122: lists = isc_mem_get(lctx->mctx, bytes); ! 1123: ! 1124: if (lists == NULL) ! 1125: return (ISC_R_NOMEMORY); ! 1126: ! 1127: memset(lists, 0, bytes); ! 1128: ! 1129: if (lcfg->channellist_count != 0) { ! 1130: bytes = lcfg->channellist_count * ! 1131: sizeof(ISC_LIST(isc_logchannellist_t)); ! 1132: memcpy(lists, lcfg->channellists, bytes); ! 1133: isc_mem_put(lctx->mctx, lcfg->channellists, bytes); ! 1134: } ! 1135: ! 1136: lcfg->channellists = lists; ! 1137: lcfg->channellist_count = lctx->category_count; ! 1138: ! 1139: return (ISC_R_SUCCESS); ! 1140: } ! 1141: ! 1142: static isc_result_t ! 1143: greatest_version(isc_logchannel_t *channel, int *greatestp) { ! 1144: /* XXXDCL HIGHLY NT */ ! 1145: char *basename, *digit_end; ! 1146: const char *dirname; ! 1147: int version, greatest = -1; ! 1148: unsigned int basenamelen; ! 1149: isc_dir_t dir; ! 1150: isc_result_t result; ! 1151: char sep = '/'; ! 1152: #ifdef _WIN32 ! 1153: char *basename2; ! 1154: #endif ! 1155: ! 1156: REQUIRE(channel->type == ISC_LOG_TOFILE); ! 1157: ! 1158: /* ! 1159: * It is safe to DE_CONST the file.name because it was copied ! 1160: * with isc_mem_strdup in isc_log_createchannel. ! 1161: */ ! 1162: basename = strrchr(FILE_NAME(channel), sep); ! 1163: #ifdef _WIN32 ! 1164: basename2 = strrchr(FILE_NAME(channel), '\\'); ! 1165: if ((basename != NULL && basename2 != NULL && basename2 > basename) || ! 1166: (basename == NULL && basename2 != NULL)) { ! 1167: basename = basename2; ! 1168: sep = '\\'; ! 1169: } ! 1170: #endif ! 1171: if (basename != NULL) { ! 1172: *basename++ = '\0'; ! 1173: dirname = FILE_NAME(channel); ! 1174: } else { ! 1175: DE_CONST(FILE_NAME(channel), basename); ! 1176: dirname = "."; ! 1177: } ! 1178: basenamelen = strlen(basename); ! 1179: ! 1180: isc_dir_init(&dir); ! 1181: result = isc_dir_open(&dir, dirname); ! 1182: ! 1183: /* ! 1184: * Replace the file separator if it was taken out. ! 1185: */ ! 1186: if (basename != FILE_NAME(channel)) ! 1187: *(basename - 1) = sep; ! 1188: ! 1189: /* ! 1190: * Return if the directory open failed. ! 1191: */ ! 1192: if (result != ISC_R_SUCCESS) ! 1193: return (result); ! 1194: ! 1195: while (isc_dir_read(&dir) == ISC_R_SUCCESS) { ! 1196: if (dir.entry.length > basenamelen && ! 1197: strncmp(dir.entry.name, basename, basenamelen) == 0 && ! 1198: dir.entry.name[basenamelen] == '.') { ! 1199: ! 1200: version = strtol(&dir.entry.name[basenamelen + 1], ! 1201: &digit_end, 10); ! 1202: if (*digit_end == '\0' && version > greatest) ! 1203: greatest = version; ! 1204: } ! 1205: } ! 1206: isc_dir_close(&dir); ! 1207: ! 1208: *greatestp = ++greatest; ! 1209: ! 1210: return (ISC_R_SUCCESS); ! 1211: } ! 1212: ! 1213: static isc_result_t ! 1214: roll_log(isc_logchannel_t *channel) { ! 1215: int i, n, greatest; ! 1216: char current[PATH_MAX + 1]; ! 1217: char new[PATH_MAX + 1]; ! 1218: const char *path; ! 1219: isc_result_t result; ! 1220: ! 1221: /* ! 1222: * Do nothing (not even excess version trimming) if ISC_LOG_ROLLNEVER ! 1223: * is specified. Apparently complete external control over the log ! 1224: * files is desired. ! 1225: */ ! 1226: if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER) ! 1227: return (ISC_R_SUCCESS); ! 1228: ! 1229: path = FILE_NAME(channel); ! 1230: ! 1231: /* ! 1232: * Set greatest_version to the greatest existing version ! 1233: * (not the maximum requested version). This is 1 based even ! 1234: * though the file names are 0 based, so an oldest log of log.1 ! 1235: * is a greatest_version of 2. ! 1236: */ ! 1237: result = greatest_version(channel, &greatest); ! 1238: if (result != ISC_R_SUCCESS) ! 1239: return (result); ! 1240: ! 1241: /* ! 1242: * Now greatest should be set to the highest version number desired. ! 1243: * Since the highest number is one less than FILE_VERSIONS(channel) ! 1244: * when not doing infinite log rolling, greatest will need to be ! 1245: * decremented when it is equal to -- or greater than -- ! 1246: * FILE_VERSIONS(channel). When greatest is less than ! 1247: * FILE_VERSIONS(channel), it is already suitable for use as ! 1248: * the maximum version number. ! 1249: */ ! 1250: ! 1251: if (FILE_VERSIONS(channel) == ISC_LOG_ROLLINFINITE || ! 1252: FILE_VERSIONS(channel) > greatest) ! 1253: ; /* Do nothing. */ ! 1254: else ! 1255: /* ! 1256: * When greatest is >= FILE_VERSIONS(channel), it needs to ! 1257: * be reduced until it is FILE_VERSIONS(channel) - 1. ! 1258: * Remove any excess logs on the way to that value. ! 1259: */ ! 1260: while (--greatest >= FILE_VERSIONS(channel)) { ! 1261: n = snprintf(current, sizeof(current), "%s.%d", ! 1262: path, greatest); ! 1263: if (n >= (int)sizeof(current) || n < 0) ! 1264: result = ISC_R_NOSPACE; ! 1265: else ! 1266: result = isc_file_remove(current); ! 1267: if (result != ISC_R_SUCCESS && ! 1268: result != ISC_R_FILENOTFOUND) ! 1269: syslog(LOG_ERR, ! 1270: "unable to remove log file '%s.%d': %s", ! 1271: path, greatest, ! 1272: isc_result_totext(result)); ! 1273: } ! 1274: ! 1275: for (i = greatest; i > 0; i--) { ! 1276: result = ISC_R_SUCCESS; ! 1277: n = snprintf(current, sizeof(current), "%s.%d", path, i - 1); ! 1278: if (n >= (int)sizeof(current) || n < 0) ! 1279: result = ISC_R_NOSPACE; ! 1280: if (result == ISC_R_SUCCESS) { ! 1281: n = snprintf(new, sizeof(new), "%s.%d", path, i); ! 1282: if (n >= (int)sizeof(new) || n < 0) ! 1283: result = ISC_R_NOSPACE; ! 1284: } ! 1285: if (result == ISC_R_SUCCESS) ! 1286: result = isc_file_rename(current, new); ! 1287: if (result != ISC_R_SUCCESS && ! 1288: result != ISC_R_FILENOTFOUND) ! 1289: syslog(LOG_ERR, ! 1290: "unable to rename log file '%s.%d' to " ! 1291: "'%s.%d': %s", path, i - 1, path, i, ! 1292: isc_result_totext(result)); ! 1293: } ! 1294: ! 1295: if (FILE_VERSIONS(channel) != 0) { ! 1296: n = snprintf(new, sizeof(new), "%s.0", path); ! 1297: if (n >= (int)sizeof(new) || n < 0) ! 1298: result = ISC_R_NOSPACE; ! 1299: else ! 1300: result = isc_file_rename(path, new); ! 1301: if (result != ISC_R_SUCCESS && ! 1302: result != ISC_R_FILENOTFOUND) ! 1303: syslog(LOG_ERR, ! 1304: "unable to rename log file '%s' to '%s.0': %s", ! 1305: path, path, isc_result_totext(result)); ! 1306: } else { ! 1307: result = isc_file_remove(path); ! 1308: if (result != ISC_R_SUCCESS && ! 1309: result != ISC_R_FILENOTFOUND) ! 1310: syslog(LOG_ERR, "unable to remove log file '%s': %s", ! 1311: path, isc_result_totext(result)); ! 1312: } ! 1313: ! 1314: return (ISC_R_SUCCESS); ! 1315: } ! 1316: ! 1317: static isc_result_t ! 1318: isc_log_open(isc_logchannel_t *channel) { ! 1319: struct stat statbuf; ! 1320: isc_boolean_t regular_file; ! 1321: isc_boolean_t roll = ISC_FALSE; ! 1322: isc_result_t result = ISC_R_SUCCESS; ! 1323: const char *path; ! 1324: ! 1325: REQUIRE(channel->type == ISC_LOG_TOFILE); ! 1326: REQUIRE(FILE_STREAM(channel) == NULL); ! 1327: ! 1328: path = FILE_NAME(channel); ! 1329: ! 1330: REQUIRE(path != NULL && *path != '\0'); ! 1331: ! 1332: /* ! 1333: * Determine type of file; only regular files will be ! 1334: * version renamed, and only if the base file exists ! 1335: * and either has no size limit or has reached its size limit. ! 1336: */ ! 1337: if (stat(path, &statbuf) == 0) { ! 1338: regular_file = S_ISREG(statbuf.st_mode) ? ISC_TRUE : ISC_FALSE; ! 1339: /* XXXDCL if not regular_file complain? */ ! 1340: if ((FILE_MAXSIZE(channel) == 0 && ! 1341: FILE_VERSIONS(channel) != ISC_LOG_ROLLNEVER) || ! 1342: (FILE_MAXSIZE(channel) > 0 && ! 1343: statbuf.st_size >= FILE_MAXSIZE(channel))) ! 1344: roll = regular_file; ! 1345: } else if (errno == ENOENT) ! 1346: regular_file = ISC_TRUE; ! 1347: else ! 1348: result = ISC_R_INVALIDFILE; ! 1349: ! 1350: /* ! 1351: * Version control. ! 1352: */ ! 1353: if (result == ISC_R_SUCCESS && roll) { ! 1354: if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER) ! 1355: return (ISC_R_MAXSIZE); ! 1356: result = roll_log(channel); ! 1357: if (result != ISC_R_SUCCESS) { ! 1358: if ((channel->flags & ISC_LOG_OPENERR) == 0) { ! 1359: syslog(LOG_ERR, ! 1360: "isc_log_open: roll_log '%s' " ! 1361: "failed: %s", ! 1362: FILE_NAME(channel), ! 1363: isc_result_totext(result)); ! 1364: channel->flags |= ISC_LOG_OPENERR; ! 1365: } ! 1366: return (result); ! 1367: } ! 1368: } ! 1369: ! 1370: result = isc_stdio_open(path, "a", &FILE_STREAM(channel)); ! 1371: ! 1372: return (result); ! 1373: } ! 1374: ! 1375: isc_boolean_t ! 1376: isc_log_wouldlog(isc_log_t *lctx, int level) { ! 1377: /* ! 1378: * Try to avoid locking the mutex for messages which can't ! 1379: * possibly be logged to any channels -- primarily debugging ! 1380: * messages that the debug level is not high enough to print. ! 1381: * ! 1382: * If the level is (mathematically) less than or equal to the ! 1383: * highest_level, or if there is a dynamic channel and the level is ! 1384: * less than or equal to the debug level, the main loop must be ! 1385: * entered to see if the message should really be output. ! 1386: * ! 1387: * NOTE: this is UNLOCKED access to the logconfig. However, ! 1388: * the worst thing that can happen is that a bad decision is made ! 1389: * about returning without logging, and that's not a big concern, ! 1390: * because that's a risk anyway if the logconfig is being ! 1391: * dynamically changed. ! 1392: */ ! 1393: ! 1394: if (lctx == NULL || lctx->logconfig == NULL) ! 1395: return (ISC_FALSE); ! 1396: ! 1397: return (ISC_TF(level <= lctx->logconfig->highest_level || ! 1398: (lctx->logconfig->dynamic && ! 1399: level <= lctx->debug_level))); ! 1400: } ! 1401: ! 1402: static void ! 1403: isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category, ! 1404: isc_logmodule_t *module, int level, isc_boolean_t write_once, ! 1405: isc_msgcat_t *msgcat, int msgset, int msg, ! 1406: const char *format, va_list args) ! 1407: { ! 1408: int syslog_level; ! 1409: char time_string[64]; ! 1410: char level_string[24]; ! 1411: const char *iformat; ! 1412: struct stat statbuf; ! 1413: isc_boolean_t matched = ISC_FALSE; ! 1414: isc_boolean_t printtime, printtag; ! 1415: isc_boolean_t printcategory, printmodule, printlevel; ! 1416: isc_logconfig_t *lcfg; ! 1417: isc_logchannel_t *channel; ! 1418: isc_logchannellist_t *category_channels; ! 1419: isc_result_t result; ! 1420: ! 1421: REQUIRE(lctx == NULL || VALID_CONTEXT(lctx)); ! 1422: REQUIRE(category != NULL); ! 1423: REQUIRE(module != NULL); ! 1424: REQUIRE(level != ISC_LOG_DYNAMIC); ! 1425: REQUIRE(format != NULL); ! 1426: ! 1427: /* ! 1428: * Programs can use libraries that use this logging code without ! 1429: * wanting to do any logging, thus the log context is allowed to ! 1430: * be non-existent. ! 1431: */ ! 1432: if (lctx == NULL) ! 1433: return; ! 1434: ! 1435: REQUIRE(category->id < lctx->category_count); ! 1436: REQUIRE(module->id < lctx->module_count); ! 1437: ! 1438: if (! isc_log_wouldlog(lctx, level)) ! 1439: return; ! 1440: ! 1441: if (msgcat != NULL) ! 1442: iformat = isc_msgcat_get(msgcat, msgset, msg, format); ! 1443: else ! 1444: iformat = format; ! 1445: ! 1446: time_string[0] = '\0'; ! 1447: level_string[0] = '\0'; ! 1448: ! 1449: LOCK(&lctx->lock); ! 1450: ! 1451: lctx->buffer[0] = '\0'; ! 1452: ! 1453: lcfg = lctx->logconfig; ! 1454: ! 1455: category_channels = ISC_LIST_HEAD(lcfg->channellists[category->id]); ! 1456: ! 1457: /* ! 1458: * XXXDCL add duplicate filtering? (To not write multiple times to ! 1459: * the same source via various channels). ! 1460: */ ! 1461: do { ! 1462: /* ! 1463: * If the channel list end was reached and a match was made, ! 1464: * everything is finished. ! 1465: */ ! 1466: if (category_channels == NULL && matched) ! 1467: break; ! 1468: ! 1469: if (category_channels == NULL && ! matched && ! 1470: category_channels != ISC_LIST_HEAD(lcfg->channellists[0])) ! 1471: /* ! 1472: * No category/module pair was explicitly configured. ! 1473: * Try the category named "default". ! 1474: */ ! 1475: category_channels = ! 1476: ISC_LIST_HEAD(lcfg->channellists[0]); ! 1477: ! 1478: if (category_channels == NULL && ! matched) ! 1479: /* ! 1480: * No matching module was explicitly configured ! 1481: * for the category named "default". Use the internal ! 1482: * default channel. ! 1483: */ ! 1484: category_channels = &default_channel; ! 1485: ! 1486: if (category_channels->module != NULL && ! 1487: category_channels->module != module) { ! 1488: category_channels = ISC_LIST_NEXT(category_channels, ! 1489: link); ! 1490: continue; ! 1491: } ! 1492: ! 1493: matched = ISC_TRUE; ! 1494: ! 1495: channel = category_channels->channel; ! 1496: category_channels = ISC_LIST_NEXT(category_channels, link); ! 1497: ! 1498: if (((channel->flags & ISC_LOG_DEBUGONLY) != 0) && ! 1499: lctx->debug_level == 0) ! 1500: continue; ! 1501: ! 1502: if (channel->level == ISC_LOG_DYNAMIC) { ! 1503: if (lctx->debug_level < level) ! 1504: continue; ! 1505: } else if (channel->level < level) ! 1506: continue; ! 1507: ! 1508: if ((channel->flags & ISC_LOG_PRINTTIME) != 0 && ! 1509: time_string[0] == '\0') { ! 1510: isc_time_t isctime; ! 1511: ! 1512: TIME_NOW(&isctime); ! 1513: isc_time_formattimestamp(&isctime, time_string, ! 1514: sizeof(time_string)); ! 1515: } ! 1516: ! 1517: if ((channel->flags & ISC_LOG_PRINTLEVEL) != 0 && ! 1518: level_string[0] == '\0') { ! 1519: if (level < ISC_LOG_CRITICAL) ! 1520: snprintf(level_string, sizeof(level_string), ! 1521: isc_msgcat_get(isc_msgcat, ! 1522: ISC_MSGSET_LOG, ! 1523: ISC_MSG_LEVEL, ! 1524: "level %d: "), ! 1525: level); ! 1526: else if (level > ISC_LOG_DYNAMIC) ! 1527: snprintf(level_string, sizeof(level_string), ! 1528: "%s %d: ", log_level_strings[0], ! 1529: level); ! 1530: else ! 1531: snprintf(level_string, sizeof(level_string), ! 1532: "%s: ", log_level_strings[-level]); ! 1533: } ! 1534: ! 1535: /* ! 1536: * Only format the message once. ! 1537: */ ! 1538: if (lctx->buffer[0] == '\0') { ! 1539: (void)vsnprintf(lctx->buffer, sizeof(lctx->buffer), ! 1540: iformat, args); ! 1541: ! 1542: /* ! 1543: * Check for duplicates. ! 1544: */ ! 1545: if (write_once) { ! 1546: isc_logmessage_t *message, *new; ! 1547: isc_time_t oldest; ! 1548: isc_interval_t interval; ! 1549: ! 1550: isc_interval_set(&interval, ! 1551: lcfg->duplicate_interval, 0); ! 1552: ! 1553: /* ! 1554: * 'oldest' is the age of the oldest messages ! 1555: * which fall within the duplicate_interval ! 1556: * range. ! 1557: */ ! 1558: TIME_NOW(&oldest); ! 1559: if (isc_time_subtract(&oldest, &interval, &oldest) ! 1560: != ISC_R_SUCCESS) ! 1561: /* ! 1562: * Can't effectively do the checking ! 1563: * without having a valid time. ! 1564: */ ! 1565: message = NULL; ! 1566: else ! 1567: message =ISC_LIST_HEAD(lctx->messages); ! 1568: ! 1569: while (message != NULL) { ! 1570: if (isc_time_compare(&message->time, ! 1571: &oldest) < 0) { ! 1572: /* ! 1573: * This message is older ! 1574: * than the duplicate_interval, ! 1575: * so it should be dropped from ! 1576: * the history. ! 1577: * ! 1578: * Setting the interval to be ! 1579: * to be longer will obviously ! 1580: * not cause the expired ! 1581: * message to spring back into ! 1582: * existence. ! 1583: */ ! 1584: new = ISC_LIST_NEXT(message, ! 1585: link); ! 1586: ! 1587: ISC_LIST_UNLINK(lctx->messages, ! 1588: message, link); ! 1589: ! 1590: isc_mem_put(lctx->mctx, ! 1591: message, ! 1592: sizeof(*message) + 1 + ! 1593: strlen(message->text)); ! 1594: ! 1595: message = new; ! 1596: continue; ! 1597: } ! 1598: ! 1599: /* ! 1600: * This message is in the duplicate ! 1601: * filtering interval ... ! 1602: */ ! 1603: if (strcmp(lctx->buffer, message->text) ! 1604: == 0) { ! 1605: /* ! 1606: * ... and it is a duplicate. ! 1607: * Unlock the mutex and ! 1608: * get the hell out of Dodge. ! 1609: */ ! 1610: UNLOCK(&lctx->lock); ! 1611: return; ! 1612: } ! 1613: ! 1614: message = ISC_LIST_NEXT(message, link); ! 1615: } ! 1616: ! 1617: /* ! 1618: * It wasn't in the duplicate interval, ! 1619: * so add it to the message list. ! 1620: */ ! 1621: new = isc_mem_get(lctx->mctx, ! 1622: sizeof(isc_logmessage_t) + ! 1623: strlen(lctx->buffer) + 1); ! 1624: if (new != NULL) { ! 1625: /* ! 1626: * Put the text immediately after ! 1627: * the struct. The strcpy is safe. ! 1628: */ ! 1629: new->text = (char *)(new + 1); ! 1630: strcpy(new->text, lctx->buffer); ! 1631: ! 1632: TIME_NOW(&new->time); ! 1633: ! 1634: ISC_LIST_APPEND(lctx->messages, ! 1635: new, link); ! 1636: } ! 1637: } ! 1638: } ! 1639: ! 1640: printtime = ISC_TF((channel->flags & ISC_LOG_PRINTTIME) ! 1641: != 0); ! 1642: printtag = ISC_TF((channel->flags & ISC_LOG_PRINTTAG) ! 1643: != 0 && lcfg->tag != NULL); ! 1644: printcategory = ISC_TF((channel->flags & ISC_LOG_PRINTCATEGORY) ! 1645: != 0); ! 1646: printmodule = ISC_TF((channel->flags & ISC_LOG_PRINTMODULE) ! 1647: != 0); ! 1648: printlevel = ISC_TF((channel->flags & ISC_LOG_PRINTLEVEL) ! 1649: != 0); ! 1650: ! 1651: switch (channel->type) { ! 1652: case ISC_LOG_TOFILE: ! 1653: if (FILE_MAXREACHED(channel)) { ! 1654: /* ! 1655: * If the file can be rolled, OR ! 1656: * If the file no longer exists, OR ! 1657: * If the file is less than the maximum size, ! 1658: * (such as if it had been renamed and ! 1659: * a new one touched, or it was truncated ! 1660: * in place) ! 1661: * ... then close it to trigger reopening. ! 1662: */ ! 1663: if (FILE_VERSIONS(channel) != ! 1664: ISC_LOG_ROLLNEVER || ! 1665: (stat(FILE_NAME(channel), &statbuf) != 0 && ! 1666: errno == ENOENT) || ! 1667: statbuf.st_size < FILE_MAXSIZE(channel)) { ! 1668: (void)fclose(FILE_STREAM(channel)); ! 1669: FILE_STREAM(channel) = NULL; ! 1670: FILE_MAXREACHED(channel) = ISC_FALSE; ! 1671: } else ! 1672: /* ! 1673: * Eh, skip it. ! 1674: */ ! 1675: break; ! 1676: } ! 1677: ! 1678: if (FILE_STREAM(channel) == NULL) { ! 1679: result = isc_log_open(channel); ! 1680: if (result != ISC_R_SUCCESS && ! 1681: result != ISC_R_MAXSIZE && ! 1682: (channel->flags & ISC_LOG_OPENERR) == 0) { ! 1683: syslog(LOG_ERR, ! 1684: "isc_log_open '%s' failed: %s", ! 1685: FILE_NAME(channel), ! 1686: isc_result_totext(result)); ! 1687: channel->flags |= ISC_LOG_OPENERR; ! 1688: } ! 1689: if (result != ISC_R_SUCCESS) ! 1690: break; ! 1691: channel->flags &= ~ISC_LOG_OPENERR; ! 1692: } ! 1693: /* FALLTHROUGH */ ! 1694: ! 1695: case ISC_LOG_TOFILEDESC: ! 1696: fprintf(FILE_STREAM(channel), "%s%s%s%s%s%s%s%s%s%s\n", ! 1697: printtime ? time_string : "", ! 1698: printtime ? " " : "", ! 1699: printtag ? lcfg->tag : "", ! 1700: printtag ? ": " : "", ! 1701: printcategory ? category->name : "", ! 1702: printcategory ? ": " : "", ! 1703: printmodule ? (module != NULL ? module->name ! 1704: : "no_module") ! 1705: : "", ! 1706: printmodule ? ": " : "", ! 1707: printlevel ? level_string : "", ! 1708: lctx->buffer); ! 1709: ! 1710: fflush(FILE_STREAM(channel)); ! 1711: ! 1712: /* ! 1713: * If the file now exceeds its maximum size ! 1714: * threshold, note it so that it will not be logged ! 1715: * to any more. ! 1716: */ ! 1717: if (FILE_MAXSIZE(channel) > 0) { ! 1718: INSIST(channel->type == ISC_LOG_TOFILE); ! 1719: ! 1720: /* XXXDCL NT fstat/fileno */ ! 1721: /* XXXDCL complain if fstat fails? */ ! 1722: if (fstat(fileno(FILE_STREAM(channel)), ! 1723: &statbuf) >= 0 && ! 1724: statbuf.st_size > FILE_MAXSIZE(channel)) ! 1725: FILE_MAXREACHED(channel) = ISC_TRUE; ! 1726: } ! 1727: ! 1728: break; ! 1729: ! 1730: case ISC_LOG_TOSYSLOG: ! 1731: if (level > 0) ! 1732: syslog_level = LOG_DEBUG; ! 1733: else if (level < ISC_LOG_CRITICAL) ! 1734: syslog_level = LOG_CRIT; ! 1735: else ! 1736: syslog_level = syslog_map[-level]; ! 1737: ! 1738: (void)syslog(FACILITY(channel) | syslog_level, ! 1739: "%s%s%s%s%s%s%s%s%s%s", ! 1740: printtime ? time_string : "", ! 1741: printtime ? " " : "", ! 1742: printtag ? lcfg->tag : "", ! 1743: printtag ? ": " : "", ! 1744: printcategory ? category->name : "", ! 1745: printcategory ? ": " : "", ! 1746: printmodule ? (module != NULL ? module->name ! 1747: : "no_module") ! 1748: : "", ! 1749: printmodule ? ": " : "", ! 1750: printlevel ? level_string : "", ! 1751: lctx->buffer); ! 1752: break; ! 1753: ! 1754: case ISC_LOG_TONULL: ! 1755: break; ! 1756: ! 1757: } ! 1758: ! 1759: } while (1); ! 1760: ! 1761: UNLOCK(&lctx->lock); ! 1762: }