diff options
Diffstat (limited to '')
-rw-r--r-- | bin/named/logconf.c | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/bin/named/logconf.c b/bin/named/logconf.c new file mode 100644 index 0000000..e1cd762 --- /dev/null +++ b/bin/named/logconf.c @@ -0,0 +1,338 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id: logconf.c,v 1.45 2011/03/05 23:52:29 tbox Exp $ */ + +/*! \file */ + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/file.h> +#include <isc/offset.h> +#include <isc/print.h> +#include <isc/result.h> +#include <isc/stdio.h> +#include <isc/string.h> +#include <isc/syslog.h> +#include <isc/util.h> + +#include <isccfg/cfg.h> +#include <isccfg/log.h> + +#include <named/log.h> +#include <named/logconf.h> + +#define CHECK(op) \ + do { result = (op); \ + if (result != ISC_R_SUCCESS) goto cleanup; \ + } while (0) + +/*% + * Set up a logging category according to the named.conf data + * in 'ccat' and add it to 'logconfig'. + */ +static isc_result_t +category_fromconf(const cfg_obj_t *ccat, isc_logconfig_t *logconfig) { + isc_result_t result; + const char *catname; + isc_logcategory_t *category; + isc_logmodule_t *module; + const cfg_obj_t *destinations = NULL; + const cfg_listelt_t *element = NULL; + + catname = cfg_obj_asstring(cfg_tuple_get(ccat, "name")); + category = isc_log_categorybyname(ns_g_lctx, catname); + if (category == NULL) { + cfg_obj_log(ccat, ns_g_lctx, ISC_LOG_ERROR, + "unknown logging category '%s' ignored", + catname); + /* + * Allow further processing by returning success. + */ + return (ISC_R_SUCCESS); + } + + if (logconfig == NULL) + return (ISC_R_SUCCESS); + + module = NULL; + + destinations = cfg_tuple_get(ccat, "destinations"); + for (element = cfg_list_first(destinations); + element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *channel = cfg_listelt_value(element); + const char *channelname = cfg_obj_asstring(channel); + + result = isc_log_usechannel(logconfig, channelname, category, + module); + if (result != ISC_R_SUCCESS) { + isc_log_write(ns_g_lctx, CFG_LOGCATEGORY_CONFIG, + NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "logging channel '%s': %s", channelname, + isc_result_totext(result)); + return (result); + } + } + return (ISC_R_SUCCESS); +} + +/*% + * Set up a logging channel according to the named.conf data + * in 'cchan' and add it to 'logconfig'. + */ +static isc_result_t +channel_fromconf(const cfg_obj_t *channel, isc_logconfig_t *logconfig) +{ + isc_result_t result; + isc_logdestination_t dest; + unsigned int type; + unsigned int flags = 0; + int level; + const char *channelname; + const cfg_obj_t *fileobj = NULL; + const cfg_obj_t *syslogobj = NULL; + const cfg_obj_t *nullobj = NULL; + const cfg_obj_t *stderrobj = NULL; + const cfg_obj_t *severity = NULL; + int i; + + channelname = cfg_obj_asstring(cfg_map_getname(channel)); + + (void)cfg_map_get(channel, "file", &fileobj); + (void)cfg_map_get(channel, "syslog", &syslogobj); + (void)cfg_map_get(channel, "null", &nullobj); + (void)cfg_map_get(channel, "stderr", &stderrobj); + + i = 0; + if (fileobj != NULL) + i++; + if (syslogobj != NULL) + i++; + if (nullobj != NULL) + i++; + if (stderrobj != NULL) + i++; + + if (i != 1) { + cfg_obj_log(channel, ns_g_lctx, ISC_LOG_ERROR, + "channel '%s': exactly one of file, syslog, " + "null, and stderr must be present", channelname); + return (ISC_R_FAILURE); + } + + type = ISC_LOG_TONULL; + + if (fileobj != NULL) { + const cfg_obj_t *pathobj = cfg_tuple_get(fileobj, "file"); + const cfg_obj_t *sizeobj = cfg_tuple_get(fileobj, "size"); + const cfg_obj_t *versionsobj = + cfg_tuple_get(fileobj, "versions"); + int32_t versions = ISC_LOG_ROLLNEVER; + isc_offset_t size = 0; + uint64_t maxoffset; + + /* + * isc_offset_t is a signed integer type, so the maximum + * value is all 1s except for the MSB. + */ + switch (sizeof(isc_offset_t)) { + case 4: + maxoffset = 0x7fffffffULL; + break; + case 8: + maxoffset = 0x7fffffffffffffffULL; + break; + default: + INSIST(0); + } + + type = ISC_LOG_TOFILE; + + if (versionsobj != NULL && cfg_obj_isuint32(versionsobj)) + versions = cfg_obj_asuint32(versionsobj); + if (versionsobj != NULL && cfg_obj_isstring(versionsobj) && + strcasecmp(cfg_obj_asstring(versionsobj), "unlimited") == 0) + versions = ISC_LOG_ROLLINFINITE; + if (sizeobj != NULL && + cfg_obj_isuint64(sizeobj) && + cfg_obj_asuint64(sizeobj) < maxoffset) + size = (isc_offset_t)cfg_obj_asuint64(sizeobj); + dest.file.stream = NULL; + dest.file.name = cfg_obj_asstring(pathobj); + dest.file.versions = versions; + dest.file.maximum_size = size; + } else if (syslogobj != NULL) { + int facility = LOG_DAEMON; + + type = ISC_LOG_TOSYSLOG; + + if (cfg_obj_isstring(syslogobj)) { + const char *facilitystr = cfg_obj_asstring(syslogobj); + (void)isc_syslog_facilityfromstring(facilitystr, + &facility); + } + dest.facility = facility; + } else if (stderrobj != NULL) { + type = ISC_LOG_TOFILEDESC; + dest.file.stream = stderr; + dest.file.name = NULL; + dest.file.versions = ISC_LOG_ROLLNEVER; + dest.file.maximum_size = 0; + } + + /* + * Munge flags. + */ + { + const cfg_obj_t *printcat = NULL; + const cfg_obj_t *printsev = NULL; + const cfg_obj_t *printtime = NULL; + const cfg_obj_t *buffered = NULL; + + (void)cfg_map_get(channel, "print-category", &printcat); + (void)cfg_map_get(channel, "print-severity", &printsev); + (void)cfg_map_get(channel, "print-time", &printtime); + (void)cfg_map_get(channel, "buffered", &buffered); + + if (printcat != NULL && cfg_obj_asboolean(printcat)) + flags |= ISC_LOG_PRINTCATEGORY; + if (printtime != NULL && cfg_obj_asboolean(printtime)) + flags |= ISC_LOG_PRINTTIME; + if (printsev != NULL && cfg_obj_asboolean(printsev)) + flags |= ISC_LOG_PRINTLEVEL; + if (buffered != NULL && cfg_obj_asboolean(buffered)) + flags |= ISC_LOG_BUFFERED; + } + + level = ISC_LOG_INFO; + if (cfg_map_get(channel, "severity", &severity) == ISC_R_SUCCESS) { + if (cfg_obj_isstring(severity)) { + const char *str = cfg_obj_asstring(severity); + if (strcasecmp(str, "critical") == 0) + level = ISC_LOG_CRITICAL; + else if (strcasecmp(str, "error") == 0) + level = ISC_LOG_ERROR; + else if (strcasecmp(str, "warning") == 0) + level = ISC_LOG_WARNING; + else if (strcasecmp(str, "notice") == 0) + level = ISC_LOG_NOTICE; + else if (strcasecmp(str, "info") == 0) + level = ISC_LOG_INFO; + else if (strcasecmp(str, "dynamic") == 0) + level = ISC_LOG_DYNAMIC; + } else + /* debug */ + level = cfg_obj_asuint32(severity); + } + + if (logconfig == NULL) + result = ISC_R_SUCCESS; + else + result = isc_log_createchannel(logconfig, channelname, + type, level, &dest, flags); + + if (result == ISC_R_SUCCESS && type == ISC_LOG_TOFILE) { + FILE *fp; + + /* + * Test to make sure that file is a plain file. + * Fix defect #22771 + */ + result = isc_file_isplainfile(dest.file.name); + if (result == ISC_R_SUCCESS || result == ISC_R_FILENOTFOUND) { + /* + * Test that the file can be opened, since + * isc_log_open() can't effectively report + * failures when called in isc_log_doit(). + */ + result = isc_stdio_open(dest.file.name, "a", &fp); + if (result != ISC_R_SUCCESS) { + if (logconfig != NULL && !ns_g_nosyslog) + syslog(LOG_ERR, + "isc_stdio_open '%s' failed: " + "%s", dest.file.name, + isc_result_totext(result)); + fprintf(stderr, + "isc_stdio_open '%s' failed: %s\n", + dest.file.name, + isc_result_totext(result)); + } else + (void)isc_stdio_close(fp); + goto done; + } + if (logconfig != NULL && !ns_g_nosyslog) + syslog(LOG_ERR, "isc_file_isplainfile '%s' failed: %s", + dest.file.name, isc_result_totext(result)); + fprintf(stderr, "isc_file_isplainfile '%s' failed: %s\n", + dest.file.name, isc_result_totext(result)); + } + + done: + return (result); +} + +isc_result_t +ns_log_configure(isc_logconfig_t *logconfig, const cfg_obj_t *logstmt) { + isc_result_t result; + const cfg_obj_t *channels = NULL; + const cfg_obj_t *categories = NULL; + const cfg_listelt_t *element; + bool default_set = false; + bool unmatched_set = false; + const cfg_obj_t *catname; + + if (logconfig != NULL) + CHECK(ns_log_setdefaultchannels(logconfig)); + + (void)cfg_map_get(logstmt, "channel", &channels); + for (element = cfg_list_first(channels); + element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *channel = cfg_listelt_value(element); + CHECK(channel_fromconf(channel, logconfig)); + } + + (void)cfg_map_get(logstmt, "category", &categories); + for (element = cfg_list_first(categories); + element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *category = cfg_listelt_value(element); + CHECK(category_fromconf(category, logconfig)); + if (!default_set) { + catname = cfg_tuple_get(category, "name"); + if (strcmp(cfg_obj_asstring(catname), "default") == 0) + default_set = true; + } + if (!unmatched_set) { + catname = cfg_tuple_get(category, "name"); + if (strcmp(cfg_obj_asstring(catname), "unmatched") == 0) + unmatched_set = true; + } + } + + if (logconfig != NULL && !default_set) + CHECK(ns_log_setdefaultcategory(logconfig)); + + if (logconfig != NULL && !unmatched_set) + CHECK(ns_log_setunmatchedcategory(logconfig)); + + return (ISC_R_SUCCESS); + + cleanup: + return (result); +} |