]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/bind9/bin/named/logconf.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / bind9 / bin / named / logconf.c
1 /*
2  * Copyright (C) 2004-2007, 2011  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.42.816.3 2011/03/05 23:52:06 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 'lctx'.
45  */
46 static isc_result_t
47 category_fromconf(const cfg_obj_t *ccat, isc_logconfig_t *lctx) {
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         module = NULL;
68
69         destinations = cfg_tuple_get(ccat, "destinations");
70         for (element = cfg_list_first(destinations);
71              element != NULL;
72              element = cfg_list_next(element))
73         {
74                 const cfg_obj_t *channel = cfg_listelt_value(element);
75                 const char *channelname = cfg_obj_asstring(channel);
76
77                 result = isc_log_usechannel(lctx, channelname, category,
78                                             module);
79                 if (result != ISC_R_SUCCESS) {
80                         isc_log_write(ns_g_lctx, CFG_LOGCATEGORY_CONFIG,
81                                       NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
82                                       "logging channel '%s': %s", channelname,
83                                       isc_result_totext(result));
84                         return (result);
85                 }
86         }
87         return (ISC_R_SUCCESS);
88 }
89
90 /*%
91  * Set up a logging channel according to the named.conf data
92  * in 'cchan' and add it to 'lctx'.
93  */
94 static isc_result_t
95 channel_fromconf(const cfg_obj_t *channel, isc_logconfig_t *lctx) {
96         isc_result_t result;
97         isc_logdestination_t dest;
98         unsigned int type;
99         unsigned int flags = 0;
100         int level;
101         const char *channelname;
102         const cfg_obj_t *fileobj = NULL;
103         const cfg_obj_t *syslogobj = NULL;
104         const cfg_obj_t *nullobj = NULL;
105         const cfg_obj_t *stderrobj = NULL;
106         const cfg_obj_t *severity = NULL;
107         int i;
108
109         channelname = cfg_obj_asstring(cfg_map_getname(channel));
110
111         (void)cfg_map_get(channel, "file", &fileobj);
112         (void)cfg_map_get(channel, "syslog", &syslogobj);
113         (void)cfg_map_get(channel, "null", &nullobj);
114         (void)cfg_map_get(channel, "stderr", &stderrobj);
115
116         i = 0;
117         if (fileobj != NULL)
118                 i++;
119         if (syslogobj != NULL)
120                 i++;
121         if (nullobj != NULL)
122                 i++;
123         if (stderrobj != NULL)
124                 i++;
125
126         if (i != 1) {
127                 cfg_obj_log(channel, ns_g_lctx, ISC_LOG_ERROR,
128                               "channel '%s': exactly one of file, syslog, "
129                               "null, and stderr must be present", channelname);
130                 return (ISC_R_FAILURE);
131         }
132
133         type = ISC_LOG_TONULL;
134
135         if (fileobj != NULL) {
136                 const cfg_obj_t *pathobj = cfg_tuple_get(fileobj, "file");
137                 const cfg_obj_t *sizeobj = cfg_tuple_get(fileobj, "size");
138                 const cfg_obj_t *versionsobj =
139                                  cfg_tuple_get(fileobj, "versions");
140                 isc_int32_t versions = ISC_LOG_ROLLNEVER;
141                 isc_offset_t size = 0;
142
143                 type = ISC_LOG_TOFILE;
144
145                 if (versionsobj != NULL && cfg_obj_isuint32(versionsobj))
146                         versions = cfg_obj_asuint32(versionsobj);
147                 if (versionsobj != NULL && cfg_obj_isstring(versionsobj) &&
148                     strcasecmp(cfg_obj_asstring(versionsobj), "unlimited") == 0)
149                         versions = ISC_LOG_ROLLINFINITE;
150                 if (sizeobj != NULL &&
151                     cfg_obj_isuint64(sizeobj) &&
152                     cfg_obj_asuint64(sizeobj) < ISC_OFFSET_MAXIMUM)
153                         size = (isc_offset_t)cfg_obj_asuint64(sizeobj);
154                 dest.file.stream = NULL;
155                 dest.file.name = cfg_obj_asstring(pathobj);
156                 dest.file.versions = versions;
157                 dest.file.maximum_size = size;
158         } else if (syslogobj != NULL) {
159                 int facility = LOG_DAEMON;
160
161                 type = ISC_LOG_TOSYSLOG;
162
163                 if (cfg_obj_isstring(syslogobj)) {
164                         const char *facilitystr = cfg_obj_asstring(syslogobj);
165                         (void)isc_syslog_facilityfromstring(facilitystr,
166                                                             &facility);
167                 }
168                 dest.facility = facility;
169         } else if (stderrobj != NULL) {
170                 type = ISC_LOG_TOFILEDESC;
171                 dest.file.stream = stderr;
172                 dest.file.name = NULL;
173                 dest.file.versions = ISC_LOG_ROLLNEVER;
174                 dest.file.maximum_size = 0;
175         }
176
177         /*
178          * Munge flags.
179          */
180         {
181                 const cfg_obj_t *printcat = NULL;
182                 const cfg_obj_t *printsev = NULL;
183                 const cfg_obj_t *printtime = NULL;
184
185                 (void)cfg_map_get(channel, "print-category", &printcat);
186                 (void)cfg_map_get(channel, "print-severity", &printsev);
187                 (void)cfg_map_get(channel, "print-time", &printtime);
188
189                 if (printcat != NULL && cfg_obj_asboolean(printcat))
190                         flags |= ISC_LOG_PRINTCATEGORY;
191                 if (printtime != NULL && cfg_obj_asboolean(printtime))
192                         flags |= ISC_LOG_PRINTTIME;
193                 if (printsev != NULL && cfg_obj_asboolean(printsev))
194                         flags |= ISC_LOG_PRINTLEVEL;
195         }
196
197         level = ISC_LOG_INFO;
198         if (cfg_map_get(channel, "severity", &severity) == ISC_R_SUCCESS) {
199                 if (cfg_obj_isstring(severity)) {
200                         const char *str = cfg_obj_asstring(severity);
201                         if (strcasecmp(str, "critical") == 0)
202                                 level = ISC_LOG_CRITICAL;
203                         else if (strcasecmp(str, "error") == 0)
204                                 level = ISC_LOG_ERROR;
205                         else if (strcasecmp(str, "warning") == 0)
206                                 level = ISC_LOG_WARNING;
207                         else if (strcasecmp(str, "notice") == 0)
208                                 level = ISC_LOG_NOTICE;
209                         else if (strcasecmp(str, "info") == 0)
210                                 level = ISC_LOG_INFO;
211                         else if (strcasecmp(str, "dynamic") == 0)
212                                 level = ISC_LOG_DYNAMIC;
213                 } else
214                         /* debug */
215                         level = cfg_obj_asuint32(severity);
216         }
217
218         result = isc_log_createchannel(lctx, channelname,
219                                        type, level, &dest, flags);
220
221         if (result == ISC_R_SUCCESS && type == ISC_LOG_TOFILE) {
222                 FILE *fp;
223
224                 /*
225                  * Test to make sure that file is a plain file.
226                  * Fix defect #22771
227                 */
228                 result = isc_file_isplainfile(dest.file.name);
229                 if (result == ISC_R_SUCCESS ||
230                     result == ISC_R_FILENOTFOUND) {
231                         /*
232                          * Test that the file can be opened, since
233                          * isc_log_open() can't effectively report
234                          * failures when called in
235                          * isc_log_doit().
236                          */
237                         result = isc_stdio_open(dest.file.name, "a", &fp);
238                         if (result != ISC_R_SUCCESS) {
239                                 syslog(LOG_ERR,
240                                         "isc_stdio_open '%s' failed: %s",
241                                         dest.file.name,
242                                         isc_result_totext(result));
243                                 fprintf(stderr,
244                                         "isc_stdio_open '%s' failed: %s",
245                                         dest.file.name,
246                                         isc_result_totext(result));
247                         } else
248                                 (void)isc_stdio_close(fp);
249                 } else {
250                         syslog(LOG_ERR, "isc_file_isplainfile '%s' failed: %s",
251                                 dest.file.name, isc_result_totext(result));
252                         fprintf(stderr, "isc_file_isplainfile '%s' failed: %s",
253                                 dest.file.name, isc_result_totext(result));
254                 }
255         }
256
257         return (result);
258 }
259
260 isc_result_t
261 ns_log_configure(isc_logconfig_t *logconf, const cfg_obj_t *logstmt) {
262         isc_result_t result;
263         const cfg_obj_t *channels = NULL;
264         const cfg_obj_t *categories = NULL;
265         const cfg_listelt_t *element;
266         isc_boolean_t default_set = ISC_FALSE;
267         isc_boolean_t unmatched_set = ISC_FALSE;
268         const cfg_obj_t *catname;
269
270         CHECK(ns_log_setdefaultchannels(logconf));
271
272         (void)cfg_map_get(logstmt, "channel", &channels);
273         for (element = cfg_list_first(channels);
274              element != NULL;
275              element = cfg_list_next(element))
276         {
277                 const cfg_obj_t *channel = cfg_listelt_value(element);
278                 CHECK(channel_fromconf(channel, logconf));
279         }
280
281         (void)cfg_map_get(logstmt, "category", &categories);
282         for (element = cfg_list_first(categories);
283              element != NULL;
284              element = cfg_list_next(element))
285         {
286                 const cfg_obj_t *category = cfg_listelt_value(element);
287                 CHECK(category_fromconf(category, logconf));
288                 if (!default_set) {
289                         catname = cfg_tuple_get(category, "name");
290                         if (strcmp(cfg_obj_asstring(catname), "default") == 0)
291                                 default_set = ISC_TRUE;
292                 }
293                 if (!unmatched_set) {
294                         catname = cfg_tuple_get(category, "name");
295                         if (strcmp(cfg_obj_asstring(catname), "unmatched") == 0)
296                                 unmatched_set = ISC_TRUE;
297                 }
298         }
299
300         if (!default_set)
301                 CHECK(ns_log_setdefaultcategory(logconf));
302
303         if (!unmatched_set)
304                 CHECK(ns_log_setunmatchedcategory(logconf));
305
306         return (ISC_R_SUCCESS);
307
308  cleanup:
309         if (logconf != NULL)
310                 isc_logconfig_destroy(&logconf);
311         return (result);
312 }