]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - contrib/bind9/bin/named/logconf.c
Copy stable/9 to releng/9.3 as part of the 9.3-RELEASE cycle.
[FreeBSD/releng/9.3.git] / contrib / bind9 / bin / named / logconf.c
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  */
46 static isc_result_t
47 category_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  */
97 static isc_result_t
98 channel_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
268 isc_result_t
269 ns_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 }