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