/src/ntp-dev/lib/isc/log.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2004-2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") |
3 | | * Copyright (C) 1999-2003 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$ */ |
19 | | |
20 | | /*! \file |
21 | | * \author Principal Authors: DCL */ |
22 | | |
23 | | #include <config.h> |
24 | | |
25 | | #include <errno.h> |
26 | | #include <stdlib.h> |
27 | | #include <limits.h> |
28 | | #include <time.h> |
29 | | |
30 | | #include <sys/types.h> /* dev_t FreeBSD 2.1 */ |
31 | | |
32 | | #include <isc/dir.h> |
33 | | #include <isc/file.h> |
34 | | #include <isc/log.h> |
35 | | #include <isc/magic.h> |
36 | | #include <isc/mem.h> |
37 | | #include <isc/msgs.h> |
38 | | #include <isc/print.h> |
39 | | #include <isc/stat.h> |
40 | | #include <isc/stdio.h> |
41 | | #include <isc/string.h> |
42 | | #include <isc/time.h> |
43 | | #include <isc/util.h> |
44 | | #include "ntp_stdlib.h" /* NTP change for strlcpy, strlcat */ |
45 | | |
46 | 0 | #define LCTX_MAGIC ISC_MAGIC('L', 'c', 't', 'x') |
47 | | #define VALID_CONTEXT(lctx) ISC_MAGIC_VALID(lctx, LCTX_MAGIC) |
48 | | |
49 | 0 | #define LCFG_MAGIC ISC_MAGIC('L', 'c', 'f', 'g') |
50 | | #define VALID_CONFIG(lcfg) ISC_MAGIC_VALID(lcfg, LCFG_MAGIC) |
51 | | |
52 | | /* |
53 | | * XXXDCL make dynamic? |
54 | | */ |
55 | | #define LOG_BUFFER_SIZE (8 * 1024) |
56 | | |
57 | | #ifndef PATH_MAX |
58 | | #define PATH_MAX 1024 /* AIX and others don't define this. */ |
59 | | #endif |
60 | | |
61 | | /*! |
62 | | * This is the structure that holds each named channel. A simple linked |
63 | | * list chains all of the channels together, so an individual channel is |
64 | | * found by doing strcmp()s with the names down the list. Their should |
65 | | * be no performance penalty from this as it is expected that the number |
66 | | * of named channels will be no more than a dozen or so, and name lookups |
67 | | * from the head of the list are only done when isc_log_usechannel() is |
68 | | * called, which should also be very infrequent. |
69 | | */ |
70 | | typedef struct isc_logchannel isc_logchannel_t; |
71 | | |
72 | | struct isc_logchannel { |
73 | | char * name; |
74 | | unsigned int type; |
75 | | int level; |
76 | | unsigned int flags; |
77 | | isc_logdestination_t destination; |
78 | | ISC_LINK(isc_logchannel_t) link; |
79 | | }; |
80 | | |
81 | | /*! |
82 | | * The logchannellist structure associates categories and modules with |
83 | | * channels. First the appropriate channellist is found based on the |
84 | | * category, and then each structure in the linked list is checked for |
85 | | * a matching module. It is expected that the number of channels |
86 | | * associated with any given category will be very short, no more than |
87 | | * three or four in the more unusual cases. |
88 | | */ |
89 | | typedef struct isc_logchannellist isc_logchannellist_t; |
90 | | |
91 | | struct isc_logchannellist { |
92 | | const isc_logmodule_t * module; |
93 | | isc_logchannel_t * channel; |
94 | | ISC_LINK(isc_logchannellist_t) link; |
95 | | }; |
96 | | |
97 | | /*! |
98 | | * This structure is used to remember messages for pruning via |
99 | | * isc_log_[v]write1(). |
100 | | */ |
101 | | typedef struct isc_logmessage isc_logmessage_t; |
102 | | |
103 | | struct isc_logmessage { |
104 | | char * text; |
105 | | isc_time_t time; |
106 | | ISC_LINK(isc_logmessage_t) link; |
107 | | }; |
108 | | |
109 | | /*! |
110 | | * The isc_logconfig structure is used to store the configurable information |
111 | | * about where messages are actually supposed to be sent -- the information |
112 | | * that could changed based on some configuration file, as opposed to the |
113 | | * the category/module specification of isc_log_[v]write[1] that is compiled |
114 | | * into a program, or the debug_level which is dynamic state information. |
115 | | */ |
116 | | struct isc_logconfig { |
117 | | unsigned int magic; |
118 | | isc_log_t * lctx; |
119 | | ISC_LIST(isc_logchannel_t) channels; |
120 | | ISC_LIST(isc_logchannellist_t) *channellists; |
121 | | unsigned int channellist_count; |
122 | | unsigned int duplicate_interval; |
123 | | int highest_level; |
124 | | char * tag; |
125 | | isc_boolean_t dynamic; |
126 | | }; |
127 | | |
128 | | /*! |
129 | | * This isc_log structure provides the context for the isc_log functions. |
130 | | * The log context locks itself in isc_log_doit, the internal backend to |
131 | | * isc_log_write. The locking is necessary both to provide exclusive access |
132 | | * to the buffer into which the message is formatted and to guard against |
133 | | * competing threads trying to write to the same syslog resource. (On |
134 | | * some systems, such as BSD/OS, stdio is thread safe but syslog is not.) |
135 | | * Unfortunately, the lock cannot guard against a _different_ logging |
136 | | * context in the same program competing for syslog's attention. Thus |
137 | | * There Can Be Only One, but this is not enforced. |
138 | | * XXXDCL enforce it? |
139 | | * |
140 | | * Note that the category and module information is not locked. |
141 | | * This is because in the usual case, only one isc_log_t is ever created |
142 | | * in a program, and the category/module registration happens only once. |
143 | | * XXXDCL it might be wise to add more locking overall. |
144 | | */ |
145 | | struct isc_log { |
146 | | /* Not locked. */ |
147 | | unsigned int magic; |
148 | | isc_mem_t * mctx; |
149 | | isc_logcategory_t * categories; |
150 | | unsigned int category_count; |
151 | | isc_logmodule_t * modules; |
152 | | unsigned int module_count; |
153 | | int debug_level; |
154 | | isc_mutex_t lock; |
155 | | /* Locked by isc_log lock. */ |
156 | | isc_logconfig_t * logconfig; |
157 | | char buffer[LOG_BUFFER_SIZE]; |
158 | | ISC_LIST(isc_logmessage_t) messages; |
159 | | }; |
160 | | |
161 | | /*! |
162 | | * Used when ISC_LOG_PRINTLEVEL is enabled for a channel. |
163 | | */ |
164 | | static const char *log_level_strings[] = { |
165 | | "debug", |
166 | | "info", |
167 | | "notice", |
168 | | "warning", |
169 | | "error", |
170 | | "critical" |
171 | | }; |
172 | | |
173 | | /*! |
174 | | * Used to convert ISC_LOG_* priorities into syslog priorities. |
175 | | * XXXDCL This will need modification for NT. |
176 | | */ |
177 | | static const int syslog_map[] = { |
178 | | LOG_DEBUG, |
179 | | LOG_INFO, |
180 | | LOG_NOTICE, |
181 | | LOG_WARNING, |
182 | | LOG_ERR, |
183 | | LOG_CRIT |
184 | | }; |
185 | | |
186 | | /*! |
187 | | * When adding new categories, a corresponding ISC_LOGCATEGORY_foo |
188 | | * definition needs to be added to <isc/log.h>. |
189 | | * |
190 | | * The default category is provided so that the internal default can |
191 | | * be overridden. Since the default is always looked up as the first |
192 | | * channellist in the log context, it must come first in isc_categories[]. |
193 | | */ |
194 | | LIBISC_EXTERNAL_DATA isc_logcategory_t isc_categories[] = { |
195 | | { "default", 0 }, /* "default" must come first. */ |
196 | | { "general", 0 }, |
197 | | { NULL, 0 } |
198 | | }; |
199 | | |
200 | | /*! |
201 | | * See above comment for categories on LIBISC_EXTERNAL_DATA, and apply it to modules. |
202 | | */ |
203 | | LIBISC_EXTERNAL_DATA isc_logmodule_t isc_modules[] = { |
204 | | { "socket", 0 }, |
205 | | { "time", 0 }, |
206 | | { "interface", 0 }, |
207 | | { "timer", 0 }, |
208 | | { "file", 0 }, |
209 | | { NULL, 0 } |
210 | | }; |
211 | | |
212 | | /*! |
213 | | * This essentially constant structure must be filled in at run time, |
214 | | * because its channel member is pointed to a channel that is created |
215 | | * dynamically with isc_log_createchannel. |
216 | | */ |
217 | | static isc_logchannellist_t default_channel; |
218 | | |
219 | | /*! |
220 | | * libisc logs to this context. |
221 | | */ |
222 | | LIBISC_EXTERNAL_DATA isc_log_t *isc_lctx = NULL; |
223 | | |
224 | | /*! |
225 | | * Forward declarations. |
226 | | */ |
227 | | static isc_result_t |
228 | | assignchannel(isc_logconfig_t *lcfg, unsigned int category_id, |
229 | | const isc_logmodule_t *module, isc_logchannel_t *channel); |
230 | | |
231 | | static isc_result_t |
232 | | sync_channellist(isc_logconfig_t *lcfg); |
233 | | |
234 | | static isc_result_t |
235 | | greatest_version(isc_logchannel_t *channel, int *greatest); |
236 | | |
237 | | static isc_result_t |
238 | | roll_log(isc_logchannel_t *channel); |
239 | | |
240 | | static void |
241 | | isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category, |
242 | | isc_logmodule_t *module, int level, isc_boolean_t write_once, |
243 | | isc_msgcat_t *msgcat, int msgset, int msg, |
244 | | const char *format, va_list args) |
245 | | ISC_FORMAT_PRINTF(9, 0); |
246 | | |
247 | | /*@{*/ |
248 | | /*! |
249 | | * Convenience macros. |
250 | | */ |
251 | | |
252 | 0 | #define FACILITY(channel) (channel->destination.facility) |
253 | 0 | #define FILE_NAME(channel) (channel->destination.file.name) |
254 | 0 | #define FILE_STREAM(channel) (channel->destination.file.stream) |
255 | 0 | #define FILE_VERSIONS(channel) (channel->destination.file.versions) |
256 | 0 | #define FILE_MAXSIZE(channel) (channel->destination.file.maximum_size) |
257 | 0 | #define FILE_MAXREACHED(channel) (channel->destination.file.maximum_reached) |
258 | | |
259 | | /*@}*/ |
260 | | /**** |
261 | | **** Public interfaces. |
262 | | ****/ |
263 | | |
264 | | /* |
265 | | * Establish a new logging context, with default channels. |
266 | | */ |
267 | | isc_result_t |
268 | 0 | isc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp) { |
269 | 0 | isc_log_t *lctx; |
270 | 0 | isc_logconfig_t *lcfg = NULL; |
271 | 0 | isc_result_t result; |
272 | |
|
273 | 0 | REQUIRE(mctx != NULL); |
274 | 0 | REQUIRE(lctxp != NULL && *lctxp == NULL); |
275 | 0 | REQUIRE(lcfgp == NULL || *lcfgp == NULL); |
276 | | |
277 | 0 | lctx = isc_mem_get(mctx, sizeof(*lctx)); |
278 | 0 | if (lctx != NULL) { |
279 | 0 | lctx->mctx = mctx; |
280 | 0 | lctx->categories = NULL; |
281 | 0 | lctx->category_count = 0; |
282 | 0 | lctx->modules = NULL; |
283 | 0 | lctx->module_count = 0; |
284 | 0 | lctx->debug_level = 0; |
285 | |
|
286 | 0 | ISC_LIST_INIT(lctx->messages); |
287 | |
|
288 | 0 | result = isc_mutex_init(&lctx->lock); |
289 | 0 | if (result != ISC_R_SUCCESS) { |
290 | 0 | isc_mem_put(mctx, lctx, sizeof(*lctx)); |
291 | 0 | return (result); |
292 | 0 | } |
293 | | |
294 | | /* |
295 | | * Normally setting the magic number is the last step done |
296 | | * in a creation function, but a valid log context is needed |
297 | | * by isc_log_registercategories and isc_logconfig_create. |
298 | | * If either fails, the lctx is destroyed and not returned |
299 | | * to the caller. |
300 | | */ |
301 | 0 | lctx->magic = LCTX_MAGIC; |
302 | |
|
303 | 0 | isc_log_registercategories(lctx, isc_categories); |
304 | 0 | isc_log_registermodules(lctx, isc_modules); |
305 | 0 | result = isc_logconfig_create(lctx, &lcfg); |
306 | |
|
307 | 0 | } else |
308 | 0 | result = ISC_R_NOMEMORY; |
309 | | |
310 | 0 | if (result == ISC_R_SUCCESS) |
311 | 0 | result = sync_channellist(lcfg); |
312 | |
|
313 | 0 | if (result == ISC_R_SUCCESS) { |
314 | 0 | lctx->logconfig = lcfg; |
315 | |
|
316 | 0 | *lctxp = lctx; |
317 | 0 | if (lcfgp != NULL) |
318 | 0 | *lcfgp = lcfg; |
319 | |
|
320 | 0 | } else { |
321 | 0 | if (lcfg != NULL) |
322 | 0 | isc_logconfig_destroy(&lcfg); |
323 | 0 | if (lctx != NULL) |
324 | 0 | isc_log_destroy(&lctx); |
325 | 0 | } |
326 | |
|
327 | 0 | return (result); |
328 | 0 | } |
329 | | |
330 | | isc_result_t |
331 | 0 | isc_logconfig_create(isc_log_t *lctx, isc_logconfig_t **lcfgp) { |
332 | 0 | isc_logconfig_t *lcfg; |
333 | 0 | isc_logdestination_t destination; |
334 | 0 | isc_result_t result = ISC_R_SUCCESS; |
335 | 0 | int level = ISC_LOG_INFO; |
336 | |
|
337 | 0 | REQUIRE(lcfgp != NULL && *lcfgp == NULL); |
338 | 0 | REQUIRE(VALID_CONTEXT(lctx)); |
339 | | |
340 | 0 | lcfg = isc_mem_get(lctx->mctx, sizeof(*lcfg)); |
341 | |
|
342 | 0 | if (lcfg != NULL) { |
343 | 0 | lcfg->lctx = lctx; |
344 | 0 | lcfg->channellists = NULL; |
345 | 0 | lcfg->channellist_count = 0; |
346 | 0 | lcfg->duplicate_interval = 0; |
347 | 0 | lcfg->highest_level = level; |
348 | 0 | lcfg->tag = NULL; |
349 | 0 | lcfg->dynamic = ISC_FALSE; |
350 | |
|
351 | 0 | ISC_LIST_INIT(lcfg->channels); |
352 | | |
353 | | /* |
354 | | * Normally the magic number is the last thing set in the |
355 | | * structure, but isc_log_createchannel() needs a valid |
356 | | * config. If the channel creation fails, the lcfg is not |
357 | | * returned to the caller. |
358 | | */ |
359 | 0 | lcfg->magic = LCFG_MAGIC; |
360 | |
|
361 | 0 | } else |
362 | 0 | result = ISC_R_NOMEMORY; |
363 | | |
364 | | /* |
365 | | * Create the default channels: |
366 | | * default_syslog, default_stderr, default_debug and null. |
367 | | */ |
368 | 0 | if (result == ISC_R_SUCCESS) { |
369 | 0 | destination.facility = LOG_DAEMON; |
370 | 0 | result = isc_log_createchannel(lcfg, "default_syslog", |
371 | 0 | ISC_LOG_TOSYSLOG, level, |
372 | 0 | &destination, 0); |
373 | 0 | } |
374 | |
|
375 | 0 | if (result == ISC_R_SUCCESS) { |
376 | 0 | destination.file.stream = stderr; |
377 | 0 | destination.file.name = NULL; |
378 | 0 | destination.file.versions = ISC_LOG_ROLLNEVER; |
379 | 0 | destination.file.maximum_size = 0; |
380 | 0 | result = isc_log_createchannel(lcfg, "default_stderr", |
381 | 0 | ISC_LOG_TOFILEDESC, |
382 | 0 | level, |
383 | 0 | &destination, |
384 | 0 | ISC_LOG_PRINTTIME); |
385 | 0 | } |
386 | |
|
387 | 0 | if (result == ISC_R_SUCCESS) { |
388 | | /* |
389 | | * Set the default category's channel to default_stderr, |
390 | | * which is at the head of the channels list because it was |
391 | | * just created. |
392 | | */ |
393 | 0 | default_channel.channel = ISC_LIST_HEAD(lcfg->channels); |
394 | |
|
395 | 0 | destination.file.stream = stderr; |
396 | 0 | destination.file.name = NULL; |
397 | 0 | destination.file.versions = ISC_LOG_ROLLNEVER; |
398 | 0 | destination.file.maximum_size = 0; |
399 | 0 | result = isc_log_createchannel(lcfg, "default_debug", |
400 | 0 | ISC_LOG_TOFILEDESC, |
401 | 0 | ISC_LOG_DYNAMIC, |
402 | 0 | &destination, |
403 | 0 | ISC_LOG_PRINTTIME); |
404 | 0 | } |
405 | |
|
406 | 0 | if (result == ISC_R_SUCCESS) |
407 | 0 | result = isc_log_createchannel(lcfg, "null", |
408 | 0 | ISC_LOG_TONULL, |
409 | 0 | ISC_LOG_DYNAMIC, |
410 | 0 | NULL, 0); |
411 | |
|
412 | 0 | if (result == ISC_R_SUCCESS) |
413 | 0 | *lcfgp = lcfg; |
414 | | |
415 | 0 | else |
416 | 0 | if (lcfg != NULL) |
417 | 0 | isc_logconfig_destroy(&lcfg); |
418 | |
|
419 | 0 | return (result); |
420 | 0 | } |
421 | | |
422 | | isc_logconfig_t * |
423 | 0 | isc_logconfig_get(isc_log_t *lctx) { |
424 | 0 | REQUIRE(VALID_CONTEXT(lctx)); |
425 | | |
426 | 0 | ENSURE(lctx->logconfig != NULL); |
427 | | |
428 | 0 | return (lctx->logconfig); |
429 | 0 | } |
430 | | |
431 | | isc_result_t |
432 | 0 | isc_logconfig_use(isc_log_t *lctx, isc_logconfig_t *lcfg) { |
433 | 0 | isc_logconfig_t *old_cfg; |
434 | 0 | isc_result_t result; |
435 | |
|
436 | 0 | REQUIRE(VALID_CONTEXT(lctx)); |
437 | 0 | REQUIRE(VALID_CONFIG(lcfg)); |
438 | 0 | REQUIRE(lcfg->lctx == lctx); |
439 | | |
440 | | /* |
441 | | * Ensure that lcfg->channellist_count == lctx->category_count. |
442 | | * They won't be equal if isc_log_usechannel has not been called |
443 | | * since any call to isc_log_registercategories. |
444 | | */ |
445 | 0 | result = sync_channellist(lcfg); |
446 | 0 | if (result != ISC_R_SUCCESS) |
447 | 0 | return (result); |
448 | | |
449 | 0 | LOCK(&lctx->lock); |
450 | |
|
451 | 0 | old_cfg = lctx->logconfig; |
452 | 0 | lctx->logconfig = lcfg; |
453 | |
|
454 | 0 | UNLOCK(&lctx->lock); |
455 | |
|
456 | 0 | isc_logconfig_destroy(&old_cfg); |
457 | |
|
458 | 0 | return (ISC_R_SUCCESS); |
459 | 0 | } |
460 | | |
461 | | void |
462 | 0 | isc_log_destroy(isc_log_t **lctxp) { |
463 | 0 | isc_log_t *lctx; |
464 | 0 | isc_logconfig_t *lcfg; |
465 | 0 | isc_mem_t *mctx; |
466 | 0 | isc_logmessage_t *message; |
467 | |
|
468 | 0 | REQUIRE(lctxp != NULL && VALID_CONTEXT(*lctxp)); |
469 | | |
470 | 0 | lctx = *lctxp; |
471 | 0 | mctx = lctx->mctx; |
472 | |
|
473 | 0 | if (lctx->logconfig != NULL) { |
474 | 0 | lcfg = lctx->logconfig; |
475 | 0 | lctx->logconfig = NULL; |
476 | 0 | isc_logconfig_destroy(&lcfg); |
477 | 0 | } |
478 | |
|
479 | 0 | DESTROYLOCK(&lctx->lock); |
480 | |
|
481 | 0 | while ((message = ISC_LIST_HEAD(lctx->messages)) != NULL) { |
482 | 0 | ISC_LIST_UNLINK(lctx->messages, message, link); |
483 | | |
484 | 0 | isc_mem_put(mctx, message, |
485 | 0 | sizeof(*message) + strlen(message->text) + 1); |
486 | 0 | } |
487 | |
|
488 | 0 | lctx->buffer[0] = '\0'; |
489 | 0 | lctx->debug_level = 0; |
490 | 0 | lctx->categories = NULL; |
491 | 0 | lctx->category_count = 0; |
492 | 0 | lctx->modules = NULL; |
493 | 0 | lctx->module_count = 0; |
494 | 0 | lctx->mctx = NULL; |
495 | 0 | lctx->magic = 0; |
496 | |
|
497 | 0 | isc_mem_put(mctx, lctx, sizeof(*lctx)); |
498 | |
|
499 | 0 | *lctxp = NULL; |
500 | 0 | } |
501 | | |
502 | | void |
503 | 0 | isc_logconfig_destroy(isc_logconfig_t **lcfgp) { |
504 | 0 | isc_logconfig_t *lcfg; |
505 | 0 | isc_mem_t *mctx; |
506 | 0 | isc_logchannel_t *channel; |
507 | 0 | isc_logchannellist_t *item; |
508 | 0 | char *filename; |
509 | 0 | unsigned int i; |
510 | |
|
511 | 0 | REQUIRE(lcfgp != NULL && VALID_CONFIG(*lcfgp)); |
512 | | |
513 | 0 | lcfg = *lcfgp; |
514 | | |
515 | | /* |
516 | | * This function cannot be called with a logconfig that is in |
517 | | * use by a log context. |
518 | | */ |
519 | 0 | REQUIRE(lcfg->lctx != NULL && lcfg->lctx->logconfig != lcfg); |
520 | | |
521 | 0 | mctx = lcfg->lctx->mctx; |
522 | |
|
523 | 0 | while ((channel = ISC_LIST_HEAD(lcfg->channels)) != NULL) { |
524 | 0 | ISC_LIST_UNLINK(lcfg->channels, channel, link); |
525 | | |
526 | 0 | if (channel->type == ISC_LOG_TOFILE) { |
527 | | /* |
528 | | * The filename for the channel may have ultimately |
529 | | * started its life in user-land as a const string, |
530 | | * but in isc_log_createchannel it gets copied |
531 | | * into writable memory and is not longer truly const. |
532 | | */ |
533 | 0 | DE_CONST(FILE_NAME(channel), filename); |
534 | 0 | isc_mem_free(mctx, filename); |
535 | |
|
536 | 0 | if (FILE_STREAM(channel) != NULL) |
537 | 0 | (void)fclose(FILE_STREAM(channel)); |
538 | 0 | } |
539 | |
|
540 | 0 | isc_mem_free(mctx, channel->name); |
541 | 0 | isc_mem_put(mctx, channel, sizeof(*channel)); |
542 | 0 | } |
543 | |
|
544 | 0 | for (i = 0; i < lcfg->channellist_count; i++) |
545 | 0 | while ((item = ISC_LIST_HEAD(lcfg->channellists[i])) != NULL) { |
546 | 0 | ISC_LIST_UNLINK(lcfg->channellists[i], item, link); |
547 | 0 | isc_mem_put(mctx, item, sizeof(*item)); |
548 | 0 | } |
549 | |
|
550 | 0 | if (lcfg->channellist_count > 0) |
551 | 0 | isc_mem_put(mctx, lcfg->channellists, |
552 | 0 | lcfg->channellist_count * |
553 | 0 | sizeof(ISC_LIST(isc_logchannellist_t))); |
554 | |
|
555 | 0 | lcfg->dynamic = ISC_FALSE; |
556 | 0 | if (lcfg->tag != NULL) |
557 | 0 | isc_mem_free(lcfg->lctx->mctx, lcfg->tag); |
558 | 0 | lcfg->tag = NULL; |
559 | 0 | lcfg->highest_level = 0; |
560 | 0 | lcfg->duplicate_interval = 0; |
561 | 0 | lcfg->magic = 0; |
562 | |
|
563 | 0 | isc_mem_put(mctx, lcfg, sizeof(*lcfg)); |
564 | |
|
565 | 0 | *lcfgp = NULL; |
566 | 0 | } |
567 | | |
568 | | void |
569 | 0 | isc_log_registercategories(isc_log_t *lctx, isc_logcategory_t categories[]) { |
570 | 0 | isc_logcategory_t *catp; |
571 | |
|
572 | 0 | REQUIRE(VALID_CONTEXT(lctx)); |
573 | 0 | REQUIRE(categories != NULL && categories[0].name != NULL); |
574 | | |
575 | | /* |
576 | | * XXXDCL This somewhat sleazy situation of using the last pointer |
577 | | * in one category array to point to the next array exists because |
578 | | * this registration function returns void and I didn't want to have |
579 | | * change everything that used it by making it return an isc_result_t. |
580 | | * It would need to do that if it had to allocate memory to store |
581 | | * pointers to each array passed in. |
582 | | */ |
583 | 0 | if (lctx->categories == NULL) |
584 | 0 | lctx->categories = categories; |
585 | | |
586 | 0 | else { |
587 | | /* |
588 | | * Adjust the last (NULL) pointer of the already registered |
589 | | * categories to point to the incoming array. |
590 | | */ |
591 | 0 | for (catp = lctx->categories; catp->name != NULL; ) |
592 | 0 | if (catp->id == UINT_MAX) |
593 | | /* |
594 | | * The name pointer points to the next array. |
595 | | * Ick. |
596 | | */ |
597 | 0 | DE_CONST(catp->name, catp); |
598 | 0 | else |
599 | 0 | catp++; |
600 | |
|
601 | 0 | catp->name = (void *)categories; |
602 | 0 | catp->id = UINT_MAX; |
603 | 0 | } |
604 | | |
605 | | /* |
606 | | * Update the id number of the category with its new global id. |
607 | | */ |
608 | 0 | for (catp = categories; catp->name != NULL; catp++) |
609 | 0 | catp->id = lctx->category_count++; |
610 | 0 | } |
611 | | |
612 | | isc_logcategory_t * |
613 | 0 | isc_log_categorybyname(isc_log_t *lctx, const char *name) { |
614 | 0 | isc_logcategory_t *catp; |
615 | |
|
616 | 0 | REQUIRE(VALID_CONTEXT(lctx)); |
617 | 0 | REQUIRE(name != NULL); |
618 | | |
619 | 0 | for (catp = lctx->categories; catp->name != NULL; ) |
620 | 0 | if (catp->id == UINT_MAX) |
621 | | /* |
622 | | * catp is neither modified nor returned to the |
623 | | * caller, so removing its const qualifier is ok. |
624 | | */ |
625 | 0 | DE_CONST(catp->name, catp); |
626 | 0 | else { |
627 | 0 | if (strcmp(catp->name, name) == 0) |
628 | 0 | return (catp); |
629 | 0 | catp++; |
630 | 0 | } |
631 | | |
632 | 0 | return (NULL); |
633 | 0 | } |
634 | | |
635 | | void |
636 | 0 | isc_log_registermodules(isc_log_t *lctx, isc_logmodule_t modules[]) { |
637 | 0 | isc_logmodule_t *modp; |
638 | |
|
639 | 0 | REQUIRE(VALID_CONTEXT(lctx)); |
640 | 0 | REQUIRE(modules != NULL && modules[0].name != NULL); |
641 | | |
642 | | /* |
643 | | * XXXDCL This somewhat sleazy situation of using the last pointer |
644 | | * in one category array to point to the next array exists because |
645 | | * this registration function returns void and I didn't want to have |
646 | | * change everything that used it by making it return an isc_result_t. |
647 | | * It would need to do that if it had to allocate memory to store |
648 | | * pointers to each array passed in. |
649 | | */ |
650 | 0 | if (lctx->modules == NULL) |
651 | 0 | lctx->modules = modules; |
652 | | |
653 | 0 | else { |
654 | | /* |
655 | | * Adjust the last (NULL) pointer of the already registered |
656 | | * modules to point to the incoming array. |
657 | | */ |
658 | 0 | for (modp = lctx->modules; modp->name != NULL; ) |
659 | 0 | if (modp->id == UINT_MAX) |
660 | | /* |
661 | | * The name pointer points to the next array. |
662 | | * Ick. |
663 | | */ |
664 | 0 | DE_CONST(modp->name, modp); |
665 | 0 | else |
666 | 0 | modp++; |
667 | |
|
668 | 0 | modp->name = (void *)modules; |
669 | 0 | modp->id = UINT_MAX; |
670 | 0 | } |
671 | | |
672 | | /* |
673 | | * Update the id number of the module with its new global id. |
674 | | */ |
675 | 0 | for (modp = modules; modp->name != NULL; modp++) |
676 | 0 | modp->id = lctx->module_count++; |
677 | 0 | } |
678 | | |
679 | | isc_logmodule_t * |
680 | 0 | isc_log_modulebyname(isc_log_t *lctx, const char *name) { |
681 | 0 | isc_logmodule_t *modp; |
682 | |
|
683 | 0 | REQUIRE(VALID_CONTEXT(lctx)); |
684 | 0 | REQUIRE(name != NULL); |
685 | | |
686 | 0 | for (modp = lctx->modules; modp->name != NULL; ) |
687 | 0 | if (modp->id == UINT_MAX) |
688 | | /* |
689 | | * modp is neither modified nor returned to the |
690 | | * caller, so removing its const qualifier is ok. |
691 | | */ |
692 | 0 | DE_CONST(modp->name, modp); |
693 | 0 | else { |
694 | 0 | if (strcmp(modp->name, name) == 0) |
695 | 0 | return (modp); |
696 | 0 | modp++; |
697 | 0 | } |
698 | | |
699 | 0 | return (NULL); |
700 | 0 | } |
701 | | |
702 | | isc_result_t |
703 | | isc_log_createchannel(isc_logconfig_t *lcfg, const char *name, |
704 | | unsigned int type, int level, |
705 | | const isc_logdestination_t *destination, |
706 | | unsigned int flags) |
707 | 0 | { |
708 | 0 | isc_logchannel_t *channel; |
709 | 0 | isc_mem_t *mctx; |
710 | |
|
711 | 0 | REQUIRE(VALID_CONFIG(lcfg)); |
712 | 0 | REQUIRE(name != NULL); |
713 | 0 | REQUIRE(type == ISC_LOG_TOSYSLOG || type == ISC_LOG_TOFILE || |
714 | 0 | type == ISC_LOG_TOFILEDESC || type == ISC_LOG_TONULL); |
715 | 0 | REQUIRE(destination != NULL || type == ISC_LOG_TONULL); |
716 | 0 | REQUIRE(level >= ISC_LOG_CRITICAL); |
717 | 0 | REQUIRE((flags & |
718 | 0 | (unsigned int)~(ISC_LOG_PRINTALL | ISC_LOG_DEBUGONLY)) == 0); |
719 | | |
720 | | /* XXXDCL find duplicate names? */ |
721 | | |
722 | 0 | mctx = lcfg->lctx->mctx; |
723 | |
|
724 | 0 | channel = isc_mem_get(mctx, sizeof(*channel)); |
725 | 0 | if (channel == NULL) |
726 | 0 | return (ISC_R_NOMEMORY); |
727 | | |
728 | 0 | channel->name = isc_mem_strdup(mctx, name); |
729 | 0 | if (channel->name == NULL) { |
730 | 0 | isc_mem_put(mctx, channel, sizeof(*channel)); |
731 | 0 | return (ISC_R_NOMEMORY); |
732 | 0 | } |
733 | | |
734 | 0 | channel->type = type; |
735 | 0 | channel->level = level; |
736 | 0 | channel->flags = flags; |
737 | 0 | ISC_LINK_INIT(channel, link); |
738 | |
|
739 | 0 | switch (type) { |
740 | 0 | case ISC_LOG_TOSYSLOG: |
741 | 0 | FACILITY(channel) = destination->facility; |
742 | 0 | break; |
743 | | |
744 | 0 | case ISC_LOG_TOFILE: |
745 | | /* |
746 | | * The file name is copied because greatest_version wants |
747 | | * to scribble on it, so it needs to be definitely in |
748 | | * writable memory. |
749 | | */ |
750 | 0 | FILE_NAME(channel) = |
751 | 0 | isc_mem_strdup(mctx, destination->file.name); |
752 | 0 | FILE_STREAM(channel) = NULL; |
753 | 0 | FILE_VERSIONS(channel) = destination->file.versions; |
754 | 0 | FILE_MAXSIZE(channel) = destination->file.maximum_size; |
755 | 0 | FILE_MAXREACHED(channel) = ISC_FALSE; |
756 | 0 | break; |
757 | | |
758 | 0 | case ISC_LOG_TOFILEDESC: |
759 | 0 | FILE_NAME(channel) = NULL; |
760 | 0 | FILE_STREAM(channel) = destination->file.stream; |
761 | 0 | FILE_MAXSIZE(channel) = 0; |
762 | 0 | FILE_VERSIONS(channel) = ISC_LOG_ROLLNEVER; |
763 | 0 | break; |
764 | | |
765 | 0 | case ISC_LOG_TONULL: |
766 | | /* Nothing. */ |
767 | 0 | break; |
768 | | |
769 | 0 | default: |
770 | 0 | isc_mem_put(mctx, channel->name, strlen(channel->name) + 1); |
771 | 0 | isc_mem_put(mctx, channel, sizeof(*channel)); |
772 | 0 | return (ISC_R_UNEXPECTED); |
773 | 0 | } |
774 | | |
775 | 0 | ISC_LIST_PREPEND(lcfg->channels, channel, link); |
776 | | |
777 | | /* |
778 | | * If default_stderr was redefined, make the default category |
779 | | * point to the new default_stderr. |
780 | | */ |
781 | 0 | if (strcmp(name, "default_stderr") == 0) |
782 | 0 | default_channel.channel = channel; |
783 | |
|
784 | 0 | return (ISC_R_SUCCESS); |
785 | 0 | } |
786 | | |
787 | | isc_result_t |
788 | | isc_log_usechannel(isc_logconfig_t *lcfg, const char *name, |
789 | | const isc_logcategory_t *category, |
790 | | const isc_logmodule_t *module) |
791 | 0 | { |
792 | 0 | isc_log_t *lctx; |
793 | 0 | isc_logchannel_t *channel; |
794 | 0 | isc_result_t result = ISC_R_SUCCESS; |
795 | 0 | unsigned int i; |
796 | |
|
797 | 0 | REQUIRE(VALID_CONFIG(lcfg)); |
798 | 0 | REQUIRE(name != NULL); |
799 | | |
800 | 0 | lctx = lcfg->lctx; |
801 | |
|
802 | 0 | REQUIRE(category == NULL || category->id < lctx->category_count); |
803 | 0 | REQUIRE(module == NULL || module->id < lctx->module_count); |
804 | | |
805 | 0 | for (channel = ISC_LIST_HEAD(lcfg->channels); channel != NULL; |
806 | 0 | channel = ISC_LIST_NEXT(channel, link)) |
807 | 0 | if (strcmp(name, channel->name) == 0) |
808 | 0 | break; |
809 | |
|
810 | 0 | if (channel == NULL) |
811 | 0 | return (ISC_R_NOTFOUND); |
812 | | |
813 | 0 | if (category != NULL) |
814 | 0 | result = assignchannel(lcfg, category->id, module, channel); |
815 | | |
816 | 0 | else |
817 | | /* |
818 | | * Assign to all categories. Note that this includes |
819 | | * the default channel. |
820 | | */ |
821 | 0 | for (i = 0; i < lctx->category_count; i++) { |
822 | 0 | result = assignchannel(lcfg, i, module, channel); |
823 | 0 | if (result != ISC_R_SUCCESS) |
824 | 0 | break; |
825 | 0 | } |
826 | |
|
827 | 0 | return (result); |
828 | 0 | } |
829 | | |
830 | | void |
831 | | isc_log_write(isc_log_t *lctx, isc_logcategory_t *category, |
832 | | isc_logmodule_t *module, int level, const char *format, ...) |
833 | 0 | { |
834 | 0 | va_list args; |
835 | | |
836 | | /* |
837 | | * Contract checking is done in isc_log_doit(). |
838 | | */ |
839 | |
|
840 | 0 | va_start(args, format); |
841 | 0 | isc_log_doit(lctx, category, module, level, ISC_FALSE, |
842 | 0 | NULL, 0, 0, format, args); |
843 | 0 | va_end(args); |
844 | 0 | } |
845 | | |
846 | | void |
847 | | isc_log_vwrite(isc_log_t *lctx, isc_logcategory_t *category, |
848 | | isc_logmodule_t *module, int level, |
849 | | const char *format, va_list args) |
850 | 0 | { |
851 | | /* |
852 | | * Contract checking is done in isc_log_doit(). |
853 | | */ |
854 | 0 | isc_log_doit(lctx, category, module, level, ISC_FALSE, |
855 | 0 | NULL, 0, 0, format, args); |
856 | 0 | } |
857 | | |
858 | | void |
859 | | isc_log_write1(isc_log_t *lctx, isc_logcategory_t *category, |
860 | | isc_logmodule_t *module, int level, const char *format, ...) |
861 | 0 | { |
862 | 0 | va_list args; |
863 | | |
864 | | /* |
865 | | * Contract checking is done in isc_log_doit(). |
866 | | */ |
867 | |
|
868 | 0 | va_start(args, format); |
869 | 0 | isc_log_doit(lctx, category, module, level, ISC_TRUE, |
870 | 0 | NULL, 0, 0, format, args); |
871 | 0 | va_end(args); |
872 | 0 | } |
873 | | |
874 | | void |
875 | | isc_log_vwrite1(isc_log_t *lctx, isc_logcategory_t *category, |
876 | | isc_logmodule_t *module, int level, |
877 | | const char *format, va_list args) |
878 | 0 | { |
879 | | /* |
880 | | * Contract checking is done in isc_log_doit(). |
881 | | */ |
882 | 0 | isc_log_doit(lctx, category, module, level, ISC_TRUE, |
883 | 0 | NULL, 0, 0, format, args); |
884 | 0 | } |
885 | | |
886 | | void |
887 | | isc_log_iwrite(isc_log_t *lctx, isc_logcategory_t *category, |
888 | | isc_logmodule_t *module, int level, |
889 | | isc_msgcat_t *msgcat, int msgset, int msg, |
890 | | const char *format, ...) |
891 | 0 | { |
892 | 0 | va_list args; |
893 | | |
894 | | /* |
895 | | * Contract checking is done in isc_log_doit(). |
896 | | */ |
897 | |
|
898 | 0 | va_start(args, format); |
899 | 0 | isc_log_doit(lctx, category, module, level, ISC_FALSE, |
900 | 0 | msgcat, msgset, msg, format, args); |
901 | 0 | va_end(args); |
902 | 0 | } |
903 | | |
904 | | void |
905 | | isc_log_ivwrite(isc_log_t *lctx, isc_logcategory_t *category, |
906 | | isc_logmodule_t *module, int level, |
907 | | isc_msgcat_t *msgcat, int msgset, int msg, |
908 | | const char *format, va_list args) |
909 | 0 | { |
910 | | /* |
911 | | * Contract checking is done in isc_log_doit(). |
912 | | */ |
913 | 0 | isc_log_doit(lctx, category, module, level, ISC_FALSE, |
914 | 0 | msgcat, msgset, msg, format, args); |
915 | 0 | } |
916 | | |
917 | | void |
918 | | isc_log_iwrite1(isc_log_t *lctx, isc_logcategory_t *category, |
919 | | isc_logmodule_t *module, int level, |
920 | | isc_msgcat_t *msgcat, int msgset, int msg, |
921 | | const char *format, ...) |
922 | 0 | { |
923 | 0 | va_list args; |
924 | | |
925 | | /* |
926 | | * Contract checking is done in isc_log_doit(). |
927 | | */ |
928 | |
|
929 | 0 | va_start(args, format); |
930 | 0 | isc_log_doit(lctx, category, module, level, ISC_TRUE, |
931 | 0 | msgcat, msgset, msg, format, args); |
932 | 0 | va_end(args); |
933 | 0 | } |
934 | | |
935 | | void |
936 | | isc_log_ivwrite1(isc_log_t *lctx, isc_logcategory_t *category, |
937 | | isc_logmodule_t *module, int level, |
938 | | isc_msgcat_t *msgcat, int msgset, int msg, |
939 | | const char *format, va_list args) |
940 | 0 | { |
941 | | /* |
942 | | * Contract checking is done in isc_log_doit(). |
943 | | */ |
944 | 0 | isc_log_doit(lctx, category, module, level, ISC_TRUE, |
945 | 0 | msgcat, msgset, msg, format, args); |
946 | 0 | } |
947 | | |
948 | | void |
949 | 0 | isc_log_setcontext(isc_log_t *lctx) { |
950 | 0 | isc_lctx = lctx; |
951 | 0 | } |
952 | | |
953 | | void |
954 | 0 | isc_log_setdebuglevel(isc_log_t *lctx, unsigned int level) { |
955 | 0 | isc_logchannel_t *channel; |
956 | |
|
957 | 0 | REQUIRE(VALID_CONTEXT(lctx)); |
958 | | |
959 | 0 | LOCK(&lctx->lock); |
960 | |
|
961 | 0 | lctx->debug_level = level; |
962 | | /* |
963 | | * Close ISC_LOG_DEBUGONLY channels if level is zero. |
964 | | */ |
965 | 0 | if (lctx->debug_level == 0) |
966 | 0 | for (channel = ISC_LIST_HEAD(lctx->logconfig->channels); |
967 | 0 | channel != NULL; |
968 | 0 | channel = ISC_LIST_NEXT(channel, link)) |
969 | 0 | if (channel->type == ISC_LOG_TOFILE && |
970 | 0 | (channel->flags & ISC_LOG_DEBUGONLY) != 0 && |
971 | 0 | FILE_STREAM(channel) != NULL) { |
972 | 0 | (void)fclose(FILE_STREAM(channel)); |
973 | 0 | FILE_STREAM(channel) = NULL; |
974 | 0 | } |
975 | 0 | UNLOCK(&lctx->lock); |
976 | 0 | } |
977 | | |
978 | | unsigned int |
979 | 0 | isc_log_getdebuglevel(isc_log_t *lctx) { |
980 | 0 | REQUIRE(VALID_CONTEXT(lctx)); |
981 | | |
982 | 0 | return (lctx->debug_level); |
983 | 0 | } |
984 | | |
985 | | void |
986 | 0 | isc_log_setduplicateinterval(isc_logconfig_t *lcfg, unsigned int interval) { |
987 | 0 | REQUIRE(VALID_CONFIG(lcfg)); |
988 | | |
989 | 0 | lcfg->duplicate_interval = interval; |
990 | 0 | } |
991 | | |
992 | | unsigned int |
993 | 0 | isc_log_getduplicateinterval(isc_logconfig_t *lcfg) { |
994 | 0 | REQUIRE(VALID_CONTEXT(lcfg)); |
995 | | |
996 | 0 | return (lcfg->duplicate_interval); |
997 | 0 | } |
998 | | |
999 | | isc_result_t |
1000 | 0 | isc_log_settag(isc_logconfig_t *lcfg, const char *tag) { |
1001 | 0 | REQUIRE(VALID_CONFIG(lcfg)); |
1002 | | |
1003 | 0 | if (tag != NULL && *tag != '\0') { |
1004 | 0 | if (lcfg->tag != NULL) |
1005 | 0 | isc_mem_free(lcfg->lctx->mctx, lcfg->tag); |
1006 | 0 | lcfg->tag = isc_mem_strdup(lcfg->lctx->mctx, tag); |
1007 | 0 | if (lcfg->tag == NULL) |
1008 | 0 | return (ISC_R_NOMEMORY); |
1009 | |
|
1010 | 0 | } else { |
1011 | 0 | if (lcfg->tag != NULL) |
1012 | 0 | isc_mem_free(lcfg->lctx->mctx, lcfg->tag); |
1013 | 0 | lcfg->tag = NULL; |
1014 | 0 | } |
1015 | | |
1016 | 0 | return (ISC_R_SUCCESS); |
1017 | 0 | } |
1018 | | |
1019 | | char * |
1020 | 0 | isc_log_gettag(isc_logconfig_t *lcfg) { |
1021 | 0 | REQUIRE(VALID_CONFIG(lcfg)); |
1022 | | |
1023 | 0 | return (lcfg->tag); |
1024 | 0 | } |
1025 | | |
1026 | | /* XXXDCL NT -- This interface will assuredly be changing. */ |
1027 | | void |
1028 | 0 | isc_log_opensyslog(const char *tag, int options, int facility) { |
1029 | 0 | (void)openlog(tag, options, facility); |
1030 | 0 | } |
1031 | | |
1032 | | void |
1033 | 0 | isc_log_closefilelogs(isc_log_t *lctx) { |
1034 | 0 | isc_logchannel_t *channel; |
1035 | |
|
1036 | 0 | REQUIRE(VALID_CONTEXT(lctx)); |
1037 | | |
1038 | 0 | LOCK(&lctx->lock); |
1039 | 0 | for (channel = ISC_LIST_HEAD(lctx->logconfig->channels); |
1040 | 0 | channel != NULL; |
1041 | 0 | channel = ISC_LIST_NEXT(channel, link)) |
1042 | | |
1043 | 0 | if (channel->type == ISC_LOG_TOFILE && |
1044 | 0 | FILE_STREAM(channel) != NULL) { |
1045 | 0 | (void)fclose(FILE_STREAM(channel)); |
1046 | 0 | FILE_STREAM(channel) = NULL; |
1047 | 0 | } |
1048 | 0 | UNLOCK(&lctx->lock); |
1049 | 0 | } |
1050 | | |
1051 | | /**** |
1052 | | **** Internal functions |
1053 | | ****/ |
1054 | | |
1055 | | static isc_result_t |
1056 | | assignchannel(isc_logconfig_t *lcfg, unsigned int category_id, |
1057 | | const isc_logmodule_t *module, isc_logchannel_t *channel) |
1058 | 0 | { |
1059 | 0 | isc_logchannellist_t *new_item; |
1060 | 0 | isc_log_t *lctx; |
1061 | 0 | isc_result_t result; |
1062 | |
|
1063 | 0 | REQUIRE(VALID_CONFIG(lcfg)); |
1064 | | |
1065 | 0 | lctx = lcfg->lctx; |
1066 | |
|
1067 | 0 | REQUIRE(category_id < lctx->category_count); |
1068 | 0 | REQUIRE(module == NULL || module->id < lctx->module_count); |
1069 | 0 | REQUIRE(channel != NULL); |
1070 | | |
1071 | | /* |
1072 | | * Ensure lcfg->channellist_count == lctx->category_count. |
1073 | | */ |
1074 | 0 | result = sync_channellist(lcfg); |
1075 | 0 | if (result != ISC_R_SUCCESS) |
1076 | 0 | return (result); |
1077 | | |
1078 | 0 | new_item = isc_mem_get(lctx->mctx, sizeof(*new_item)); |
1079 | 0 | if (new_item == NULL) |
1080 | 0 | return (ISC_R_NOMEMORY); |
1081 | | |
1082 | 0 | new_item->channel = channel; |
1083 | 0 | new_item->module = module; |
1084 | 0 | ISC_LIST_INITANDPREPEND(lcfg->channellists[category_id], |
1085 | 0 | new_item, link); |
1086 | | |
1087 | | /* |
1088 | | * Remember the highest logging level set by any channel in the |
1089 | | * logging config, so isc_log_doit() can quickly return if the |
1090 | | * message is too high to be logged by any channel. |
1091 | | */ |
1092 | 0 | if (channel->type != ISC_LOG_TONULL) { |
1093 | 0 | if (lcfg->highest_level < channel->level) |
1094 | 0 | lcfg->highest_level = channel->level; |
1095 | 0 | if (channel->level == ISC_LOG_DYNAMIC) |
1096 | 0 | lcfg->dynamic = ISC_TRUE; |
1097 | 0 | } |
1098 | |
|
1099 | 0 | return (ISC_R_SUCCESS); |
1100 | 0 | } |
1101 | | |
1102 | | /* |
1103 | | * This would ideally be part of isc_log_registercategories(), except then |
1104 | | * that function would have to return isc_result_t instead of void. |
1105 | | */ |
1106 | | static isc_result_t |
1107 | 0 | sync_channellist(isc_logconfig_t *lcfg) { |
1108 | 0 | unsigned int bytes; |
1109 | 0 | isc_log_t *lctx; |
1110 | 0 | void *lists; |
1111 | |
|
1112 | 0 | REQUIRE(VALID_CONFIG(lcfg)); |
1113 | | |
1114 | 0 | lctx = lcfg->lctx; |
1115 | |
|
1116 | 0 | REQUIRE(lctx->category_count != 0); |
1117 | | |
1118 | 0 | if (lctx->category_count == lcfg->channellist_count) |
1119 | 0 | return (ISC_R_SUCCESS); |
1120 | | |
1121 | 0 | bytes = lctx->category_count * sizeof(ISC_LIST(isc_logchannellist_t)); |
1122 | |
|
1123 | 0 | lists = isc_mem_get(lctx->mctx, bytes); |
1124 | |
|
1125 | 0 | if (lists == NULL) |
1126 | 0 | return (ISC_R_NOMEMORY); |
1127 | | |
1128 | 0 | memset(lists, 0, bytes); |
1129 | |
|
1130 | 0 | if (lcfg->channellist_count != 0) { |
1131 | 0 | bytes = lcfg->channellist_count * |
1132 | 0 | sizeof(ISC_LIST(isc_logchannellist_t)); |
1133 | 0 | memcpy(lists, lcfg->channellists, bytes); |
1134 | 0 | isc_mem_put(lctx->mctx, lcfg->channellists, bytes); |
1135 | 0 | } |
1136 | |
|
1137 | 0 | lcfg->channellists = lists; |
1138 | 0 | lcfg->channellist_count = lctx->category_count; |
1139 | |
|
1140 | 0 | return (ISC_R_SUCCESS); |
1141 | 0 | } |
1142 | | |
1143 | | static isc_result_t |
1144 | 0 | greatest_version(isc_logchannel_t *channel, int *greatestp) { |
1145 | | /* XXXDCL HIGHLY NT */ |
1146 | 0 | char *basenam, *digit_end; |
1147 | 0 | const char *dirname; |
1148 | 0 | int version, greatest = -1; |
1149 | 0 | size_t basenamelen; |
1150 | 0 | isc_dir_t dir; |
1151 | 0 | isc_result_t result; |
1152 | 0 | char sep = '/'; |
1153 | | #ifdef _WIN32 |
1154 | | char *basename2; |
1155 | | #endif |
1156 | |
|
1157 | 0 | REQUIRE(channel->type == ISC_LOG_TOFILE); |
1158 | | |
1159 | | /* |
1160 | | * It is safe to DE_CONST the file.name because it was copied |
1161 | | * with isc_mem_strdup in isc_log_createchannel. |
1162 | | */ |
1163 | 0 | basenam = strrchr(FILE_NAME(channel), sep); |
1164 | | #ifdef _WIN32 |
1165 | | basename2 = strrchr(FILE_NAME(channel), '\\'); |
1166 | | if ((basenam != NULL && basename2 != NULL && basename2 > basenam) || |
1167 | | (basenam == NULL && basename2 != NULL)) { |
1168 | | basenam = basename2; |
1169 | | sep = '\\'; |
1170 | | } |
1171 | | #endif |
1172 | 0 | if (basenam != NULL) { |
1173 | 0 | *basenam++ = '\0'; |
1174 | 0 | dirname = FILE_NAME(channel); |
1175 | 0 | } else { |
1176 | 0 | DE_CONST(FILE_NAME(channel), basenam); |
1177 | 0 | dirname = "."; |
1178 | 0 | } |
1179 | 0 | basenamelen = strlen(basenam); |
1180 | |
|
1181 | 0 | isc_dir_init(&dir); |
1182 | 0 | result = isc_dir_open(&dir, dirname); |
1183 | | |
1184 | | /* |
1185 | | * Replace the file separator if it was taken out. |
1186 | | */ |
1187 | 0 | if (basenam != FILE_NAME(channel)) |
1188 | 0 | *(basenam - 1) = sep; |
1189 | | |
1190 | | /* |
1191 | | * Return if the directory open failed. |
1192 | | */ |
1193 | 0 | if (result != ISC_R_SUCCESS) |
1194 | 0 | return (result); |
1195 | | |
1196 | 0 | while (isc_dir_read(&dir) == ISC_R_SUCCESS) { |
1197 | 0 | if (dir.entry.length > basenamelen && |
1198 | 0 | strncmp(dir.entry.name, basenam, basenamelen) == 0 && |
1199 | 0 | dir.entry.name[basenamelen] == '.') { |
1200 | |
|
1201 | 0 | version = strtol(&dir.entry.name[basenamelen + 1], |
1202 | 0 | &digit_end, 10); |
1203 | 0 | if (*digit_end == '\0' && version > greatest) |
1204 | 0 | greatest = version; |
1205 | 0 | } |
1206 | 0 | } |
1207 | 0 | isc_dir_close(&dir); |
1208 | |
|
1209 | 0 | *greatestp = ++greatest; |
1210 | |
|
1211 | 0 | return (ISC_R_SUCCESS); |
1212 | 0 | } |
1213 | | |
1214 | | static isc_result_t |
1215 | 0 | roll_log(isc_logchannel_t *channel) { |
1216 | 0 | int i, n, greatest; |
1217 | 0 | char current[PATH_MAX + 1]; |
1218 | 0 | char new[PATH_MAX + 1]; |
1219 | 0 | const char *path; |
1220 | 0 | isc_result_t result; |
1221 | | |
1222 | | /* |
1223 | | * Do nothing (not even excess version trimming) if ISC_LOG_ROLLNEVER |
1224 | | * is specified. Apparently complete external control over the log |
1225 | | * files is desired. |
1226 | | */ |
1227 | 0 | if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER) |
1228 | 0 | return (ISC_R_SUCCESS); |
1229 | | |
1230 | 0 | path = FILE_NAME(channel); |
1231 | | |
1232 | | /* |
1233 | | * Set greatest_version to the greatest existing version |
1234 | | * (not the maximum requested version). This is 1 based even |
1235 | | * though the file names are 0 based, so an oldest log of log.1 |
1236 | | * is a greatest_version of 2. |
1237 | | */ |
1238 | 0 | result = greatest_version(channel, &greatest); |
1239 | 0 | if (result != ISC_R_SUCCESS) |
1240 | 0 | return (result); |
1241 | | |
1242 | | /* |
1243 | | * Now greatest should be set to the highest version number desired. |
1244 | | * Since the highest number is one less than FILE_VERSIONS(channel) |
1245 | | * when not doing infinite log rolling, greatest will need to be |
1246 | | * decremented when it is equal to -- or greater than -- |
1247 | | * FILE_VERSIONS(channel). When greatest is less than |
1248 | | * FILE_VERSIONS(channel), it is already suitable for use as |
1249 | | * the maximum version number. |
1250 | | */ |
1251 | | |
1252 | 0 | if (FILE_VERSIONS(channel) == ISC_LOG_ROLLINFINITE || |
1253 | 0 | FILE_VERSIONS(channel) > greatest) |
1254 | 0 | ; /* Do nothing. */ |
1255 | 0 | else |
1256 | | /* |
1257 | | * When greatest is >= FILE_VERSIONS(channel), it needs to |
1258 | | * be reduced until it is FILE_VERSIONS(channel) - 1. |
1259 | | * Remove any excess logs on the way to that value. |
1260 | | */ |
1261 | 0 | while (--greatest >= FILE_VERSIONS(channel)) { |
1262 | 0 | n = snprintf(current, sizeof(current), "%s.%d", |
1263 | 0 | path, greatest); |
1264 | 0 | if (n >= (int)sizeof(current) || n < 0) |
1265 | 0 | result = ISC_R_NOSPACE; |
1266 | 0 | else |
1267 | 0 | result = isc_file_remove(current); |
1268 | 0 | if (result != ISC_R_SUCCESS && |
1269 | 0 | result != ISC_R_FILENOTFOUND) |
1270 | 0 | syslog(LOG_ERR, |
1271 | 0 | "unable to remove log file '%s.%d': %s", |
1272 | 0 | path, greatest, |
1273 | 0 | isc_result_totext(result)); |
1274 | 0 | } |
1275 | |
|
1276 | 0 | for (i = greatest; i > 0; i--) { |
1277 | 0 | result = ISC_R_SUCCESS; |
1278 | 0 | n = snprintf(current, sizeof(current), "%s.%d", path, i - 1); |
1279 | 0 | if (n >= (int)sizeof(current) || n < 0) |
1280 | 0 | result = ISC_R_NOSPACE; |
1281 | 0 | if (result == ISC_R_SUCCESS) { |
1282 | 0 | n = snprintf(new, sizeof(new), "%s.%d", path, i); |
1283 | 0 | if (n >= (int)sizeof(new) || n < 0) |
1284 | 0 | result = ISC_R_NOSPACE; |
1285 | 0 | } |
1286 | 0 | if (result == ISC_R_SUCCESS) |
1287 | 0 | result = isc_file_rename(current, new); |
1288 | 0 | if (result != ISC_R_SUCCESS && |
1289 | 0 | result != ISC_R_FILENOTFOUND) |
1290 | 0 | syslog(LOG_ERR, |
1291 | 0 | "unable to rename log file '%s.%d' to " |
1292 | 0 | "'%s.%d': %s", path, i - 1, path, i, |
1293 | 0 | isc_result_totext(result)); |
1294 | 0 | } |
1295 | |
|
1296 | 0 | if (FILE_VERSIONS(channel) != 0) { |
1297 | 0 | n = snprintf(new, sizeof(new), "%s.0", path); |
1298 | 0 | if (n >= (int)sizeof(new) || n < 0) |
1299 | 0 | result = ISC_R_NOSPACE; |
1300 | 0 | else |
1301 | 0 | result = isc_file_rename(path, new); |
1302 | 0 | if (result != ISC_R_SUCCESS && |
1303 | 0 | result != ISC_R_FILENOTFOUND) |
1304 | 0 | syslog(LOG_ERR, |
1305 | 0 | "unable to rename log file '%s' to '%s.0': %s", |
1306 | 0 | path, path, isc_result_totext(result)); |
1307 | 0 | } else { |
1308 | 0 | result = isc_file_remove(path); |
1309 | 0 | if (result != ISC_R_SUCCESS && |
1310 | 0 | result != ISC_R_FILENOTFOUND) |
1311 | 0 | syslog(LOG_ERR, "unable to remove log file '%s': %s", |
1312 | 0 | path, isc_result_totext(result)); |
1313 | 0 | } |
1314 | |
|
1315 | 0 | return (ISC_R_SUCCESS); |
1316 | 0 | } |
1317 | | |
1318 | | static isc_result_t |
1319 | 0 | isc_log_open(isc_logchannel_t *channel) { |
1320 | 0 | struct stat statbuf; |
1321 | 0 | isc_boolean_t regular_file; |
1322 | 0 | isc_boolean_t roll = ISC_FALSE; |
1323 | 0 | isc_result_t result = ISC_R_SUCCESS; |
1324 | 0 | const char *path; |
1325 | |
|
1326 | 0 | REQUIRE(channel->type == ISC_LOG_TOFILE); |
1327 | 0 | REQUIRE(FILE_STREAM(channel) == NULL); |
1328 | | |
1329 | 0 | path = FILE_NAME(channel); |
1330 | |
|
1331 | 0 | REQUIRE(path != NULL && *path != '\0'); |
1332 | | |
1333 | | /* |
1334 | | * Determine type of file; only regular files will be |
1335 | | * version renamed, and only if the base file exists |
1336 | | * and either has no size limit or has reached its size limit. |
1337 | | */ |
1338 | 0 | if (stat(path, &statbuf) == 0) { |
1339 | 0 | regular_file = S_ISREG(statbuf.st_mode) ? ISC_TRUE : ISC_FALSE; |
1340 | | /* XXXDCL if not regular_file complain? */ |
1341 | 0 | if ((FILE_MAXSIZE(channel) == 0 && |
1342 | 0 | FILE_VERSIONS(channel) != ISC_LOG_ROLLNEVER) || |
1343 | 0 | (FILE_MAXSIZE(channel) > 0 && |
1344 | 0 | statbuf.st_size >= FILE_MAXSIZE(channel))) |
1345 | 0 | roll = regular_file; |
1346 | 0 | } else if (errno == ENOENT) { |
1347 | 0 | regular_file = ISC_TRUE; |
1348 | 0 | POST(regular_file); |
1349 | 0 | } else |
1350 | 0 | result = ISC_R_INVALIDFILE; |
1351 | | |
1352 | | /* |
1353 | | * Version control. |
1354 | | */ |
1355 | 0 | if (result == ISC_R_SUCCESS && roll) { |
1356 | 0 | if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER) |
1357 | 0 | return (ISC_R_MAXSIZE); |
1358 | 0 | result = roll_log(channel); |
1359 | 0 | if (result != ISC_R_SUCCESS) { |
1360 | 0 | if ((channel->flags & ISC_LOG_OPENERR) == 0) { |
1361 | 0 | syslog(LOG_ERR, |
1362 | 0 | "isc_log_open: roll_log '%s' " |
1363 | 0 | "failed: %s", |
1364 | 0 | FILE_NAME(channel), |
1365 | 0 | isc_result_totext(result)); |
1366 | 0 | channel->flags |= ISC_LOG_OPENERR; |
1367 | 0 | } |
1368 | 0 | return (result); |
1369 | 0 | } |
1370 | 0 | } |
1371 | | |
1372 | 0 | result = isc_stdio_open(path, "a", &FILE_STREAM(channel)); |
1373 | |
|
1374 | 0 | return (result); |
1375 | 0 | } |
1376 | | |
1377 | | isc_boolean_t |
1378 | 0 | isc_log_wouldlog(isc_log_t *lctx, int level) { |
1379 | | /* |
1380 | | * Try to avoid locking the mutex for messages which can't |
1381 | | * possibly be logged to any channels -- primarily debugging |
1382 | | * messages that the debug level is not high enough to print. |
1383 | | * |
1384 | | * If the level is (mathematically) less than or equal to the |
1385 | | * highest_level, or if there is a dynamic channel and the level is |
1386 | | * less than or equal to the debug level, the main loop must be |
1387 | | * entered to see if the message should really be output. |
1388 | | * |
1389 | | * NOTE: this is UNLOCKED access to the logconfig. However, |
1390 | | * the worst thing that can happen is that a bad decision is made |
1391 | | * about returning without logging, and that's not a big concern, |
1392 | | * because that's a risk anyway if the logconfig is being |
1393 | | * dynamically changed. |
1394 | | */ |
1395 | |
|
1396 | 0 | if (lctx == NULL || lctx->logconfig == NULL) |
1397 | 0 | return (ISC_FALSE); |
1398 | | |
1399 | 0 | return (ISC_TF(level <= lctx->logconfig->highest_level || |
1400 | 0 | (lctx->logconfig->dynamic && |
1401 | 0 | level <= lctx->debug_level))); |
1402 | 0 | } |
1403 | | |
1404 | | static void |
1405 | | isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category, |
1406 | | isc_logmodule_t *module, int level, isc_boolean_t write_once, |
1407 | | isc_msgcat_t *msgcat, int msgset, int msg, |
1408 | | const char *format, va_list args) |
1409 | 0 | { |
1410 | 0 | int syslog_level; |
1411 | 0 | char time_string[64]; |
1412 | 0 | char level_string[24]; |
1413 | 0 | size_t octets; |
1414 | 0 | const char *iformat; |
1415 | 0 | struct stat statbuf; |
1416 | 0 | isc_boolean_t matched = ISC_FALSE; |
1417 | 0 | isc_boolean_t printtime, printtag; |
1418 | 0 | isc_boolean_t printcategory, printmodule, printlevel; |
1419 | 0 | isc_logconfig_t *lcfg; |
1420 | 0 | isc_logchannel_t *channel; |
1421 | 0 | isc_logchannellist_t *category_channels; |
1422 | 0 | isc_result_t result; |
1423 | |
|
1424 | 0 | REQUIRE(lctx == NULL || VALID_CONTEXT(lctx)); |
1425 | 0 | REQUIRE(category != NULL); |
1426 | 0 | REQUIRE(module != NULL); |
1427 | 0 | REQUIRE(level != ISC_LOG_DYNAMIC); |
1428 | 0 | REQUIRE(format != NULL); |
1429 | | |
1430 | | /* |
1431 | | * Programs can use libraries that use this logging code without |
1432 | | * wanting to do any logging, thus the log context is allowed to |
1433 | | * be non-existent. |
1434 | | */ |
1435 | 0 | if (lctx == NULL) |
1436 | 0 | return; |
1437 | | |
1438 | 0 | REQUIRE(category->id < lctx->category_count); |
1439 | 0 | REQUIRE(module->id < lctx->module_count); |
1440 | | |
1441 | 0 | if (! isc_log_wouldlog(lctx, level)) |
1442 | 0 | return; |
1443 | | |
1444 | 0 | if (msgcat != NULL) |
1445 | 0 | iformat = isc_msgcat_get(msgcat, msgset, msg, format); |
1446 | 0 | else |
1447 | 0 | iformat = format; |
1448 | |
|
1449 | 0 | time_string[0] = '\0'; |
1450 | 0 | level_string[0] = '\0'; |
1451 | |
|
1452 | 0 | LOCK(&lctx->lock); |
1453 | |
|
1454 | 0 | lctx->buffer[0] = '\0'; |
1455 | |
|
1456 | 0 | lcfg = lctx->logconfig; |
1457 | |
|
1458 | 0 | category_channels = ISC_LIST_HEAD(lcfg->channellists[category->id]); |
1459 | | |
1460 | | /* |
1461 | | * XXXDCL add duplicate filtering? (To not write multiple times to |
1462 | | * the same source via various channels). |
1463 | | */ |
1464 | 0 | do { |
1465 | | /* |
1466 | | * If the channel list end was reached and a match was made, |
1467 | | * everything is finished. |
1468 | | */ |
1469 | 0 | if (category_channels == NULL && matched) |
1470 | 0 | break; |
1471 | | |
1472 | 0 | if (category_channels == NULL && ! matched && |
1473 | 0 | category_channels != ISC_LIST_HEAD(lcfg->channellists[0])) |
1474 | | /* |
1475 | | * No category/module pair was explicitly configured. |
1476 | | * Try the category named "default". |
1477 | | */ |
1478 | 0 | category_channels = |
1479 | 0 | ISC_LIST_HEAD(lcfg->channellists[0]); |
1480 | |
|
1481 | 0 | if (category_channels == NULL && ! matched) |
1482 | | /* |
1483 | | * No matching module was explicitly configured |
1484 | | * for the category named "default". Use the internal |
1485 | | * default channel. |
1486 | | */ |
1487 | 0 | category_channels = &default_channel; |
1488 | |
|
1489 | 0 | if (category_channels->module != NULL && |
1490 | 0 | category_channels->module != module) { |
1491 | 0 | category_channels = ISC_LIST_NEXT(category_channels, |
1492 | 0 | link); |
1493 | 0 | continue; |
1494 | 0 | } |
1495 | | |
1496 | 0 | matched = ISC_TRUE; |
1497 | |
|
1498 | 0 | channel = category_channels->channel; |
1499 | 0 | category_channels = ISC_LIST_NEXT(category_channels, link); |
1500 | |
|
1501 | 0 | if (((channel->flags & ISC_LOG_DEBUGONLY) != 0) && |
1502 | 0 | lctx->debug_level == 0) |
1503 | 0 | continue; |
1504 | | |
1505 | 0 | if (channel->level == ISC_LOG_DYNAMIC) { |
1506 | 0 | if (lctx->debug_level < level) |
1507 | 0 | continue; |
1508 | 0 | } else if (channel->level < level) |
1509 | 0 | continue; |
1510 | | |
1511 | 0 | if ((channel->flags & ISC_LOG_PRINTTIME) != 0 && |
1512 | 0 | time_string[0] == '\0') { |
1513 | 0 | isc_time_t isctime; |
1514 | |
|
1515 | 0 | TIME_NOW(&isctime); |
1516 | 0 | isc_time_formattimestamp(&isctime, time_string, |
1517 | 0 | sizeof(time_string)); |
1518 | 0 | } |
1519 | |
|
1520 | 0 | if ((channel->flags & ISC_LOG_PRINTLEVEL) != 0 && |
1521 | 0 | level_string[0] == '\0') { |
1522 | 0 | if (level < ISC_LOG_CRITICAL) |
1523 | 0 | snprintf(level_string, sizeof(level_string), |
1524 | 0 | "%s %d: ", |
1525 | 0 | isc_msgcat_get(isc_msgcat, |
1526 | 0 | ISC_MSGSET_LOG, |
1527 | 0 | ISC_MSG_LEVEL, |
1528 | 0 | "level"), |
1529 | 0 | level); |
1530 | 0 | else if (level > ISC_LOG_DYNAMIC) |
1531 | 0 | snprintf(level_string, sizeof(level_string), |
1532 | 0 | "%s %d: ", log_level_strings[0], |
1533 | 0 | level); |
1534 | 0 | else |
1535 | 0 | snprintf(level_string, sizeof(level_string), |
1536 | 0 | "%s: ", log_level_strings[-level]); |
1537 | 0 | } |
1538 | | |
1539 | | /* |
1540 | | * Only format the message once. |
1541 | | */ |
1542 | 0 | if (lctx->buffer[0] == '\0') { |
1543 | 0 | (void)vsnprintf(lctx->buffer, sizeof(lctx->buffer), |
1544 | 0 | iformat, args); |
1545 | | |
1546 | | /* |
1547 | | * Check for duplicates. |
1548 | | */ |
1549 | 0 | if (write_once) { |
1550 | 0 | isc_logmessage_t *message, *new; |
1551 | 0 | isc_time_t oldest; |
1552 | 0 | isc_interval_t interval; |
1553 | |
|
1554 | 0 | isc_interval_set(&interval, |
1555 | 0 | lcfg->duplicate_interval, 0); |
1556 | | |
1557 | | /* |
1558 | | * 'oldest' is the age of the oldest messages |
1559 | | * which fall within the duplicate_interval |
1560 | | * range. |
1561 | | */ |
1562 | 0 | TIME_NOW(&oldest); |
1563 | 0 | if (isc_time_subtract(&oldest, &interval, &oldest) |
1564 | 0 | != ISC_R_SUCCESS) |
1565 | | /* |
1566 | | * Can't effectively do the checking |
1567 | | * without having a valid time. |
1568 | | */ |
1569 | 0 | message = NULL; |
1570 | 0 | else |
1571 | 0 | message =ISC_LIST_HEAD(lctx->messages); |
1572 | |
|
1573 | 0 | while (message != NULL) { |
1574 | 0 | if (isc_time_compare(&message->time, |
1575 | 0 | &oldest) < 0) { |
1576 | | /* |
1577 | | * This message is older |
1578 | | * than the duplicate_interval, |
1579 | | * so it should be dropped from |
1580 | | * the history. |
1581 | | * |
1582 | | * Setting the interval to be |
1583 | | * to be longer will obviously |
1584 | | * not cause the expired |
1585 | | * message to spring back into |
1586 | | * existence. |
1587 | | */ |
1588 | 0 | new = ISC_LIST_NEXT(message, |
1589 | 0 | link); |
1590 | |
|
1591 | 0 | ISC_LIST_UNLINK(lctx->messages, |
1592 | 0 | message, link); |
1593 | | |
1594 | 0 | isc_mem_put(lctx->mctx, |
1595 | 0 | message, |
1596 | 0 | sizeof(*message) + 1 + |
1597 | 0 | strlen(message->text)); |
1598 | |
|
1599 | 0 | message = new; |
1600 | 0 | continue; |
1601 | 0 | } |
1602 | | |
1603 | | /* |
1604 | | * This message is in the duplicate |
1605 | | * filtering interval ... |
1606 | | */ |
1607 | 0 | if (strcmp(lctx->buffer, message->text) |
1608 | 0 | == 0) { |
1609 | | /* |
1610 | | * ... and it is a duplicate. |
1611 | | * Unlock the mutex and |
1612 | | * get the hell out of Dodge. |
1613 | | */ |
1614 | 0 | UNLOCK(&lctx->lock); |
1615 | 0 | return; |
1616 | 0 | } |
1617 | | |
1618 | 0 | message = ISC_LIST_NEXT(message, link); |
1619 | 0 | } |
1620 | | |
1621 | | /* |
1622 | | * It wasn't in the duplicate interval, |
1623 | | * so add it to the message list. |
1624 | | */ |
1625 | 0 | octets = strlen(lctx->buffer) + 1; |
1626 | 0 | new = isc_mem_get(lctx->mctx, |
1627 | 0 | sizeof(isc_logmessage_t) + |
1628 | 0 | octets); |
1629 | 0 | if (new != NULL) { |
1630 | | /* |
1631 | | * Put the text immediately after |
1632 | | * the struct. The strcpy is safe. |
1633 | | */ |
1634 | 0 | new->text = (char *)(new + 1); |
1635 | 0 | strlcpy(new->text, lctx->buffer, octets); |
1636 | |
|
1637 | 0 | TIME_NOW(&new->time); |
1638 | |
|
1639 | 0 | ISC_LIST_APPEND(lctx->messages, |
1640 | 0 | new, link); |
1641 | 0 | } |
1642 | 0 | } |
1643 | 0 | } |
1644 | | |
1645 | 0 | printtime = ISC_TF((channel->flags & ISC_LOG_PRINTTIME) |
1646 | 0 | != 0); |
1647 | 0 | printtag = ISC_TF((channel->flags & ISC_LOG_PRINTTAG) |
1648 | 0 | != 0 && lcfg->tag != NULL); |
1649 | 0 | printcategory = ISC_TF((channel->flags & ISC_LOG_PRINTCATEGORY) |
1650 | 0 | != 0); |
1651 | 0 | printmodule = ISC_TF((channel->flags & ISC_LOG_PRINTMODULE) |
1652 | 0 | != 0); |
1653 | 0 | printlevel = ISC_TF((channel->flags & ISC_LOG_PRINTLEVEL) |
1654 | 0 | != 0); |
1655 | |
|
1656 | 0 | switch (channel->type) { |
1657 | 0 | case ISC_LOG_TOFILE: |
1658 | 0 | if (FILE_MAXREACHED(channel)) { |
1659 | | /* |
1660 | | * If the file can be rolled, OR |
1661 | | * If the file no longer exists, OR |
1662 | | * If the file is less than the maximum size, |
1663 | | * (such as if it had been renamed and |
1664 | | * a new one touched, or it was truncated |
1665 | | * in place) |
1666 | | * ... then close it to trigger reopening. |
1667 | | */ |
1668 | 0 | if (FILE_VERSIONS(channel) != |
1669 | 0 | ISC_LOG_ROLLNEVER || |
1670 | 0 | (stat(FILE_NAME(channel), &statbuf) != 0 && |
1671 | 0 | errno == ENOENT) || |
1672 | 0 | statbuf.st_size < FILE_MAXSIZE(channel)) { |
1673 | 0 | (void)fclose(FILE_STREAM(channel)); |
1674 | 0 | FILE_STREAM(channel) = NULL; |
1675 | 0 | FILE_MAXREACHED(channel) = ISC_FALSE; |
1676 | 0 | } else |
1677 | | /* |
1678 | | * Eh, skip it. |
1679 | | */ |
1680 | 0 | break; |
1681 | 0 | } |
1682 | | |
1683 | 0 | if (FILE_STREAM(channel) == NULL) { |
1684 | 0 | result = isc_log_open(channel); |
1685 | 0 | if (result != ISC_R_SUCCESS && |
1686 | 0 | result != ISC_R_MAXSIZE && |
1687 | 0 | (channel->flags & ISC_LOG_OPENERR) == 0) { |
1688 | 0 | syslog(LOG_ERR, |
1689 | 0 | "isc_log_open '%s' failed: %s", |
1690 | 0 | FILE_NAME(channel), |
1691 | 0 | isc_result_totext(result)); |
1692 | 0 | channel->flags |= ISC_LOG_OPENERR; |
1693 | 0 | } |
1694 | 0 | if (result != ISC_R_SUCCESS) |
1695 | 0 | break; |
1696 | 0 | channel->flags &= ~ISC_LOG_OPENERR; |
1697 | 0 | } |
1698 | | /* FALLTHROUGH */ |
1699 | | |
1700 | 0 | case ISC_LOG_TOFILEDESC: |
1701 | 0 | fprintf(FILE_STREAM(channel), "%s%s%s%s%s%s%s%s%s%s\n", |
1702 | 0 | printtime ? time_string : "", |
1703 | 0 | printtime ? " " : "", |
1704 | 0 | printtag ? lcfg->tag : "", |
1705 | 0 | printtag ? ": " : "", |
1706 | 0 | printcategory ? category->name : "", |
1707 | 0 | printcategory ? ": " : "", |
1708 | 0 | printmodule ? (module != NULL ? module->name |
1709 | 0 | : "no_module") |
1710 | 0 | : "", |
1711 | 0 | printmodule ? ": " : "", |
1712 | 0 | printlevel ? level_string : "", |
1713 | 0 | lctx->buffer); |
1714 | |
|
1715 | 0 | fflush(FILE_STREAM(channel)); |
1716 | | |
1717 | | /* |
1718 | | * If the file now exceeds its maximum size |
1719 | | * threshold, note it so that it will not be logged |
1720 | | * to any more. |
1721 | | */ |
1722 | 0 | if (FILE_MAXSIZE(channel) > 0) { |
1723 | 0 | INSIST(channel->type == ISC_LOG_TOFILE); |
1724 | | |
1725 | | /* XXXDCL NT fstat/fileno */ |
1726 | | /* XXXDCL complain if fstat fails? */ |
1727 | 0 | if (fstat(fileno(FILE_STREAM(channel)), |
1728 | 0 | &statbuf) >= 0 && |
1729 | 0 | statbuf.st_size > FILE_MAXSIZE(channel)) |
1730 | 0 | FILE_MAXREACHED(channel) = ISC_TRUE; |
1731 | 0 | } |
1732 | | |
1733 | 0 | break; |
1734 | | |
1735 | 0 | case ISC_LOG_TOSYSLOG: |
1736 | 0 | if (level > 0) |
1737 | 0 | syslog_level = LOG_DEBUG; |
1738 | 0 | else if (level < ISC_LOG_CRITICAL) |
1739 | 0 | syslog_level = LOG_CRIT; |
1740 | 0 | else |
1741 | 0 | syslog_level = syslog_map[-level]; |
1742 | |
|
1743 | 0 | (void)syslog(FACILITY(channel) | syslog_level, |
1744 | 0 | "%s%s%s%s%s%s%s%s%s%s", |
1745 | 0 | printtime ? time_string : "", |
1746 | 0 | printtime ? " " : "", |
1747 | 0 | printtag ? lcfg->tag : "", |
1748 | 0 | printtag ? ": " : "", |
1749 | 0 | printcategory ? category->name : "", |
1750 | 0 | printcategory ? ": " : "", |
1751 | 0 | printmodule ? (module != NULL ? module->name |
1752 | 0 | : "no_module") |
1753 | 0 | : "", |
1754 | 0 | printmodule ? ": " : "", |
1755 | 0 | printlevel ? level_string : "", |
1756 | 0 | lctx->buffer); |
1757 | 0 | break; |
1758 | | |
1759 | 0 | case ISC_LOG_TONULL: |
1760 | 0 | break; |
1761 | |
|
1762 | 0 | } |
1763 | |
|
1764 | 0 | } while (1); |
1765 | | |
1766 | 0 | UNLOCK(&lctx->lock); |
1767 | 0 | } |