1/* 2 * Copyright (C) 2004-2007, 2011, 2013 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 1999-2001 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* $Id: logconf.c,v 1.45 2011/03/05 23:52:29 tbox Exp $ */ 19 20/*! \file */ 21 22#include <config.h> 23 24#include <isc/file.h> 25#include <isc/offset.h> 26#include <isc/result.h> 27#include <isc/stdio.h> 28#include <isc/string.h> 29#include <isc/syslog.h> 30 31#include <isccfg/cfg.h> 32#include <isccfg/log.h> 33 34#include <named/log.h> 35#include <named/logconf.h> 36 37#define CHECK(op) \ 38 do { result = (op); \ 39 if (result != ISC_R_SUCCESS) goto cleanup; \ 40 } while (0) 41 42/*% 43 * Set up a logging category according to the named.conf data 44 * in 'ccat' and add it to 'logconfig'. 45 */ 46static isc_result_t 47category_fromconf(const cfg_obj_t *ccat, isc_logconfig_t *logconfig) { 48 isc_result_t result; 49 const char *catname; 50 isc_logcategory_t *category; 51 isc_logmodule_t *module; 52 const cfg_obj_t *destinations = NULL; 53 const cfg_listelt_t *element = NULL; 54 55 catname = cfg_obj_asstring(cfg_tuple_get(ccat, "name")); 56 category = isc_log_categorybyname(ns_g_lctx, catname); 57 if (category == NULL) { 58 cfg_obj_log(ccat, ns_g_lctx, ISC_LOG_ERROR, 59 "unknown logging category '%s' ignored", 60 catname); 61 /* 62 * Allow further processing by returning success. 63 */ 64 return (ISC_R_SUCCESS); 65 } 66 67 if (logconfig == NULL) 68 return (ISC_R_SUCCESS); 69 70 module = NULL; 71 72 destinations = cfg_tuple_get(ccat, "destinations"); 73 for (element = cfg_list_first(destinations); 74 element != NULL; 75 element = cfg_list_next(element)) 76 { 77 const cfg_obj_t *channel = cfg_listelt_value(element); 78 const char *channelname = cfg_obj_asstring(channel); 79 80 result = isc_log_usechannel(logconfig, channelname, category, 81 module); 82 if (result != ISC_R_SUCCESS) { 83 isc_log_write(ns_g_lctx, CFG_LOGCATEGORY_CONFIG, 84 NS_LOGMODULE_SERVER, ISC_LOG_ERROR, 85 "logging channel '%s': %s", channelname, 86 isc_result_totext(result)); 87 return (result); 88 } 89 } 90 return (ISC_R_SUCCESS); 91} 92 93/*% 94 * Set up a logging channel according to the named.conf data 95 * in 'cchan' and add it to 'logconfig'. 96 */ 97static isc_result_t 98channel_fromconf(const cfg_obj_t *channel, isc_logconfig_t *logconfig) 99{ 100 isc_result_t result; 101 isc_logdestination_t dest; 102 unsigned int type; 103 unsigned int flags = 0; 104 int level; 105 const char *channelname; 106 const cfg_obj_t *fileobj = NULL; 107 const cfg_obj_t *syslogobj = NULL; 108 const cfg_obj_t *nullobj = NULL; 109 const cfg_obj_t *stderrobj = NULL; 110 const cfg_obj_t *severity = NULL; 111 int i; 112 113 channelname = cfg_obj_asstring(cfg_map_getname(channel)); 114 115 (void)cfg_map_get(channel, "file", &fileobj); 116 (void)cfg_map_get(channel, "syslog", &syslogobj); 117 (void)cfg_map_get(channel, "null", &nullobj); 118 (void)cfg_map_get(channel, "stderr", &stderrobj); 119 120 i = 0; 121 if (fileobj != NULL) 122 i++; 123 if (syslogobj != NULL) 124 i++; 125 if (nullobj != NULL) 126 i++; 127 if (stderrobj != NULL) 128 i++; 129 130 if (i != 1) { 131 cfg_obj_log(channel, ns_g_lctx, ISC_LOG_ERROR, 132 "channel '%s': exactly one of file, syslog, " 133 "null, and stderr must be present", channelname); 134 return (ISC_R_FAILURE); 135 } 136 137 type = ISC_LOG_TONULL; 138 139 if (fileobj != NULL) { 140 const cfg_obj_t *pathobj = cfg_tuple_get(fileobj, "file"); 141 const cfg_obj_t *sizeobj = cfg_tuple_get(fileobj, "size"); 142 const cfg_obj_t *versionsobj = 143 cfg_tuple_get(fileobj, "versions"); 144 isc_int32_t versions = ISC_LOG_ROLLNEVER; 145 isc_offset_t size = 0; 146 147 type = ISC_LOG_TOFILE; 148 149 if (versionsobj != NULL && cfg_obj_isuint32(versionsobj)) 150 versions = cfg_obj_asuint32(versionsobj); 151 if (versionsobj != NULL && cfg_obj_isstring(versionsobj) && 152 strcasecmp(cfg_obj_asstring(versionsobj), "unlimited") == 0) 153 versions = ISC_LOG_ROLLINFINITE; 154 if (sizeobj != NULL && 155 cfg_obj_isuint64(sizeobj) && 156 cfg_obj_asuint64(sizeobj) < ISC_OFFSET_MAXIMUM) 157 size = (isc_offset_t)cfg_obj_asuint64(sizeobj); 158 dest.file.stream = NULL; 159 dest.file.name = cfg_obj_asstring(pathobj); 160 dest.file.versions = versions; 161 dest.file.maximum_size = size; 162 } else if (syslogobj != NULL) { 163 int facility = LOG_DAEMON; 164 165 type = ISC_LOG_TOSYSLOG; 166 167 if (cfg_obj_isstring(syslogobj)) { 168 const char *facilitystr = cfg_obj_asstring(syslogobj); 169 (void)isc_syslog_facilityfromstring(facilitystr, 170 &facility); 171 } 172 dest.facility = facility; 173 } else if (stderrobj != NULL) { 174 type = ISC_LOG_TOFILEDESC; 175 dest.file.stream = stderr; 176 dest.file.name = NULL; 177 dest.file.versions = ISC_LOG_ROLLNEVER; 178 dest.file.maximum_size = 0; 179 } 180 181 /* 182 * Munge flags. 183 */ 184 { 185 const cfg_obj_t *printcat = NULL; 186 const cfg_obj_t *printsev = NULL; 187 const cfg_obj_t *printtime = NULL; 188 189 (void)cfg_map_get(channel, "print-category", &printcat); 190 (void)cfg_map_get(channel, "print-severity", &printsev); 191 (void)cfg_map_get(channel, "print-time", &printtime); 192 193 if (printcat != NULL && cfg_obj_asboolean(printcat)) 194 flags |= ISC_LOG_PRINTCATEGORY; 195 if (printtime != NULL && cfg_obj_asboolean(printtime)) 196 flags |= ISC_LOG_PRINTTIME; 197 if (printsev != NULL && cfg_obj_asboolean(printsev)) 198 flags |= ISC_LOG_PRINTLEVEL; 199 } 200 201 level = ISC_LOG_INFO; 202 if (cfg_map_get(channel, "severity", &severity) == ISC_R_SUCCESS) { 203 if (cfg_obj_isstring(severity)) { 204 const char *str = cfg_obj_asstring(severity); 205 if (strcasecmp(str, "critical") == 0) 206 level = ISC_LOG_CRITICAL; 207 else if (strcasecmp(str, "error") == 0) 208 level = ISC_LOG_ERROR; 209 else if (strcasecmp(str, "warning") == 0) 210 level = ISC_LOG_WARNING; 211 else if (strcasecmp(str, "notice") == 0) 212 level = ISC_LOG_NOTICE; 213 else if (strcasecmp(str, "info") == 0) 214 level = ISC_LOG_INFO; 215 else if (strcasecmp(str, "dynamic") == 0) 216 level = ISC_LOG_DYNAMIC; 217 } else 218 /* debug */ 219 level = cfg_obj_asuint32(severity); 220 } 221 222 if (logconfig == NULL) 223 result = ISC_R_SUCCESS; 224 else 225 result = isc_log_createchannel(logconfig, channelname, 226 type, level, &dest, flags); 227 228 if (result == ISC_R_SUCCESS && type == ISC_LOG_TOFILE) { 229 FILE *fp; 230 231 /* 232 * Test to make sure that file is a plain file. 233 * Fix defect #22771 234 */ 235 result = isc_file_isplainfile(dest.file.name); 236 if (result == ISC_R_SUCCESS || result == ISC_R_FILENOTFOUND) { 237 /* 238 * Test that the file can be opened, since 239 * isc_log_open() can't effectively report 240 * failures when called in isc_log_doit(). 241 */ 242 result = isc_stdio_open(dest.file.name, "a", &fp); 243 if (result != ISC_R_SUCCESS) { 244 if (logconfig != NULL && !ns_g_nosyslog) 245 syslog(LOG_ERR, 246 "isc_stdio_open '%s' failed: " 247 "%s", dest.file.name, 248 isc_result_totext(result)); 249 fprintf(stderr, 250 "isc_stdio_open '%s' failed: %s\n", 251 dest.file.name, 252 isc_result_totext(result)); 253 } else 254 (void)isc_stdio_close(fp); 255 goto done; 256 } 257 if (logconfig != NULL && !ns_g_nosyslog) 258 syslog(LOG_ERR, "isc_file_isplainfile '%s' failed: %s", 259 dest.file.name, isc_result_totext(result)); 260 fprintf(stderr, "isc_file_isplainfile '%s' failed: %s\n", 261 dest.file.name, isc_result_totext(result)); 262 } 263 264 done: 265 return (result); 266} 267 268isc_result_t 269ns_log_configure(isc_logconfig_t *logconfig, const cfg_obj_t *logstmt) { 270 isc_result_t result; 271 const cfg_obj_t *channels = NULL; 272 const cfg_obj_t *categories = NULL; 273 const cfg_listelt_t *element; 274 isc_boolean_t default_set = ISC_FALSE; 275 isc_boolean_t unmatched_set = ISC_FALSE; 276 const cfg_obj_t *catname; 277 278 if (logconfig != NULL) 279 CHECK(ns_log_setdefaultchannels(logconfig)); 280 281 (void)cfg_map_get(logstmt, "channel", &channels); 282 for (element = cfg_list_first(channels); 283 element != NULL; 284 element = cfg_list_next(element)) 285 { 286 const cfg_obj_t *channel = cfg_listelt_value(element); 287 CHECK(channel_fromconf(channel, logconfig)); 288 } 289 290 (void)cfg_map_get(logstmt, "category", &categories); 291 for (element = cfg_list_first(categories); 292 element != NULL; 293 element = cfg_list_next(element)) 294 { 295 const cfg_obj_t *category = cfg_listelt_value(element); 296 CHECK(category_fromconf(category, logconfig)); 297 if (!default_set) { 298 catname = cfg_tuple_get(category, "name"); 299 if (strcmp(cfg_obj_asstring(catname), "default") == 0) 300 default_set = ISC_TRUE; 301 } 302 if (!unmatched_set) { 303 catname = cfg_tuple_get(category, "name"); 304 if (strcmp(cfg_obj_asstring(catname), "unmatched") == 0) 305 unmatched_set = ISC_TRUE; 306 } 307 } 308 309 if (logconfig != NULL && !default_set) 310 CHECK(ns_log_setdefaultcategory(logconfig)); 311 312 if (logconfig != NULL && !unmatched_set) 313 CHECK(ns_log_setunmatchedcategory(logconfig)); 314 315 return (ISC_R_SUCCESS); 316 317 cleanup: 318 return (result); 319} 320