logconf.c 8.29 KB
Newer Older
1
/*
Mark Andrews's avatar
Mark Andrews committed
2
 * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
Brian Wellington's avatar
Brian Wellington committed
3
 * Copyright (C) 1999-2001  Internet Software Consortium.
4
 *
5 6 7
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
16 17
 */

Mark Andrews's avatar
Mark Andrews committed
18
/* $Id: logconf.c,v 1.37 2005/04/29 00:22:27 marka Exp $ */
19 20

/*! \file */
David Lawrence's avatar
David Lawrence committed
21

22 23
#include <config.h>

24
#include <isc/offset.h>
Brian Wellington's avatar
Brian Wellington committed
25
#include <isc/result.h>
26
#include <isc/stdio.h>
27
#include <isc/string.h>
28 29 30 31
#include <isc/syslog.h>

#include <isccfg/cfg.h>
#include <isccfg/log.h>
32

33 34 35 36 37 38 39 40
#include <named/log.h>
#include <named/logconf.h>

#define CHECK(op) \
	do { result = (op); 				  	 \
	       if (result != ISC_R_SUCCESS) goto cleanup; 	 \
	} while (0)

41
/*%
42 43 44 45
 * Set up a logging category according to the named.conf data
 * in 'ccat' and add it to 'lctx'.
 */
static isc_result_t
46
category_fromconf(cfg_obj_t *ccat, isc_logconfig_t *lctx) {
47
	isc_result_t result;
48
	const char *catname;
49 50
	isc_logcategory_t *category;
	isc_logmodule_t *module;
51 52
	cfg_obj_t *destinations = NULL;
	cfg_listelt_t *element = NULL;
53

54 55
	catname = cfg_obj_asstring(cfg_tuple_get(ccat, "name"));
	category = isc_log_categorybyname(ns_g_lctx, catname);
56
	if (category == NULL) {
57 58 59
		cfg_obj_log(ccat, ns_g_lctx, ISC_LOG_ERROR,
			    "unknown logging category '%s' ignored",
			    catname);
60 61 62 63 64 65
		/*
		 * Allow further processing by returning success.
		 */
		return (ISC_R_SUCCESS);
	}

66 67
	module = NULL;

68 69 70 71 72 73 74
	destinations = cfg_tuple_get(ccat, "destinations");
	for (element = cfg_list_first(destinations);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		cfg_obj_t *channel = cfg_listelt_value(element);
		char *channelname = cfg_obj_asstring(channel);
75

76 77
		result = isc_log_usechannel(lctx, channelname, category,
					    module);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
78
		if (result != ISC_R_SUCCESS) {
79
			isc_log_write(ns_g_lctx, CFG_LOGCATEGORY_CONFIG,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
80
				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
81
				      "logging channel '%s': %s", channelname,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
82
				      isc_result_totext(result));
83
			return (result);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
84
		}
85 86 87 88
	}
	return (ISC_R_SUCCESS);
}

89
/*%
90 91 92 93
 * Set up a logging channel according to the named.conf data
 * in 'cchan' and add it to 'lctx'.
 */
static isc_result_t
94
channel_fromconf(cfg_obj_t *channel, isc_logconfig_t *lctx) {
95 96 97
	isc_result_t result;
	isc_logdestination_t dest;
	unsigned int type;
David Lawrence's avatar
David Lawrence committed
98
	unsigned int flags = 0;
99
	int level;
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
	const char *channelname;
	cfg_obj_t *fileobj = NULL;
	cfg_obj_t *syslogobj = NULL;
	cfg_obj_t *nullobj = NULL;
	cfg_obj_t *stderrobj = NULL;
	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);
	}
131

132
	type = ISC_LOG_TONULL;
133 134 135 136 137 138
	
	if (fileobj != NULL) {
		cfg_obj_t *pathobj = cfg_tuple_get(fileobj, "file");
		cfg_obj_t *sizeobj = cfg_tuple_get(fileobj, "size");
		cfg_obj_t *versionsobj = cfg_tuple_get(fileobj, "versions");
		isc_int32_t versions = ISC_LOG_ROLLNEVER;
139
		isc_offset_t size = 0;
140

141
		type = ISC_LOG_TOFILE;
142 143 144
		
		if (versionsobj != NULL && cfg_obj_isuint32(versionsobj))
			versions = cfg_obj_asuint32(versionsobj);
145 146 147
		if (versionsobj != NULL && cfg_obj_isstring(versionsobj) &&
		    strcasecmp(cfg_obj_asstring(versionsobj), "unlimited") == 0)
			versions = ISC_LOG_ROLLINFINITE;
148 149 150 151 152 153 154 155 156 157
		if (sizeobj != NULL &&
		    cfg_obj_isuint64(sizeobj) &&
		    cfg_obj_asuint64(sizeobj) < ISC_OFFSET_MAXIMUM)
			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;
158 159 160

		type = ISC_LOG_TOSYSLOG;

161 162 163 164
		if (cfg_obj_isstring(syslogobj)) {
			char *facilitystr = cfg_obj_asstring(syslogobj);
			(void)isc_syslog_facilityfromstring(facilitystr,
							    &facility);
165
		}
166 167 168 169 170 171 172
		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;
173 174 175 176 177 178
	}

	/*
	 * Munge flags.
	 */
	{
179 180 181
		cfg_obj_t *printcat = NULL;
		cfg_obj_t *printsev = NULL;
		cfg_obj_t *printtime = NULL;
182

183 184 185
		(void)cfg_map_get(channel, "print-category", &printcat);
		(void)cfg_map_get(channel, "print-severity", &printsev);
		(void)cfg_map_get(channel, "print-time", &printtime);
186

187
		if (printcat != NULL && cfg_obj_asboolean(printcat))
188
			flags |= ISC_LOG_PRINTCATEGORY;
189
		if (printtime != NULL && cfg_obj_asboolean(printtime))
190
			flags |= ISC_LOG_PRINTTIME;
191
		if (printsev != NULL && cfg_obj_asboolean(printsev))
192 193
			flags |= ISC_LOG_PRINTLEVEL;
	}
194

195
	level = ISC_LOG_INFO;
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
	if (cfg_map_get(channel, "severity", &severity) == ISC_R_SUCCESS) {
		if (cfg_obj_isstring(severity)) {
			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);
214
	}
215

216
	result = isc_log_createchannel(lctx, channelname,
217
				       type, level, &dest, flags);
218 219 220 221 222 223 224 225 226 227 228

	if (result == ISC_R_SUCCESS && type == ISC_LOG_TOFILE) {
		FILE *fp;
		
		/*
		 * 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)
229
			isc_log_write(ns_g_lctx, CFG_LOGCATEGORY_CONFIG,
230 231 232 233 234 235 236 237 238 239 240 241 242
				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
				      "logging channel '%s' file '%s': %s",
				      channelname, dest.file.name,
				      isc_result_totext(result));
		else
			(void)isc_stdio_close(fp);

		/*
		 * Allow named to continue by returning success.
		 */
		result = ISC_R_SUCCESS;
	}

243 244 245 246
	return (result);
}

isc_result_t
247
ns_log_configure(isc_logconfig_t *logconf, cfg_obj_t *logstmt) {
248
	isc_result_t result;
249 250 251
	cfg_obj_t *channels = NULL;
	cfg_obj_t *categories = NULL;
	cfg_listelt_t *element;
252
	isc_boolean_t default_set = ISC_FALSE;
Mark Andrews's avatar
Redo:  
Mark Andrews committed
253
	isc_boolean_t unmatched_set = ISC_FALSE;
254

255
	CHECK(ns_log_setdefaultchannels(logconf));
256

257 258 259 260 261 262 263
	(void)cfg_map_get(logstmt, "channel", &channels);
	for (element = cfg_list_first(channels);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		cfg_obj_t *channel = cfg_listelt_value(element);
		CHECK(channel_fromconf(channel, logconf));
264 265
	}

266 267 268 269 270 271 272 273 274
	(void)cfg_map_get(logstmt, "category", &categories);
	for (element = cfg_list_first(categories);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		cfg_obj_t *category = cfg_listelt_value(element);
		CHECK(category_fromconf(category, logconf));
		if (!default_set) {
			cfg_obj_t *catname = cfg_tuple_get(category, "name");
275
			if (strcmp(cfg_obj_asstring(catname), "default") == 0)
276 277
				default_set = ISC_TRUE;
		}
Mark Andrews's avatar
Redo:  
Mark Andrews committed
278 279
		if (!unmatched_set) {
			cfg_obj_t *catname = cfg_tuple_get(category, "name");
280
			if (strcmp(cfg_obj_asstring(catname), "unmatched") == 0)
Mark Andrews's avatar
Redo:  
Mark Andrews committed
281 282
				unmatched_set = ISC_TRUE;
		}
283 284
	}

285 286
	if (!default_set)
		CHECK(ns_log_setdefaultcategory(logconf));
287

Mark Andrews's avatar
Redo:  
Mark Andrews committed
288 289 290
	if (!unmatched_set)
		CHECK(ns_log_setunmatchedcategory(logconf));

291 292 293
	return (ISC_R_SUCCESS);

 cleanup:
294 295
	if (logconf != NULL)
		isc_logconfig_destroy(&logconf);
296 297
	return (result);
}