iostat - add read/write details to output
[dragonfly.git] / contrib / bind / bin / named / logconf.c
1 /*
2  * Copyright (C) 2004-2007  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 2007/06/19 23:46:59 tbox Exp $ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <isc/offset.h>
25 #include <isc/result.h>
26 #include <isc/stdio.h>
27 #include <isc/string.h>
28 #include <isc/syslog.h>
29
30 #include <isccfg/cfg.h>
31 #include <isccfg/log.h>
32
33 #include <named/log.h>
34 #include <named/logconf.h>
35
36 #define CHECK(op) \
37         do { result = (op);                                      \
38                if (result != ISC_R_SUCCESS) goto cleanup;        \
39         } while (0)
40
41 /*%
42  * Set up a logging category according to the named.conf data
43  * in 'ccat' and add it to 'lctx'.
44  */
45 static isc_result_t
46 category_fromconf(const cfg_obj_t *ccat, isc_logconfig_t *lctx) {
47         isc_result_t result;
48         const char *catname;
49         isc_logcategory_t *category;
50         isc_logmodule_t *module;
51         const cfg_obj_t *destinations = NULL;
52         const cfg_listelt_t *element = NULL;
53
54         catname = cfg_obj_asstring(cfg_tuple_get(ccat, "name"));
55         category = isc_log_categorybyname(ns_g_lctx, catname);
56         if (category == NULL) {
57                 cfg_obj_log(ccat, ns_g_lctx, ISC_LOG_ERROR,
58                             "unknown logging category '%s' ignored",
59                             catname);
60                 /*
61                  * Allow further processing by returning success.
62                  */
63                 return (ISC_R_SUCCESS);
64         }
65
66         module = NULL;
67
68         destinations = cfg_tuple_get(ccat, "destinations");
69         for (element = cfg_list_first(destinations);
70              element != NULL;
71              element = cfg_list_next(element))
72         {
73                 const cfg_obj_t *channel = cfg_listelt_value(element);
74                 const char *channelname = cfg_obj_asstring(channel);
75
76                 result = isc_log_usechannel(lctx, channelname, category,
77                                             module);
78                 if (result != ISC_R_SUCCESS) {
79                         isc_log_write(ns_g_lctx, CFG_LOGCATEGORY_CONFIG,
80                                       NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
81                                       "logging channel '%s': %s", channelname,
82                                       isc_result_totext(result));
83                         return (result);
84                 }
85         }
86         return (ISC_R_SUCCESS);
87 }
88
89 /*%
90  * Set up a logging channel according to the named.conf data
91  * in 'cchan' and add it to 'lctx'.
92  */
93 static isc_result_t
94 channel_fromconf(const cfg_obj_t *channel, isc_logconfig_t *lctx) {
95         isc_result_t result;
96         isc_logdestination_t dest;
97         unsigned int type;
98         unsigned int flags = 0;
99         int level;
100         const char *channelname;
101         const cfg_obj_t *fileobj = NULL;
102         const cfg_obj_t *syslogobj = NULL;
103         const cfg_obj_t *nullobj = NULL;
104         const cfg_obj_t *stderrobj = NULL;
105         const cfg_obj_t *severity = NULL;
106         int i;
107
108         channelname = cfg_obj_asstring(cfg_map_getname(channel));
109
110         (void)cfg_map_get(channel, "file", &fileobj);
111         (void)cfg_map_get(channel, "syslog", &syslogobj);
112         (void)cfg_map_get(channel, "null", &nullobj);
113         (void)cfg_map_get(channel, "stderr", &stderrobj);
114
115         i = 0;
116         if (fileobj != NULL)
117                 i++;
118         if (syslogobj != NULL)
119                 i++;
120         if (nullobj != NULL)
121                 i++;
122         if (stderrobj != NULL)
123                 i++;
124
125         if (i != 1) {
126                 cfg_obj_log(channel, ns_g_lctx, ISC_LOG_ERROR,
127                               "channel '%s': exactly one of file, syslog, "
128                               "null, and stderr must be present", channelname);
129                 return (ISC_R_FAILURE);
130         }
131
132         type = ISC_LOG_TONULL;
133         
134         if (fileobj != NULL) {
135                 const cfg_obj_t *pathobj = cfg_tuple_get(fileobj, "file");
136                 const cfg_obj_t *sizeobj = cfg_tuple_get(fileobj, "size");
137                 const cfg_obj_t *versionsobj =
138                                  cfg_tuple_get(fileobj, "versions");
139                 isc_int32_t versions = ISC_LOG_ROLLNEVER;
140                 isc_offset_t size = 0;
141
142                 type = ISC_LOG_TOFILE;
143                 
144                 if (versionsobj != NULL && cfg_obj_isuint32(versionsobj))
145                         versions = cfg_obj_asuint32(versionsobj);
146                 if (versionsobj != NULL && cfg_obj_isstring(versionsobj) &&
147                     strcasecmp(cfg_obj_asstring(versionsobj), "unlimited") == 0)
148                         versions = ISC_LOG_ROLLINFINITE;
149                 if (sizeobj != NULL &&
150                     cfg_obj_isuint64(sizeobj) &&
151                     cfg_obj_asuint64(sizeobj) < ISC_OFFSET_MAXIMUM)
152                         size = (isc_offset_t)cfg_obj_asuint64(sizeobj);
153                 dest.file.stream = NULL;
154                 dest.file.name = cfg_obj_asstring(pathobj);
155                 dest.file.versions = versions;
156                 dest.file.maximum_size = size;
157         } else if (syslogobj != NULL) {
158                 int facility = LOG_DAEMON;
159
160                 type = ISC_LOG_TOSYSLOG;
161
162                 if (cfg_obj_isstring(syslogobj)) {
163                         const char *facilitystr = cfg_obj_asstring(syslogobj);
164                         (void)isc_syslog_facilityfromstring(facilitystr,
165                                                             &facility);
166                 }
167                 dest.facility = facility;
168         } else if (stderrobj != NULL) {
169                 type = ISC_LOG_TOFILEDESC;
170                 dest.file.stream = stderr;
171                 dest.file.name = NULL;
172                 dest.file.versions = ISC_LOG_ROLLNEVER;
173                 dest.file.maximum_size = 0;
174         }
175
176         /*
177          * Munge flags.
178          */
179         {
180                 const cfg_obj_t *printcat = NULL;
181                 const cfg_obj_t *printsev = NULL;
182                 const cfg_obj_t *printtime = NULL;
183
184                 (void)cfg_map_get(channel, "print-category", &printcat);
185                 (void)cfg_map_get(channel, "print-severity", &printsev);
186                 (void)cfg_map_get(channel, "print-time", &printtime);
187
188                 if (printcat != NULL && cfg_obj_asboolean(printcat))
189                         flags |= ISC_LOG_PRINTCATEGORY;
190                 if (printtime != NULL && cfg_obj_asboolean(printtime))
191                         flags |= ISC_LOG_PRINTTIME;
192                 if (printsev != NULL && cfg_obj_asboolean(printsev))
193                         flags |= ISC_LOG_PRINTLEVEL;
194         }
195
196         level = ISC_LOG_INFO;
197         if (cfg_map_get(channel, "severity", &severity) == ISC_R_SUCCESS) {
198                 if (cfg_obj_isstring(severity)) {
199                         const char *str = cfg_obj_asstring(severity);
200                         if (strcasecmp(str, "critical") == 0)
201                                 level = ISC_LOG_CRITICAL;
202                         else if (strcasecmp(str, "error") == 0)
203                                 level = ISC_LOG_ERROR;
204                         else if (strcasecmp(str, "warning") == 0)
205                                 level = ISC_LOG_WARNING;
206                         else if (strcasecmp(str, "notice") == 0)
207                                 level = ISC_LOG_NOTICE;
208                         else if (strcasecmp(str, "info") == 0)
209                                 level = ISC_LOG_INFO;
210                         else if (strcasecmp(str, "dynamic") == 0)
211                                 level = ISC_LOG_DYNAMIC;
212                 } else
213                         /* debug */
214                         level = cfg_obj_asuint32(severity);
215         }
216
217         result = isc_log_createchannel(lctx, channelname,
218                                        type, level, &dest, flags);
219
220         if (result == ISC_R_SUCCESS && type == ISC_LOG_TOFILE) {
221                 FILE *fp;
222                 
223                 /*
224                  * Test that the file can be opened, since isc_log_open()
225                  * can't effectively report failures when called in
226                  * isc_log_doit().
227                  */
228                 result = isc_stdio_open(dest.file.name, "a", &fp);
229                 if (result != ISC_R_SUCCESS)
230                         isc_log_write(ns_g_lctx, CFG_LOGCATEGORY_CONFIG,
231                                       NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
232                                       "logging channel '%s' file '%s': %s",
233                                       channelname, dest.file.name,
234                                       isc_result_totext(result));
235                 else
236                         (void)isc_stdio_close(fp);
237
238                 /*
239                  * Allow named to continue by returning success.
240                  */
241                 result = ISC_R_SUCCESS;
242         }
243
244         return (result);
245 }
246
247 isc_result_t
248 ns_log_configure(isc_logconfig_t *logconf, const cfg_obj_t *logstmt) {
249         isc_result_t result;
250         const cfg_obj_t *channels = NULL;
251         const cfg_obj_t *categories = NULL;
252         const cfg_listelt_t *element;
253         isc_boolean_t default_set = ISC_FALSE;
254         isc_boolean_t unmatched_set = ISC_FALSE;
255         const cfg_obj_t *catname;
256
257         CHECK(ns_log_setdefaultchannels(logconf));
258
259         (void)cfg_map_get(logstmt, "channel", &channels);
260         for (element = cfg_list_first(channels);
261              element != NULL;
262              element = cfg_list_next(element))
263         {
264                 const cfg_obj_t *channel = cfg_listelt_value(element);
265                 CHECK(channel_fromconf(channel, logconf));
266         }
267
268         (void)cfg_map_get(logstmt, "category", &categories);
269         for (element = cfg_list_first(categories);
270              element != NULL;
271              element = cfg_list_next(element))
272         {
273                 const cfg_obj_t *category = cfg_listelt_value(element);
274                 CHECK(category_fromconf(category, logconf));
275                 if (!default_set) {
276                         catname = cfg_tuple_get(category, "name");
277                         if (strcmp(cfg_obj_asstring(catname), "default") == 0)
278                                 default_set = ISC_TRUE;
279                 }
280                 if (!unmatched_set) {
281                         catname = cfg_tuple_get(category, "name");
282                         if (strcmp(cfg_obj_asstring(catname), "unmatched") == 0)
283                                 unmatched_set = ISC_TRUE;
284                 }
285         }
286
287         if (!default_set)
288                 CHECK(ns_log_setdefaultcategory(logconf));
289
290         if (!unmatched_set)
291                 CHECK(ns_log_setunmatchedcategory(logconf));
292
293         return (ISC_R_SUCCESS);
294
295  cleanup:
296         if (logconf != NULL)
297                 isc_logconfig_destroy(&logconf);
298         return (result);
299 }