summaryrefslogtreecommitdiffstats
path: root/bin/named/logconf.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--bin/named/logconf.c373
1 files changed, 373 insertions, 0 deletions
diff --git a/bin/named/logconf.c b/bin/named/logconf.c
new file mode 100644
index 0000000..36a19ba
--- /dev/null
+++ b/bin/named/logconf.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * 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 https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#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(named_g_lctx, catname);
+ if (category == NULL) {
+ cfg_obj_log(ccat, named_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(named_g_lctx, CFG_LOGCATEGORY_CONFIG,
+ NAMED_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_R_SUCCESS;
+ 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, named_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");
+ const cfg_obj_t *suffixobj = cfg_tuple_get(fileobj, "suffix");
+ int32_t versions = ISC_LOG_ROLLNEVER;
+ isc_log_rollsuffix_t suffix = isc_log_rollsuffix_increment;
+ 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:
+ UNREACHABLE();
+ }
+
+ type = ISC_LOG_TOFILE;
+
+ if (versionsobj != NULL && cfg_obj_isuint32(versionsobj)) {
+ versions = cfg_obj_asuint32(versionsobj);
+ } else 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);
+ }
+ if (suffixobj != NULL && cfg_obj_isstring(suffixobj) &&
+ strcasecmp(cfg_obj_asstring(suffixobj), "timestamp") == 0)
+ {
+ suffix = isc_log_rollsuffix_timestamp;
+ }
+
+ dest.file.stream = NULL;
+ dest.file.name = cfg_obj_asstring(pathobj);
+ dest.file.versions = versions;
+ dest.file.suffix = suffix;
+ 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.suffix = isc_log_rollsuffix_increment;
+ 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 (printsev != NULL && cfg_obj_asboolean(printsev)) {
+ flags |= ISC_LOG_PRINTLEVEL;
+ }
+ if (buffered != NULL && cfg_obj_asboolean(buffered)) {
+ flags |= ISC_LOG_BUFFERED;
+ }
+ if (printtime != NULL && cfg_obj_isboolean(printtime)) {
+ if (cfg_obj_asboolean(printtime)) {
+ flags |= ISC_LOG_PRINTTIME;
+ }
+ } else if (printtime != NULL) { /* local/iso8601/iso8601-utc */
+ const char *s = cfg_obj_asstring(printtime);
+ flags |= ISC_LOG_PRINTTIME;
+ if (strcasecmp(s, "iso8601") == 0) {
+ flags |= ISC_LOG_ISO8601;
+ } else if (strcasecmp(s, "iso8601-utc") == 0) {
+ flags |= ISC_LOG_ISO8601 | ISC_LOG_UTC;
+ }
+ }
+ }
+
+ 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) {
+ isc_log_createchannel(logconfig, channelname, type, level,
+ &dest, flags);
+ }
+
+ if (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 && !named_g_nosyslog) {
+ syslog(LOG_ERR,
+ "isc_stdio_open '%s' failed: "
+ "%s",
+ dest.file.name,
+ isc_result_totext(result));
+ }
+ } else {
+ (void)isc_stdio_close(fp);
+ }
+ goto done;
+ }
+ if (logconfig != NULL && !named_g_nosyslog) {
+ syslog(LOG_ERR, "isc_file_isplainfile '%s' failed: %s",
+ dest.file.name, isc_result_totext(result));
+ }
+ }
+
+done:
+ return (result);
+}
+
+isc_result_t
+named_logconfig(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) {
+ named_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(named_log_setdefaultcategory(logconfig));
+ }
+
+ if (logconfig != NULL && !unmatched_set) {
+ CHECK(named_log_setunmatchedcategory(logconfig));
+ }
+
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ return (result);
+}