372 lines
9.8 KiB
C
372 lines
9.8 KiB
C
/*
|
|
* 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/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;
|
|
off_t size = 0;
|
|
uint64_t maxoffset;
|
|
|
|
/*
|
|
* off_t is a signed integer type, so the maximum
|
|
* value is all 1s except for the MSB.
|
|
*/
|
|
switch (sizeof(off_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 = (off_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);
|
|
named_log_setdefaultsslkeylogfile(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;
|
|
}
|