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