1135446Strhodes/* 2254402Serwin * Copyright (C) 2004-2007, 2011, 2013 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2001 Internet Software Consortium. 4135446Strhodes * 5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18254897Serwin/* $Id: logconf.c,v 1.45 2011/03/05 23:52:29 tbox Exp $ */ 19135446Strhodes 20170222Sdougb/*! \file */ 21170222Sdougb 22135446Strhodes#include <config.h> 23135446Strhodes 24225361Sdougb#include <isc/file.h> 25135446Strhodes#include <isc/offset.h> 26135446Strhodes#include <isc/result.h> 27135446Strhodes#include <isc/stdio.h> 28135446Strhodes#include <isc/string.h> 29135446Strhodes#include <isc/syslog.h> 30135446Strhodes 31135446Strhodes#include <isccfg/cfg.h> 32135446Strhodes#include <isccfg/log.h> 33135446Strhodes 34135446Strhodes#include <named/log.h> 35135446Strhodes#include <named/logconf.h> 36135446Strhodes 37135446Strhodes#define CHECK(op) \ 38135446Strhodes do { result = (op); \ 39135446Strhodes if (result != ISC_R_SUCCESS) goto cleanup; \ 40135446Strhodes } while (0) 41135446Strhodes 42170222Sdougb/*% 43135446Strhodes * Set up a logging category according to the named.conf data 44262706Serwin * in 'ccat' and add it to 'logconfig'. 45135446Strhodes */ 46135446Strhodesstatic isc_result_t 47262706Serwincategory_fromconf(const cfg_obj_t *ccat, isc_logconfig_t *logconfig) { 48135446Strhodes isc_result_t result; 49135446Strhodes const char *catname; 50135446Strhodes isc_logcategory_t *category; 51135446Strhodes isc_logmodule_t *module; 52165071Sdougb const cfg_obj_t *destinations = NULL; 53165071Sdougb const cfg_listelt_t *element = NULL; 54135446Strhodes 55135446Strhodes catname = cfg_obj_asstring(cfg_tuple_get(ccat, "name")); 56135446Strhodes category = isc_log_categorybyname(ns_g_lctx, catname); 57135446Strhodes if (category == NULL) { 58135446Strhodes cfg_obj_log(ccat, ns_g_lctx, ISC_LOG_ERROR, 59135446Strhodes "unknown logging category '%s' ignored", 60135446Strhodes catname); 61135446Strhodes /* 62135446Strhodes * Allow further processing by returning success. 63135446Strhodes */ 64135446Strhodes return (ISC_R_SUCCESS); 65135446Strhodes } 66135446Strhodes 67262706Serwin if (logconfig == NULL) 68262706Serwin return (ISC_R_SUCCESS); 69262706Serwin 70135446Strhodes module = NULL; 71135446Strhodes 72135446Strhodes destinations = cfg_tuple_get(ccat, "destinations"); 73135446Strhodes for (element = cfg_list_first(destinations); 74135446Strhodes element != NULL; 75135446Strhodes element = cfg_list_next(element)) 76135446Strhodes { 77165071Sdougb const cfg_obj_t *channel = cfg_listelt_value(element); 78165071Sdougb const char *channelname = cfg_obj_asstring(channel); 79135446Strhodes 80262706Serwin result = isc_log_usechannel(logconfig, channelname, category, 81135446Strhodes module); 82135446Strhodes if (result != ISC_R_SUCCESS) { 83135446Strhodes isc_log_write(ns_g_lctx, CFG_LOGCATEGORY_CONFIG, 84135446Strhodes NS_LOGMODULE_SERVER, ISC_LOG_ERROR, 85135446Strhodes "logging channel '%s': %s", channelname, 86135446Strhodes isc_result_totext(result)); 87135446Strhodes return (result); 88135446Strhodes } 89135446Strhodes } 90135446Strhodes return (ISC_R_SUCCESS); 91135446Strhodes} 92135446Strhodes 93170222Sdougb/*% 94135446Strhodes * Set up a logging channel according to the named.conf data 95262706Serwin * in 'cchan' and add it to 'logconfig'. 96135446Strhodes */ 97135446Strhodesstatic isc_result_t 98262706Serwinchannel_fromconf(const cfg_obj_t *channel, isc_logconfig_t *logconfig) 99262706Serwin{ 100135446Strhodes isc_result_t result; 101135446Strhodes isc_logdestination_t dest; 102135446Strhodes unsigned int type; 103135446Strhodes unsigned int flags = 0; 104135446Strhodes int level; 105135446Strhodes const char *channelname; 106165071Sdougb const cfg_obj_t *fileobj = NULL; 107165071Sdougb const cfg_obj_t *syslogobj = NULL; 108165071Sdougb const cfg_obj_t *nullobj = NULL; 109165071Sdougb const cfg_obj_t *stderrobj = NULL; 110165071Sdougb const cfg_obj_t *severity = NULL; 111135446Strhodes int i; 112135446Strhodes 113135446Strhodes channelname = cfg_obj_asstring(cfg_map_getname(channel)); 114135446Strhodes 115135446Strhodes (void)cfg_map_get(channel, "file", &fileobj); 116135446Strhodes (void)cfg_map_get(channel, "syslog", &syslogobj); 117135446Strhodes (void)cfg_map_get(channel, "null", &nullobj); 118135446Strhodes (void)cfg_map_get(channel, "stderr", &stderrobj); 119135446Strhodes 120135446Strhodes i = 0; 121135446Strhodes if (fileobj != NULL) 122135446Strhodes i++; 123135446Strhodes if (syslogobj != NULL) 124135446Strhodes i++; 125135446Strhodes if (nullobj != NULL) 126135446Strhodes i++; 127135446Strhodes if (stderrobj != NULL) 128135446Strhodes i++; 129135446Strhodes 130135446Strhodes if (i != 1) { 131135446Strhodes cfg_obj_log(channel, ns_g_lctx, ISC_LOG_ERROR, 132135446Strhodes "channel '%s': exactly one of file, syslog, " 133135446Strhodes "null, and stderr must be present", channelname); 134135446Strhodes return (ISC_R_FAILURE); 135135446Strhodes } 136135446Strhodes 137135446Strhodes type = ISC_LOG_TONULL; 138225361Sdougb 139135446Strhodes if (fileobj != NULL) { 140165071Sdougb const cfg_obj_t *pathobj = cfg_tuple_get(fileobj, "file"); 141165071Sdougb const cfg_obj_t *sizeobj = cfg_tuple_get(fileobj, "size"); 142165071Sdougb const cfg_obj_t *versionsobj = 143165071Sdougb cfg_tuple_get(fileobj, "versions"); 144135446Strhodes isc_int32_t versions = ISC_LOG_ROLLNEVER; 145135446Strhodes isc_offset_t size = 0; 146135446Strhodes 147135446Strhodes type = ISC_LOG_TOFILE; 148225361Sdougb 149135446Strhodes if (versionsobj != NULL && cfg_obj_isuint32(versionsobj)) 150135446Strhodes versions = cfg_obj_asuint32(versionsobj); 151135446Strhodes if (versionsobj != NULL && cfg_obj_isstring(versionsobj) && 152135446Strhodes strcasecmp(cfg_obj_asstring(versionsobj), "unlimited") == 0) 153135446Strhodes versions = ISC_LOG_ROLLINFINITE; 154135446Strhodes if (sizeobj != NULL && 155135446Strhodes cfg_obj_isuint64(sizeobj) && 156135446Strhodes cfg_obj_asuint64(sizeobj) < ISC_OFFSET_MAXIMUM) 157135446Strhodes size = (isc_offset_t)cfg_obj_asuint64(sizeobj); 158135446Strhodes dest.file.stream = NULL; 159135446Strhodes dest.file.name = cfg_obj_asstring(pathobj); 160135446Strhodes dest.file.versions = versions; 161135446Strhodes dest.file.maximum_size = size; 162135446Strhodes } else if (syslogobj != NULL) { 163135446Strhodes int facility = LOG_DAEMON; 164135446Strhodes 165135446Strhodes type = ISC_LOG_TOSYSLOG; 166135446Strhodes 167135446Strhodes if (cfg_obj_isstring(syslogobj)) { 168165071Sdougb const char *facilitystr = cfg_obj_asstring(syslogobj); 169135446Strhodes (void)isc_syslog_facilityfromstring(facilitystr, 170135446Strhodes &facility); 171135446Strhodes } 172135446Strhodes dest.facility = facility; 173135446Strhodes } else if (stderrobj != NULL) { 174135446Strhodes type = ISC_LOG_TOFILEDESC; 175135446Strhodes dest.file.stream = stderr; 176135446Strhodes dest.file.name = NULL; 177135446Strhodes dest.file.versions = ISC_LOG_ROLLNEVER; 178135446Strhodes dest.file.maximum_size = 0; 179135446Strhodes } 180135446Strhodes 181135446Strhodes /* 182135446Strhodes * Munge flags. 183135446Strhodes */ 184135446Strhodes { 185165071Sdougb const cfg_obj_t *printcat = NULL; 186165071Sdougb const cfg_obj_t *printsev = NULL; 187165071Sdougb const cfg_obj_t *printtime = NULL; 188135446Strhodes 189135446Strhodes (void)cfg_map_get(channel, "print-category", &printcat); 190135446Strhodes (void)cfg_map_get(channel, "print-severity", &printsev); 191135446Strhodes (void)cfg_map_get(channel, "print-time", &printtime); 192135446Strhodes 193135446Strhodes if (printcat != NULL && cfg_obj_asboolean(printcat)) 194135446Strhodes flags |= ISC_LOG_PRINTCATEGORY; 195135446Strhodes if (printtime != NULL && cfg_obj_asboolean(printtime)) 196135446Strhodes flags |= ISC_LOG_PRINTTIME; 197135446Strhodes if (printsev != NULL && cfg_obj_asboolean(printsev)) 198135446Strhodes flags |= ISC_LOG_PRINTLEVEL; 199135446Strhodes } 200135446Strhodes 201135446Strhodes level = ISC_LOG_INFO; 202135446Strhodes if (cfg_map_get(channel, "severity", &severity) == ISC_R_SUCCESS) { 203135446Strhodes if (cfg_obj_isstring(severity)) { 204165071Sdougb const char *str = cfg_obj_asstring(severity); 205135446Strhodes if (strcasecmp(str, "critical") == 0) 206135446Strhodes level = ISC_LOG_CRITICAL; 207135446Strhodes else if (strcasecmp(str, "error") == 0) 208135446Strhodes level = ISC_LOG_ERROR; 209135446Strhodes else if (strcasecmp(str, "warning") == 0) 210135446Strhodes level = ISC_LOG_WARNING; 211135446Strhodes else if (strcasecmp(str, "notice") == 0) 212135446Strhodes level = ISC_LOG_NOTICE; 213135446Strhodes else if (strcasecmp(str, "info") == 0) 214135446Strhodes level = ISC_LOG_INFO; 215135446Strhodes else if (strcasecmp(str, "dynamic") == 0) 216135446Strhodes level = ISC_LOG_DYNAMIC; 217135446Strhodes } else 218135446Strhodes /* debug */ 219135446Strhodes level = cfg_obj_asuint32(severity); 220135446Strhodes } 221135446Strhodes 222262706Serwin if (logconfig == NULL) 223262706Serwin result = ISC_R_SUCCESS; 224262706Serwin else 225262706Serwin result = isc_log_createchannel(logconfig, channelname, 226262706Serwin type, level, &dest, flags); 227135446Strhodes 228135446Strhodes if (result == ISC_R_SUCCESS && type == ISC_LOG_TOFILE) { 229135446Strhodes FILE *fp; 230135446Strhodes 231135446Strhodes /* 232225361Sdougb * Test to make sure that file is a plain file. 233225361Sdougb * Fix defect #22771 234225361Sdougb */ 235225361Sdougb result = isc_file_isplainfile(dest.file.name); 236262706Serwin if (result == ISC_R_SUCCESS || result == ISC_R_FILENOTFOUND) { 237225361Sdougb /* 238225361Sdougb * Test that the file can be opened, since 239225361Sdougb * isc_log_open() can't effectively report 240262706Serwin * failures when called in isc_log_doit(). 241225361Sdougb */ 242225361Sdougb result = isc_stdio_open(dest.file.name, "a", &fp); 243225361Sdougb if (result != ISC_R_SUCCESS) { 244262706Serwin if (logconfig != NULL && !ns_g_nosyslog) 245262706Serwin syslog(LOG_ERR, 246262706Serwin "isc_stdio_open '%s' failed: " 247262706Serwin "%s", dest.file.name, 248262706Serwin isc_result_totext(result)); 249225361Sdougb fprintf(stderr, 250262706Serwin "isc_stdio_open '%s' failed: %s\n", 251225361Sdougb dest.file.name, 252225361Sdougb isc_result_totext(result)); 253225361Sdougb } else 254225361Sdougb (void)isc_stdio_close(fp); 255254402Serwin goto done; 256254402Serwin } 257262706Serwin if (logconfig != NULL && !ns_g_nosyslog) 258225361Sdougb syslog(LOG_ERR, "isc_file_isplainfile '%s' failed: %s", 259254402Serwin dest.file.name, isc_result_totext(result)); 260262706Serwin fprintf(stderr, "isc_file_isplainfile '%s' failed: %s\n", 261254402Serwin dest.file.name, isc_result_totext(result)); 262135446Strhodes } 263135446Strhodes 264254402Serwin done: 265135446Strhodes return (result); 266135446Strhodes} 267135446Strhodes 268135446Strhodesisc_result_t 269262706Serwinns_log_configure(isc_logconfig_t *logconfig, const cfg_obj_t *logstmt) { 270135446Strhodes isc_result_t result; 271165071Sdougb const cfg_obj_t *channels = NULL; 272165071Sdougb const cfg_obj_t *categories = NULL; 273165071Sdougb const cfg_listelt_t *element; 274135446Strhodes isc_boolean_t default_set = ISC_FALSE; 275135446Strhodes isc_boolean_t unmatched_set = ISC_FALSE; 276165071Sdougb const cfg_obj_t *catname; 277135446Strhodes 278262706Serwin if (logconfig != NULL) 279262706Serwin CHECK(ns_log_setdefaultchannels(logconfig)); 280135446Strhodes 281135446Strhodes (void)cfg_map_get(logstmt, "channel", &channels); 282135446Strhodes for (element = cfg_list_first(channels); 283135446Strhodes element != NULL; 284135446Strhodes element = cfg_list_next(element)) 285135446Strhodes { 286165071Sdougb const cfg_obj_t *channel = cfg_listelt_value(element); 287262706Serwin CHECK(channel_fromconf(channel, logconfig)); 288135446Strhodes } 289135446Strhodes 290135446Strhodes (void)cfg_map_get(logstmt, "category", &categories); 291135446Strhodes for (element = cfg_list_first(categories); 292135446Strhodes element != NULL; 293135446Strhodes element = cfg_list_next(element)) 294135446Strhodes { 295165071Sdougb const cfg_obj_t *category = cfg_listelt_value(element); 296262706Serwin CHECK(category_fromconf(category, logconfig)); 297135446Strhodes if (!default_set) { 298165071Sdougb catname = cfg_tuple_get(category, "name"); 299135446Strhodes if (strcmp(cfg_obj_asstring(catname), "default") == 0) 300135446Strhodes default_set = ISC_TRUE; 301135446Strhodes } 302135446Strhodes if (!unmatched_set) { 303165071Sdougb catname = cfg_tuple_get(category, "name"); 304135446Strhodes if (strcmp(cfg_obj_asstring(catname), "unmatched") == 0) 305135446Strhodes unmatched_set = ISC_TRUE; 306135446Strhodes } 307135446Strhodes } 308135446Strhodes 309262706Serwin if (logconfig != NULL && !default_set) 310262706Serwin CHECK(ns_log_setdefaultcategory(logconfig)); 311135446Strhodes 312262706Serwin if (logconfig != NULL && !unmatched_set) 313262706Serwin CHECK(ns_log_setunmatchedcategory(logconfig)); 314135446Strhodes 315135446Strhodes return (ISC_R_SUCCESS); 316135446Strhodes 317135446Strhodes cleanup: 318135446Strhodes return (result); 319135446Strhodes} 320