Annotation of embedaddon/ntp/lib/isc/log.c, revision 1.1
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>