📄 log.c
字号:
/* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. *//* $Id: log.c,v 1.70.2.8.2.12 2004/06/11 00:35:38 marka Exp $ *//* Principal Authors: DCL */#include <config.h>#include <errno.h>#include <stdlib.h>#include <limits.h>#include <time.h>#include <sys/types.h> /* dev_t FreeBSD 2.1 */#include <isc/dir.h>#include <isc/file.h>#include <isc/log.h>#include <isc/magic.h>#include <isc/mem.h>#include <isc/msgs.h>#include <isc/print.h>#include <isc/stat.h>#include <isc/stdio.h>#include <isc/string.h>#include <isc/time.h>#include <isc/util.h>#define LCTX_MAGIC ISC_MAGIC('L', 'c', 't', 'x')#define VALID_CONTEXT(lctx) ISC_MAGIC_VALID(lctx, LCTX_MAGIC)#define LCFG_MAGIC ISC_MAGIC('L', 'c', 'f', 'g')#define VALID_CONFIG(lcfg) ISC_MAGIC_VALID(lcfg, LCFG_MAGIC)/* * XXXDCL make dynamic? */#define LOG_BUFFER_SIZE (8 * 1024)#ifndef PATH_MAX#define PATH_MAX 1024 /* AIX and others don't define this. */#endif/* * This is the structure that holds each named channel. A simple linked * list chains all of the channels together, so an individual channel is * found by doing strcmp()s with the names down the list. Their should * be no peformance penalty from this as it is expected that the number * of named channels will be no more than a dozen or so, and name lookups * from the head of the list are only done when isc_log_usechannel() is * called, which should also be very infrequent. */typedef struct isc_logchannel isc_logchannel_t;struct isc_logchannel { char * name; unsigned int type; int level; unsigned int flags; isc_logdestination_t destination; ISC_LINK(isc_logchannel_t) link;};/* * The logchannellist structure associates categories and modules with * channels. First the appropriate channellist is found based on the * category, and then each structure in the linked list is checked for * a matching module. It is expected that the number of channels * associated with any given category will be very short, no more than * three or four in the more unusual cases. */typedef struct isc_logchannellist isc_logchannellist_t;struct isc_logchannellist { const isc_logmodule_t * module; isc_logchannel_t * channel; ISC_LINK(isc_logchannellist_t) link;};/* * This structure is used to remember messages for pruning via * isc_log_[v]write1(). */typedef struct isc_logmessage isc_logmessage_t;struct isc_logmessage { char * text; isc_time_t time; ISC_LINK(isc_logmessage_t) link;};/* * The isc_logconfig structure is used to store the configurable information * about where messages are actually supposed to be sent -- the information * that could changed based on some configuration file, as opposed to the * the category/module specification of isc_log_[v]write[1] that is compiled * into a program, or the debug_level which is dynamic state information. */struct isc_logconfig { unsigned int magic; isc_log_t * lctx; ISC_LIST(isc_logchannel_t) channels; ISC_LIST(isc_logchannellist_t) *channellists; unsigned int channellist_count; unsigned int duplicate_interval; int highest_level; char * tag; isc_boolean_t dynamic;};/* * This isc_log structure provides the context for the isc_log functions. * The log context locks itself in isc_log_doit, the internal backend to * isc_log_write. The locking is necessary both to provide exclusive access * to the the buffer into which the message is formatted and to guard against * competing threads trying to write to the same syslog resource. (On * some systems, such as BSD/OS, stdio is thread safe but syslog is not.) * Unfortunately, the lock cannot guard against a _different_ logging * context in the same program competing for syslog's attention. Thus * There Can Be Only One, but this is not enforced. * XXXDCL enforce it? * * Note that the category and module information is not locked. * This is because in the usual case, only one isc_log_t is ever created * in a program, and the category/module registration happens only once. * XXXDCL it might be wise to add more locking overall. */struct isc_log { /* Not locked. */ unsigned int magic; isc_mem_t * mctx; isc_logcategory_t * categories; unsigned int category_count; isc_logmodule_t * modules; unsigned int module_count; int debug_level; isc_mutex_t lock; /* Locked by isc_log lock. */ isc_logconfig_t * logconfig; char buffer[LOG_BUFFER_SIZE]; ISC_LIST(isc_logmessage_t) messages;};/* * Used when ISC_LOG_PRINTLEVEL is enabled for a channel. */static const char *log_level_strings[] = { "debug", "info", "notice", "warning", "error", "critical"};/* * Used to convert ISC_LOG_* priorities into syslog priorities. * XXXDCL This will need modification for NT. */static const int syslog_map[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, LOG_CRIT};/* * When adding new categories, a corresponding ISC_LOGCATEGORY_foo * definition needs to be added to <isc/log.h>. * * The default category is provided so that the internal default can * be overridden. Since the default is always looked up as the first * channellist in the log context, it must come first in isc_categories[]. */LIBISC_EXTERNAL_DATA isc_logcategory_t isc_categories[] = { { "default", 0 }, /* "default" must come first. */ { "general", 0 }, { NULL, 0 }};/* * See above comment for categories, and apply it to modules. */LIBISC_EXTERNAL_DATA isc_logmodule_t isc_modules[] = { { "socket", 0 }, { "time", 0 }, { "interface", 0 }, { "timer", 0 }, { NULL, 0 }};/* * This essentially constant structure must be filled in at run time, * because its channel member is pointed to a channel that is created * dynamically with isc_log_createchannel. */static isc_logchannellist_t default_channel;/* * libisc logs to this context. */LIBISC_EXTERNAL_DATA isc_log_t *isc_lctx = NULL;/* * Forward declarations. */static isc_result_tassignchannel(isc_logconfig_t *lcfg, unsigned int category_id, const isc_logmodule_t *module, isc_logchannel_t *channel);static isc_result_tsync_channellist(isc_logconfig_t *lcfg);static isc_result_tgreatest_version(isc_logchannel_t *channel, int *greatest);static isc_result_troll_log(isc_logchannel_t *channel);static voidisc_log_doit(isc_log_t *lctx, isc_logcategory_t *category, isc_logmodule_t *module, int level, isc_boolean_t write_once, isc_msgcat_t *msgcat, int msgset, int msg, const char *format, va_list args) ISC_FORMAT_PRINTF(9, 0);/* * Convenience macros. */#define FACILITY(channel) (channel->destination.facility)#define FILE_NAME(channel) (channel->destination.file.name)#define FILE_STREAM(channel) (channel->destination.file.stream)#define FILE_VERSIONS(channel) (channel->destination.file.versions)#define FILE_MAXSIZE(channel) (channel->destination.file.maximum_size)#define FILE_MAXREACHED(channel) (channel->destination.file.maximum_reached)/**** **** Public interfaces. ****//* * Establish a new logging context, with default channels. */isc_result_tisc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp) { isc_log_t *lctx; isc_logconfig_t *lcfg = NULL; isc_result_t result; REQUIRE(mctx != NULL); REQUIRE(lctxp != NULL && *lctxp == NULL); REQUIRE(lcfgp == NULL || *lcfgp == NULL); lctx = isc_mem_get(mctx, sizeof(*lctx)); if (lctx != NULL) { lctx->mctx = mctx; lctx->categories = NULL; lctx->category_count = 0; lctx->modules = NULL; lctx->module_count = 0; lctx->debug_level = 0; ISC_LIST_INIT(lctx->messages); RUNTIME_CHECK(isc_mutex_init(&lctx->lock) == ISC_R_SUCCESS); /* * Normally setting the magic number is the last step done * in a creation function, but a valid log context is needed * by isc_log_registercategories and isc_logconfig_create. * If either fails, the lctx is destroyed and not returned * to the caller. */ lctx->magic = LCTX_MAGIC; isc_log_registercategories(lctx, isc_categories); isc_log_registermodules(lctx, isc_modules); result = isc_logconfig_create(lctx, &lcfg); } else result = ISC_R_NOMEMORY; if (result == ISC_R_SUCCESS) result = sync_channellist(lcfg); if (result == ISC_R_SUCCESS) { lctx->logconfig = lcfg; *lctxp = lctx; if (lcfgp != NULL) *lcfgp = lcfg; } else { if (lcfg != NULL) isc_logconfig_destroy(&lcfg); if (lctx != NULL) isc_log_destroy(&lctx); } return (result);}isc_result_tisc_logconfig_create(isc_log_t *lctx, isc_logconfig_t **lcfgp) { isc_logconfig_t *lcfg; isc_logdestination_t destination; isc_result_t result = ISC_R_SUCCESS; int level = ISC_LOG_INFO; REQUIRE(lcfgp != NULL && *lcfgp == NULL); REQUIRE(VALID_CONTEXT(lctx)); lcfg = isc_mem_get(lctx->mctx, sizeof(*lcfg)); if (lcfg != NULL) { lcfg->lctx = lctx; lcfg->channellists = NULL; lcfg->channellist_count = 0; lcfg->duplicate_interval = 0; lcfg->highest_level = level; lcfg->tag = NULL; lcfg->dynamic = ISC_FALSE; ISC_LIST_INIT(lcfg->channels); /* * Normally the magic number is the last thing set in the * structure, but isc_log_createchannel() needs a valid * config. If the channel creation fails, the lcfg is not * returned to the caller. */ lcfg->magic = LCFG_MAGIC; } else result = ISC_R_NOMEMORY; /* * Create the default channels: * default_syslog, default_stderr, default_debug and null. */ if (result == ISC_R_SUCCESS) { destination.facility = LOG_DAEMON; result = isc_log_createchannel(lcfg, "default_syslog", ISC_LOG_TOSYSLOG, level, &destination, 0); } if (result == ISC_R_SUCCESS) { destination.file.stream = stderr; destination.file.name = NULL; destination.file.versions = ISC_LOG_ROLLNEVER; destination.file.maximum_size = 0; result = isc_log_createchannel(lcfg, "default_stderr", ISC_LOG_TOFILEDESC, level, &destination, ISC_LOG_PRINTTIME); } if (result == ISC_R_SUCCESS) { /* * Set the default category's channel to default_stderr, * which is at the head of the channels list because it was * just created. */ default_channel.channel = ISC_LIST_HEAD(lcfg->channels); destination.file.stream = stderr; destination.file.name = NULL; destination.file.versions = ISC_LOG_ROLLNEVER; destination.file.maximum_size = 0; result = isc_log_createchannel(lcfg, "default_debug", ISC_LOG_TOFILEDESC, ISC_LOG_DYNAMIC, &destination, ISC_LOG_PRINTTIME); } if (result == ISC_R_SUCCESS) result = isc_log_createchannel(lcfg, "null", ISC_LOG_TONULL, ISC_LOG_DYNAMIC, NULL, 0); if (result == ISC_R_SUCCESS) *lcfgp = lcfg; else if (lcfg != NULL) isc_logconfig_destroy(&lcfg); return (result);}isc_logconfig_t *isc_logconfig_get(isc_log_t *lctx) { REQUIRE(VALID_CONTEXT(lctx)); ENSURE(lctx->logconfig != NULL); return (lctx->logconfig);}isc_result_tisc_logconfig_use(isc_log_t *lctx, isc_logconfig_t *lcfg) { isc_logconfig_t *old_cfg; isc_result_t result; REQUIRE(VALID_CONTEXT(lctx)); REQUIRE(VALID_CONFIG(lcfg)); REQUIRE(lcfg->lctx == lctx); /* * Ensure that lcfg->channellist_count == lctx->category_count. * They won't be equal if isc_log_usechannel has not been called * since any call to isc_log_registercategories. */ result = sync_channellist(lcfg); if (result != ISC_R_SUCCESS) return (result); LOCK(&lctx->lock); old_cfg = lctx->logconfig; lctx->logconfig = lcfg; UNLOCK(&lctx->lock); isc_logconfig_destroy(&old_cfg); return (ISC_R_SUCCESS);}voidisc_log_destroy(isc_log_t **lctxp) { isc_log_t *lctx; isc_logconfig_t *lcfg; isc_mem_t *mctx; isc_logmessage_t *message; REQUIRE(lctxp != NULL && VALID_CONTEXT(*lctxp)); lctx = *lctxp; mctx = lctx->mctx; if (lctx->logconfig != NULL) { lcfg = lctx->logconfig; lctx->logconfig = NULL; isc_logconfig_destroy(&lcfg); } DESTROYLOCK(&lctx->lock); while ((message = ISC_LIST_HEAD(lctx->messages)) != NULL) { ISC_LIST_UNLINK(lctx->messages, message, link); isc_mem_put(mctx, message, sizeof(*message) + strlen(message->text) + 1); } lctx->buffer[0] = '\0'; lctx->debug_level = 0; lctx->categories = NULL; lctx->category_count = 0; lctx->modules = NULL; lctx->module_count = 0; lctx->mctx = NULL; lctx->magic = 0; isc_mem_put(mctx, lctx, sizeof(*lctx)); *lctxp = NULL;}voidisc_logconfig_destroy(isc_logconfig_t **lcfgp) { isc_logconfig_t *lcfg; isc_mem_t *mctx; isc_logchannel_t *channel; isc_logchannellist_t *item; char *filename; unsigned int i; REQUIRE(lcfgp != NULL && VALID_CONFIG(*lcfgp)); lcfg = *lcfgp; /* * This function cannot be called with a logconfig that is in * use by a log context. */ REQUIRE(lcfg->lctx != NULL && lcfg->lctx->logconfig != lcfg); mctx = lcfg->lctx->mctx; while ((channel = ISC_LIST_HEAD(lcfg->channels)) != NULL) { ISC_LIST_UNLINK(lcfg->channels, channel, link); if (channel->type == ISC_LOG_TOFILE) { /* * The filename for the channel may have ultimately * started its life in user-land as a const string, * but in isc_log_createchannel it gets copied * into writable memory and is not longer truly const. */ DE_CONST(FILE_NAME(channel), filename); isc_mem_free(mctx, filename); if (FILE_STREAM(channel) != NULL) (void)fclose(FILE_STREAM(channel)); } isc_mem_free(mctx, channel->name); isc_mem_put(mctx, channel, sizeof(*channel)); } for (i = 0; i < lcfg->channellist_count; i++) while ((item = ISC_LIST_HEAD(lcfg->channellists[i])) != NULL) { ISC_LIST_UNLINK(lcfg->channellists[i], item, link); isc_mem_put(mctx, item, sizeof(*item)); } if (lcfg->channellist_count > 0) isc_mem_put(mctx, lcfg->channellists, lcfg->channellist_count * sizeof(ISC_LIST(isc_logchannellist_t))); lcfg->dynamic = ISC_FALSE; if (lcfg->tag != NULL) isc_mem_free(lcfg->lctx->mctx, lcfg->tag); lcfg->tag = NULL; lcfg->highest_level = 0; lcfg->duplicate_interval = 0; lcfg->magic = 0; isc_mem_put(mctx, lcfg, sizeof(*lcfg)); *lcfgp = NULL;}voidisc_log_registercategories(isc_log_t *lctx, isc_logcategory_t categories[]) { isc_logcategory_t *catp; REQUIRE(VALID_CONTEXT(lctx)); REQUIRE(categories != NULL && categories[0].name != NULL); /* * XXXDCL This somewhat sleazy situation of using the last pointer * in one category array to point to the next array exists because * this registration function returns void and I didn't want to have * change everything that used it by making it return an isc_result_t. * It would need to do that if it had to allocate memory to store * pointers to each array passed in. */ if (lctx->categories == NULL) lctx->categories = categories; else { /* * Adjust the last (NULL) pointer of the already registered * categories to point to the incoming array. */ for (catp = lctx->categories; catp->name != NULL; ) if (catp->id == UINT_MAX) /* * The name pointer points to the next array.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -