Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) Internet Systems Consortium, Inc. ("ISC") |
3 | | * |
4 | | * SPDX-License-Identifier: MPL-2.0 |
5 | | * |
6 | | * This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, you can obtain one at https://mozilla.org/MPL/2.0/. |
9 | | * |
10 | | * See the COPYRIGHT file distributed with this work for additional |
11 | | * information regarding copyright ownership. |
12 | | */ |
13 | | |
14 | | /*! \file */ |
15 | | |
16 | | #include <errno.h> |
17 | | #include <inttypes.h> |
18 | | #include <limits.h> |
19 | | #include <stdbool.h> |
20 | | #include <stdlib.h> |
21 | | #include <sys/stat.h> |
22 | | #include <sys/types.h> /* dev_t FreeBSD 2.1 */ |
23 | | #include <time.h> |
24 | | #include <unistd.h> |
25 | | |
26 | | #include <isc/atomic.h> |
27 | | #include <isc/dir.h> |
28 | | #include <isc/errno.h> |
29 | | #include <isc/file.h> |
30 | | #include <isc/log.h> |
31 | | #include <isc/magic.h> |
32 | | #include <isc/mem.h> |
33 | | #include <isc/stdio.h> |
34 | | #include <isc/string.h> |
35 | | #include <isc/thread.h> |
36 | | #include <isc/time.h> |
37 | | #include <isc/urcu.h> |
38 | | #include <isc/util.h> |
39 | | |
40 | 2 | #define LCTX_MAGIC ISC_MAGIC('L', 'c', 't', 'x') |
41 | | #define VALID_CONTEXT(lctx) ISC_MAGIC_VALID(lctx, LCTX_MAGIC) |
42 | | |
43 | 2 | #define LCFG_MAGIC ISC_MAGIC('L', 'c', 'f', 'g') |
44 | | #define VALID_CONFIG(lcfg) ISC_MAGIC_VALID(lcfg, LCFG_MAGIC) |
45 | | |
46 | | static thread_local bool forcelog = false; |
47 | | |
48 | | /* |
49 | | * XXXDCL make dynamic? |
50 | | */ |
51 | | #define LOG_BUFFER_SIZE (8 * 1024) |
52 | | |
53 | | /* |
54 | | * Private isc_log_t data type. |
55 | | */ |
56 | | typedef struct isc_log isc_log_t; |
57 | | |
58 | | /*! |
59 | | * This is the structure that holds each named channel. A simple linked |
60 | | * list chains all of the channels together, so an individual channel is |
61 | | * found by doing strcmp()s with the names down the list. Their should |
62 | | * be no performance penalty from this as it is expected that the number |
63 | | * of named channels will be no more than a dozen or so, and name lookups |
64 | | * from the head of the list are only done when isc_log_usechannel() is |
65 | | * called, which should also be very infrequent. |
66 | | */ |
67 | | typedef struct isc_logchannel isc_logchannel_t; |
68 | | |
69 | | struct isc_logchannel { |
70 | | char *name; |
71 | | unsigned int type; |
72 | | int level; |
73 | | unsigned int flags; |
74 | | isc_logdestination_t destination; |
75 | | ISC_LINK(isc_logchannel_t) link; |
76 | | }; |
77 | | |
78 | | /*! |
79 | | * The logchannellist structure associates categories and modules with |
80 | | * channels. First the appropriate channellist is found based on the |
81 | | * category, and then each structure in the linked list is checked for |
82 | | * a matching module. It is expected that the number of channels |
83 | | * associated with any given category will be very short, no more than |
84 | | * three or four in the more unusual cases. |
85 | | */ |
86 | | typedef struct isc_logchannellist isc_logchannellist_t; |
87 | | |
88 | | struct isc_logchannellist { |
89 | | isc_logmodule_t module; |
90 | | isc_logchannel_t *channel; |
91 | | ISC_LINK(isc_logchannellist_t) link; |
92 | | }; |
93 | | |
94 | | /*! |
95 | | * This structure is used to remember messages for pruning via |
96 | | * isc_log_[v]write1(). |
97 | | */ |
98 | | typedef struct isc_logmessage isc_logmessage_t; |
99 | | |
100 | | struct isc_logmessage { |
101 | | char *text; |
102 | | isc_time_t time; |
103 | | ISC_LINK(isc_logmessage_t) link; |
104 | | }; |
105 | | |
106 | | /*! |
107 | | * The isc_logconfig structure is used to store the configurable information |
108 | | * about where messages are actually supposed to be sent -- the information |
109 | | * that could changed based on some configuration file, as opposed to the |
110 | | * the category/module specification of isc_log_[v]write[1] that is compiled |
111 | | * into a program, or the debug_level which is dynamic state information. |
112 | | */ |
113 | | struct isc_logconfig { |
114 | | unsigned int magic; |
115 | | isc_log_t *lctx; |
116 | | ISC_LIST(isc_logchannel_t) channels; |
117 | | ISC_LIST(isc_logchannellist_t) channellists[ISC_LOGCATEGORY_MAX]; |
118 | | int_fast32_t highest_level; |
119 | | char *tag; |
120 | | bool dynamic; |
121 | | }; |
122 | | |
123 | | /*! |
124 | | * This isc_log structure provides the context for the isc_log functions. |
125 | | * The log context locks itself in isc_log_doit, the internal backend to |
126 | | * isc_log_write. The locking is necessary both to provide exclusive access |
127 | | * to the buffer into which the message is formatted and to guard against |
128 | | * competing threads trying to write to the same syslog resource. |
129 | | * |
130 | | * FIXME: We can remove the locking by using per-thread .buffer. |
131 | | */ |
132 | | struct isc_log { |
133 | | /* Not locked. */ |
134 | | unsigned int magic; |
135 | | isc_mem_t *mctx; |
136 | | atomic_int_fast32_t debug_level; |
137 | | /* RCU-protected pointer */ |
138 | | isc_logconfig_t *logconfig; |
139 | | isc_mutex_t lock; |
140 | | /* Locked by isc_log lock. */ |
141 | | char buffer[LOG_BUFFER_SIZE]; |
142 | | atomic_bool dynamic; |
143 | | atomic_int_fast32_t highest_level; |
144 | | }; |
145 | | |
146 | | /*! |
147 | | * Used when ISC_LOG_PRINTLEVEL is enabled for a channel. |
148 | | */ |
149 | | static const char *log_level_strings[] = { "debug", "info", "notice", |
150 | | "warning", "error", "critical" }; |
151 | | |
152 | | /*! |
153 | | * Used to convert ISC_LOG_* priorities into syslog priorities. |
154 | | * XXXDCL This will need modification for NT. |
155 | | */ |
156 | | static const int syslog_map[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, |
157 | | LOG_WARNING, LOG_ERR, LOG_CRIT }; |
158 | | |
159 | | /*! |
160 | | * When adding new categories, a corresponding ISC_LOGCATEGORY_foo |
161 | | * definition needs to be added to <isc/log.h>. |
162 | | * |
163 | | * The default category is provided so that the internal default can |
164 | | * be overridden. Since the default is always looked up as the first |
165 | | * channellist in the log context, it must come first in isc_categories[]. |
166 | | */ |
167 | | static const char *categories_description[] = { |
168 | | /* libisc categories */ |
169 | | [ISC_LOGCATEGORY_DEFAULT] = "default", |
170 | | [ISC_LOGCATEGORY_GENERAL] = "general", |
171 | | [ISC_LOGCATEGORY_SSLKEYLOG] = "sslkeylog", |
172 | | /* dns categories */ |
173 | | [DNS_LOGCATEGORY_CNAME] = "cname", |
174 | | [DNS_LOGCATEGORY_DATABASE] = "database", |
175 | | [DNS_LOGCATEGORY_DISPATCH] = "dispatch", |
176 | | [DNS_LOGCATEGORY_DNSSEC] = "dnssec", |
177 | | [DNS_LOGCATEGORY_DNSTAP] = "dnstap", |
178 | | [DNS_LOGCATEGORY_EDNS_DISABLED] = "edns-disabled", |
179 | | [DNS_LOGCATEGORY_LAME_SERVERS] = "lame-servers", |
180 | | [DNS_LOGCATEGORY_NOTIFY] = "notify", |
181 | | [DNS_LOGCATEGORY_NSID] = "nsid", |
182 | | [DNS_LOGCATEGORY_RESOLVER] = "resolver", |
183 | | [DNS_LOGCATEGORY_RPZ] = "rpz", |
184 | | [DNS_LOGCATEGORY_RPZ_PASSTHRU] = "rpz-passthru", |
185 | | [DNS_LOGCATEGORY_RRL] = "rate-limit", |
186 | | [DNS_LOGCATEGORY_SECURITY] = "security", |
187 | | [DNS_LOGCATEGORY_SPILL] = "spill", |
188 | | [DNS_LOGCATEGORY_UPDATE_POLICY] = "update-policy", |
189 | | [DNS_LOGCATEGORY_XFER_IN] = "xfer-in", |
190 | | [DNS_LOGCATEGORY_XFER_OUT] = "xfer-out", |
191 | | [DNS_LOGCATEGORY_ZONELOAD] = "zoneload", |
192 | | [DNS_LOGCATEGORY_ZONEVERSION] = "zoneversion", |
193 | | /* ns categories */ |
194 | | [NS_LOGCATEGORY_CLIENT] = "client", |
195 | | [NS_LOGCATEGORY_DRA] = "dns-reporting-agent", |
196 | | [NS_LOGCATEGORY_NETWORK] = "network", |
197 | | [NS_LOGCATEGORY_QUERIES] = "queries", |
198 | | [NS_LOGCATEGORY_QUERY_ERRORS] = "query-errors", |
199 | | [NS_LOGCATEGORY_RESPONSES] = "responses", |
200 | | [NS_LOGCATEGORY_SERVE_STALE] = "serve-stale", |
201 | | [NS_LOGCATEGORY_TAT] = "trust-anchor-telemetry", |
202 | | [NS_LOGCATEGORY_UPDATE] = "update", |
203 | | [NS_LOGCATEGORY_UPDATE_SECURITY] = "update-security", |
204 | | /* cfg categories */ |
205 | | [CFG_LOGCATEGORY_CONFIG] = "config", |
206 | | /* named categories */ |
207 | | [NAMED_LOGCATEGORY_UNMATCHED] = "unmatched", |
208 | | /* delv categories */ |
209 | | [DELV_LOGCATEGORY_DEFAULT] = "delv", |
210 | | }; |
211 | | |
212 | | /*! |
213 | | * See above comment for categories, and apply it to modules. |
214 | | */ |
215 | | static const char *modules_description[] = { |
216 | | /* isc modules */ |
217 | | [ISC_LOGMODULE_DEFAULT] = "no_module", |
218 | | [ISC_LOGMODULE_SOCKET] = "socket", |
219 | | [ISC_LOGMODULE_TIME] = "time", |
220 | | [ISC_LOGMODULE_INTERFACE] = "interface", |
221 | | [ISC_LOGMODULE_TIMER] = "timer", |
222 | | [ISC_LOGMODULE_FILE] = "file", |
223 | | [ISC_LOGMODULE_NETMGR] = "netmgr", |
224 | | [ISC_LOGMODULE_OTHER] = "other", |
225 | | [ISC_LOGMODULE_CRYPTO] = "crypto", |
226 | | /* dns modules */ |
227 | | [DNS_LOGMODULE_DB] = "dns/db", |
228 | | [DNS_LOGMODULE_RDATA] = "dns/rdata", |
229 | | [DNS_LOGMODULE_MASTER] = "dns/master", |
230 | | [DNS_LOGMODULE_MESSAGE] = "dns/message", |
231 | | [DNS_LOGMODULE_CACHE] = "dns/cache", |
232 | | [DNS_LOGMODULE_CONFIG] = "dns/config", |
233 | | [DNS_LOGMODULE_RESOLVER] = "dns/resolver", |
234 | | [DNS_LOGMODULE_ZONE] = "dns/zone", |
235 | | [DNS_LOGMODULE_JOURNAL] = "dns/journal", |
236 | | [DNS_LOGMODULE_ADB] = "dns/adb", |
237 | | [DNS_LOGMODULE_XFER_IN] = "dns/xfrin", |
238 | | [DNS_LOGMODULE_XFER_OUT] = "dns/xfrout", |
239 | | [DNS_LOGMODULE_ACL] = "dns/acl", |
240 | | [DNS_LOGMODULE_VALIDATOR] = "dns/validator", |
241 | | [DNS_LOGMODULE_DISPATCH] = "dns/dispatch", |
242 | | [DNS_LOGMODULE_REQUEST] = "dns/request", |
243 | | [DNS_LOGMODULE_MASTERDUMP] = "dns/masterdump", |
244 | | [DNS_LOGMODULE_TSIG] = "dns/tsig", |
245 | | [DNS_LOGMODULE_TKEY] = "dns/tkey", |
246 | | [DNS_LOGMODULE_SDB] = "dns/sdb", |
247 | | [DNS_LOGMODULE_DIFF] = "dns/diff", |
248 | | [DNS_LOGMODULE_HINTS] = "dns/hints", |
249 | | [DNS_LOGMODULE_UNUSED1] = "dns/unused1", |
250 | | [DNS_LOGMODULE_DLZ] = "dns/dlz", |
251 | | [DNS_LOGMODULE_DNSSEC] = "dns/dnssec", |
252 | | [DNS_LOGMODULE_CRYPTO] = "dns/crypto", |
253 | | [DNS_LOGMODULE_PACKETS] = "dns/packets", |
254 | | [DNS_LOGMODULE_NTA] = "dns/nta", |
255 | | [DNS_LOGMODULE_DYNDB] = "dns/dyndb", |
256 | | [DNS_LOGMODULE_DNSTAP] = "dns/dnstap", |
257 | | [DNS_LOGMODULE_SSU] = "dns/ssu", |
258 | | [DNS_LOGMODULE_QP] = "dns/qp", |
259 | | [DNS_LOGMODULE_RPZ] = "dns/rpz", |
260 | | [DNS_LOGMODULE_CATZ] = "dns/catz", |
261 | | /* ns modules */ |
262 | | [NS_LOGMODULE_CLIENT] = "ns/client", |
263 | | [NS_LOGMODULE_QUERY] = "ns/query", |
264 | | [NS_LOGMODULE_INTERFACEMGR] = "ns/interfacemgr", |
265 | | [NS_LOGMODULE_UPDATE] = "ns/update", |
266 | | [NS_LOGMODULE_XFER_IN] = "ns/xfer-in", |
267 | | [NS_LOGMODULE_XFER_OUT] = "ns/xfer-out", |
268 | | [NS_LOGMODULE_NOTIFY] = "ns/notify", |
269 | | [NS_LOGMODULE_HOOKS] = "ns/hooks", |
270 | | /* cfg modules */ |
271 | | [CFG_LOGMODULE_PARSER] = "isccfg/parser", |
272 | | /* named modules */ |
273 | | [NAMED_LOGMODULE_MAIN] = "main", |
274 | | [NAMED_LOGMODULE_SERVER] = "server", |
275 | | [NAMED_LOGMODULE_CONTROL] = "control", |
276 | | /* delv modules */ |
277 | | [DELV_LOGMODULE_DEFAULT] = "delv", |
278 | | }; |
279 | | |
280 | | /*! |
281 | | * This essentially constant structure must be filled in at run time, |
282 | | * because its channel member is pointed to a channel that is created |
283 | | * dynamically with isc_log_createchannel. |
284 | | */ |
285 | | static isc_logchannellist_t default_channel; |
286 | | |
287 | | /*! |
288 | | * libisc logs to this context. |
289 | | */ |
290 | | static isc_log_t *isc__lctx = NULL; |
291 | | |
292 | | /*! |
293 | | * Forward declarations. |
294 | | */ |
295 | | static void |
296 | | assignchannel(isc_logconfig_t *lcfg, const isc_logcategory_t category, |
297 | | const isc_logmodule_t module, isc_logchannel_t *channel); |
298 | | |
299 | | static void |
300 | | sync_highest_level(isc_logconfig_t *lcfg); |
301 | | |
302 | | static isc_result_t |
303 | | greatest_version(isc_logfile_t *file, int versions, int *greatest); |
304 | | |
305 | | static void |
306 | | isc_log_doit(isc_logcategory_t category, isc_logmodule_t module, int level, |
307 | | const char *format, va_list args) ISC_FORMAT_PRINTF(4, 0); |
308 | | |
309 | | /*@{*/ |
310 | | /*! |
311 | | * Convenience macros. |
312 | | */ |
313 | | |
314 | 2 | #define FACILITY(channel) (channel->destination.facility) |
315 | 4 | #define FILE_NAME(channel) (channel->destination.file.name) |
316 | 4 | #define FILE_STREAM(channel) (channel->destination.file.stream) |
317 | 4 | #define FILE_VERSIONS(channel) (channel->destination.file.versions) |
318 | 4 | #define FILE_SUFFIX(channel) (channel->destination.file.suffix) |
319 | 4 | #define FILE_MAXSIZE(channel) (channel->destination.file.maximum_size) |
320 | 0 | #define FILE_MAXREACHED(channel) (channel->destination.file.maximum_reached) |
321 | | |
322 | | /*@}*/ |
323 | | /**** |
324 | | **** Public interfaces. |
325 | | ****/ |
326 | | |
327 | | void |
328 | 2 | isc_logconfig_create(isc_logconfig_t **lcfgp) { |
329 | 2 | REQUIRE(lcfgp != NULL && *lcfgp == NULL); |
330 | 2 | REQUIRE(VALID_CONTEXT(isc__lctx)); |
331 | | |
332 | 2 | int level = ISC_LOG_INFO; |
333 | | |
334 | 2 | isc_logconfig_t *lcfg = isc_mem_get(isc__lctx->mctx, sizeof(*lcfg)); |
335 | | |
336 | 2 | *lcfg = (isc_logconfig_t){ |
337 | 2 | .magic = LCFG_MAGIC, |
338 | 2 | .lctx = isc__lctx, |
339 | 2 | .channels = ISC_LIST_INITIALIZER, |
340 | 2 | .highest_level = level, |
341 | 2 | }; |
342 | | |
343 | | /* |
344 | | * Create the default channels: |
345 | | * default_syslog, default_stderr, default_debug and null. |
346 | | */ |
347 | 2 | isc_log_createchannel(lcfg, "default_syslog", ISC_LOG_TOSYSLOG, level, |
348 | 2 | ISC_LOGDESTINATION_SYSLOG(LOG_DAEMON), 0); |
349 | | |
350 | 2 | isc_log_createchannel(lcfg, "default_stderr", ISC_LOG_TOFILEDESC, level, |
351 | 2 | ISC_LOGDESTINATION_STDERR, ISC_LOG_PRINTTIME); |
352 | | |
353 | | /* |
354 | | * Set the default category's channel to default_stderr, |
355 | | * which is at the head of the channels list because it was |
356 | | * just created. |
357 | | */ |
358 | 2 | default_channel.channel = ISC_LIST_HEAD(lcfg->channels); |
359 | | |
360 | 2 | isc_log_createchannel(lcfg, "default_debug", ISC_LOG_TOFILEDESC, |
361 | 2 | ISC_LOG_DYNAMIC, ISC_LOGDESTINATION_STDERR, |
362 | 2 | ISC_LOG_PRINTTIME); |
363 | | |
364 | 2 | isc_log_createchannel(lcfg, "null", ISC_LOG_TONULL, ISC_LOG_DYNAMIC, |
365 | 2 | NULL, 0); |
366 | | |
367 | 2 | *lcfgp = lcfg; |
368 | 2 | } |
369 | | |
370 | | isc_logconfig_t * |
371 | 0 | isc_logconfig_get(void) { |
372 | 0 | REQUIRE(VALID_CONTEXT(isc__lctx)); |
373 | |
|
374 | 0 | return rcu_dereference(isc__lctx->logconfig); |
375 | 0 | } |
376 | | |
377 | | void |
378 | 0 | isc_logconfig_set(isc_logconfig_t *lcfg) { |
379 | 0 | REQUIRE(VALID_CONTEXT(isc__lctx)); |
380 | 0 | REQUIRE(VALID_CONFIG(lcfg)); |
381 | 0 | REQUIRE(lcfg->lctx == isc__lctx); |
382 | |
|
383 | 0 | isc_logconfig_t *old_cfg = rcu_xchg_pointer(&isc__lctx->logconfig, |
384 | 0 | lcfg); |
385 | 0 | sync_highest_level(lcfg); |
386 | 0 | synchronize_rcu(); |
387 | |
|
388 | 0 | if (old_cfg != NULL) { |
389 | 0 | isc_logconfig_destroy(&old_cfg); |
390 | 0 | } |
391 | 0 | } |
392 | | |
393 | | void |
394 | 0 | isc_logconfig_destroy(isc_logconfig_t **lcfgp) { |
395 | 0 | isc_logconfig_t *lcfg = NULL; |
396 | 0 | isc_mem_t *mctx = NULL; |
397 | 0 | char *filename = NULL; |
398 | |
|
399 | 0 | REQUIRE(lcfgp != NULL && VALID_CONFIG(*lcfgp)); |
400 | |
|
401 | 0 | lcfg = *lcfgp; |
402 | 0 | *lcfgp = NULL; |
403 | | |
404 | | /* |
405 | | * This function cannot be called with a logconfig that is in |
406 | | * use by a log context. |
407 | | */ |
408 | 0 | REQUIRE(lcfg->lctx != NULL); |
409 | |
|
410 | 0 | rcu_read_lock(); |
411 | 0 | REQUIRE(rcu_dereference(lcfg->lctx->logconfig) != lcfg); |
412 | 0 | rcu_read_unlock(); |
413 | |
|
414 | 0 | mctx = lcfg->lctx->mctx; |
415 | |
|
416 | 0 | ISC_LIST_FOREACH(lcfg->channels, channel, link) { |
417 | 0 | if (channel->type == ISC_LOG_TOFILE) { |
418 | | /* |
419 | | * The filename for the channel may have ultimately |
420 | | * started its life in user-land as a const string, |
421 | | * but in isc_log_createchannel it gets copied |
422 | | * into writable memory and is not longer truly const. |
423 | | */ |
424 | 0 | filename = UNCONST(FILE_NAME(channel)); |
425 | 0 | isc_mem_free(mctx, filename); |
426 | |
|
427 | 0 | if (FILE_STREAM(channel) != NULL) { |
428 | 0 | (void)fclose(FILE_STREAM(channel)); |
429 | 0 | } |
430 | 0 | } |
431 | |
|
432 | 0 | isc_mem_free(mctx, channel->name); |
433 | 0 | isc_mem_put(mctx, channel, sizeof(*channel)); |
434 | 0 | } |
435 | |
|
436 | 0 | for (size_t i = 0; i < ARRAY_SIZE(lcfg->channellists); i++) { |
437 | 0 | ISC_LIST_FOREACH(lcfg->channellists[i], item, link) { |
438 | 0 | ISC_LIST_UNLINK(lcfg->channellists[i], item, link); |
439 | 0 | isc_mem_put(mctx, item, sizeof(*item)); |
440 | 0 | } |
441 | 0 | } |
442 | |
|
443 | 0 | lcfg->dynamic = false; |
444 | 0 | if (lcfg->tag != NULL) { |
445 | 0 | isc_mem_free(lcfg->lctx->mctx, lcfg->tag); |
446 | 0 | } |
447 | 0 | lcfg->highest_level = 0; |
448 | 0 | lcfg->magic = 0; |
449 | |
|
450 | 0 | isc_mem_put(mctx, lcfg, sizeof(*lcfg)); |
451 | 0 | } |
452 | | |
453 | | isc_logcategory_t |
454 | 0 | isc_log_categorybyname(const char *name) { |
455 | 0 | REQUIRE(VALID_CONTEXT(isc__lctx)); |
456 | 0 | REQUIRE(name != NULL); |
457 | |
|
458 | 0 | for (isc_logcategory_t category = 0; category < ISC_LOGCATEGORY_MAX; |
459 | 0 | category++) |
460 | 0 | { |
461 | 0 | if (strcmp(categories_description[category], name) == 0) { |
462 | 0 | return category; |
463 | 0 | } |
464 | 0 | } |
465 | | |
466 | 0 | return ISC_LOGCATEGORY_INVALID; |
467 | 0 | } |
468 | | |
469 | | void |
470 | | isc_log_createchannel(isc_logconfig_t *lcfg, const char *name, |
471 | | unsigned int type, int level, |
472 | | const isc_logdestination_t *destination, |
473 | 8 | unsigned int flags) { |
474 | 8 | isc_logchannel_t *channel; |
475 | 8 | isc_mem_t *mctx; |
476 | 8 | unsigned int permitted = ISC_LOG_PRINTALL | ISC_LOG_DEBUGONLY | |
477 | 8 | ISC_LOG_BUFFERED | ISC_LOG_ISO8601 | |
478 | 8 | ISC_LOG_UTC | ISC_LOG_TZINFO; |
479 | | |
480 | 8 | REQUIRE(VALID_CONFIG(lcfg)); |
481 | 8 | REQUIRE(name != NULL); |
482 | 8 | REQUIRE(type == ISC_LOG_TOSYSLOG || type == ISC_LOG_TOFILE || |
483 | 8 | type == ISC_LOG_TOFILEDESC || type == ISC_LOG_TONULL); |
484 | 8 | REQUIRE(destination != NULL || type == ISC_LOG_TONULL); |
485 | 8 | REQUIRE(level >= ISC_LOG_CRITICAL); |
486 | 8 | REQUIRE((flags & ~permitted) == 0); |
487 | 8 | REQUIRE(!(flags & ISC_LOG_UTC) || !(flags & ISC_LOG_TZINFO)); |
488 | | |
489 | | /* FIXME: find duplicate names? */ |
490 | | |
491 | 8 | mctx = lcfg->lctx->mctx; |
492 | | |
493 | 8 | channel = isc_mem_get(mctx, sizeof(*channel)); |
494 | | |
495 | 8 | channel->name = isc_mem_strdup(mctx, name); |
496 | | |
497 | 8 | channel->type = type; |
498 | 8 | channel->level = level; |
499 | 8 | channel->flags = flags; |
500 | 8 | ISC_LINK_INIT(channel, link); |
501 | | |
502 | 8 | switch (type) { |
503 | 2 | case ISC_LOG_TOSYSLOG: |
504 | 2 | FACILITY(channel) = destination->facility; |
505 | 2 | break; |
506 | | |
507 | 0 | case ISC_LOG_TOFILE: |
508 | | /* |
509 | | * The file name is copied because greatest_version wants |
510 | | * to scribble on it, so it needs to be definitely in |
511 | | * writable memory. |
512 | | */ |
513 | 0 | FILE_NAME(channel) = isc_mem_strdup(mctx, |
514 | 0 | destination->file.name); |
515 | 0 | FILE_STREAM(channel) = NULL; |
516 | 0 | FILE_VERSIONS(channel) = destination->file.versions; |
517 | 0 | FILE_SUFFIX(channel) = destination->file.suffix; |
518 | 0 | FILE_MAXSIZE(channel) = destination->file.maximum_size; |
519 | 0 | FILE_MAXREACHED(channel) = false; |
520 | 0 | break; |
521 | | |
522 | 4 | case ISC_LOG_TOFILEDESC: |
523 | 4 | FILE_NAME(channel) = NULL; |
524 | 4 | FILE_STREAM(channel) = destination->file.stream; |
525 | 4 | FILE_MAXSIZE(channel) = 0; |
526 | 4 | FILE_VERSIONS(channel) = ISC_LOG_ROLLNEVER; |
527 | 4 | FILE_SUFFIX(channel) = isc_log_rollsuffix_increment; |
528 | 4 | break; |
529 | | |
530 | 2 | case ISC_LOG_TONULL: |
531 | | /* Nothing. */ |
532 | 2 | break; |
533 | | |
534 | 0 | default: |
535 | 0 | UNREACHABLE(); |
536 | 8 | } |
537 | | |
538 | 8 | ISC_LIST_PREPEND(lcfg->channels, channel, link); |
539 | | |
540 | | /* |
541 | | * If default_stderr was redefined, make the default category |
542 | | * point to the new default_stderr. |
543 | | */ |
544 | 8 | if (strcmp(name, "default_stderr") == 0) { |
545 | 2 | default_channel.channel = channel; |
546 | 2 | } |
547 | 8 | } |
548 | | |
549 | | isc_result_t |
550 | | isc_log_usechannel(isc_logconfig_t *lcfg, const char *name, |
551 | | const isc_logcategory_t category, |
552 | 0 | const isc_logmodule_t module) { |
553 | 0 | isc_logchannel_t *channel = NULL; |
554 | |
|
555 | 0 | REQUIRE(VALID_CONFIG(lcfg)); |
556 | 0 | REQUIRE(name != NULL); |
557 | 0 | REQUIRE(category >= ISC_LOGCATEGORY_DEFAULT && |
558 | 0 | category < ISC_LOGCATEGORY_MAX); |
559 | 0 | REQUIRE(module >= ISC_LOGMODULE_DEFAULT && module < ISC_LOGMODULE_MAX); |
560 | |
|
561 | 0 | ISC_LIST_FOREACH(lcfg->channels, c, link) { |
562 | 0 | if (strcmp(name, c->name) == 0) { |
563 | 0 | channel = c; |
564 | 0 | break; |
565 | 0 | } |
566 | 0 | } |
567 | |
|
568 | 0 | if (channel == NULL) { |
569 | 0 | return ISC_R_NOTFOUND; |
570 | 0 | } |
571 | | |
572 | 0 | if (category != ISC_LOGCATEGORY_DEFAULT) { |
573 | 0 | assignchannel(lcfg, category, module, channel); |
574 | 0 | } else { |
575 | | /* |
576 | | * Assign to all categories. Note that this includes |
577 | | * the default channel. |
578 | | */ |
579 | 0 | for (size_t i = ISC_LOGCATEGORY_DEFAULT; |
580 | 0 | i < ISC_LOGCATEGORY_MAX; i++) |
581 | 0 | { |
582 | 0 | assignchannel(lcfg, i, module, channel); |
583 | 0 | } |
584 | 0 | } |
585 | | |
586 | | /* |
587 | | * Update the highest logging level, if the current lcfg is in use. |
588 | | */ |
589 | 0 | rcu_read_lock(); |
590 | 0 | if (rcu_dereference(lcfg->lctx->logconfig) == lcfg) { |
591 | 0 | sync_highest_level(lcfg); |
592 | 0 | } |
593 | 0 | rcu_read_unlock(); |
594 | |
|
595 | 0 | return ISC_R_SUCCESS; |
596 | 0 | } |
597 | | |
598 | | void |
599 | | isc_log_createandusechannel(isc_logconfig_t *lcfg, const char *name, |
600 | | unsigned int type, int level, |
601 | | const isc_logdestination_t *destination, |
602 | | unsigned int flags, |
603 | | const isc_logcategory_t category, |
604 | 0 | const isc_logmodule_t module) { |
605 | 0 | isc_log_createchannel(lcfg, name, type, level, destination, flags); |
606 | 0 | RUNTIME_CHECK(isc_log_usechannel(lcfg, name, category, module) == |
607 | 0 | ISC_R_SUCCESS); |
608 | 0 | } |
609 | | |
610 | | void |
611 | | isc_log_write(isc_logcategory_t category, isc_logmodule_t module, int level, |
612 | 0 | const char *format, ...) { |
613 | 0 | va_list args; |
614 | | |
615 | | /* |
616 | | * Contract checking is done in isc_log_doit(). |
617 | | */ |
618 | |
|
619 | 0 | va_start(args, format); |
620 | 0 | isc_log_doit(category, module, level, format, args); |
621 | 0 | va_end(args); |
622 | 0 | } |
623 | | |
624 | | void |
625 | | isc_log_vwrite(isc_logcategory_t category, isc_logmodule_t module, int level, |
626 | 0 | const char *format, va_list args) { |
627 | | /* |
628 | | * Contract checking is done in isc_log_doit(). |
629 | | */ |
630 | 0 | isc_log_doit(category, module, level, format, args); |
631 | 0 | } |
632 | | |
633 | | void |
634 | 0 | isc_log_setdebuglevel(unsigned int level) { |
635 | 0 | REQUIRE(VALID_CONTEXT(isc__lctx)); |
636 | |
|
637 | 0 | atomic_store_release(&isc__lctx->debug_level, level); |
638 | | /* |
639 | | * Close ISC_LOG_DEBUGONLY channels if level is zero. |
640 | | */ |
641 | 0 | if (level == 0) { |
642 | 0 | rcu_read_lock(); |
643 | 0 | isc_logconfig_t *lcfg = rcu_dereference(isc__lctx->logconfig); |
644 | 0 | if (lcfg != NULL) { |
645 | 0 | LOCK(&isc__lctx->lock); |
646 | 0 | ISC_LIST_FOREACH(lcfg->channels, channel, link) { |
647 | 0 | if (channel->type == ISC_LOG_TOFILE && |
648 | 0 | (channel->flags & ISC_LOG_DEBUGONLY) != 0 && |
649 | 0 | FILE_STREAM(channel) != NULL) |
650 | 0 | { |
651 | 0 | (void)fclose(FILE_STREAM(channel)); |
652 | 0 | FILE_STREAM(channel) = NULL; |
653 | 0 | } |
654 | 0 | } |
655 | 0 | UNLOCK(&isc__lctx->lock); |
656 | 0 | } |
657 | 0 | rcu_read_unlock(); |
658 | 0 | } |
659 | 0 | } |
660 | | |
661 | | unsigned int |
662 | 0 | isc_log_getdebuglevel(void) { |
663 | 0 | REQUIRE(VALID_CONTEXT(isc__lctx)); |
664 | |
|
665 | 0 | return atomic_load_acquire(&isc__lctx->debug_level); |
666 | 0 | } |
667 | | |
668 | | void |
669 | 0 | isc_log_settag(isc_logconfig_t *lcfg, const char *tag) { |
670 | 0 | REQUIRE(VALID_CONFIG(lcfg)); |
671 | |
|
672 | 0 | if (tag != NULL && *tag != '\0') { |
673 | 0 | if (lcfg->tag != NULL) { |
674 | 0 | isc_mem_free(lcfg->lctx->mctx, lcfg->tag); |
675 | 0 | } |
676 | 0 | lcfg->tag = isc_mem_strdup(lcfg->lctx->mctx, tag); |
677 | 0 | } else { |
678 | 0 | if (lcfg->tag != NULL) { |
679 | 0 | isc_mem_free(lcfg->lctx->mctx, lcfg->tag); |
680 | 0 | } |
681 | 0 | } |
682 | 0 | } |
683 | | |
684 | | char * |
685 | 0 | isc_log_gettag(isc_logconfig_t *lcfg) { |
686 | 0 | REQUIRE(VALID_CONFIG(lcfg)); |
687 | |
|
688 | 0 | return lcfg->tag; |
689 | 0 | } |
690 | | |
691 | | /* XXXDCL NT -- This interface will assuredly be changing. */ |
692 | | void |
693 | 0 | isc_log_opensyslog(const char *tag, int options, int facility) { |
694 | 0 | (void)openlog(tag, options, facility); |
695 | 0 | } |
696 | | |
697 | | void |
698 | 0 | isc_log_closefilelogs(void) { |
699 | 0 | REQUIRE(VALID_CONTEXT(isc__lctx)); |
700 | |
|
701 | 0 | rcu_read_lock(); |
702 | 0 | isc_logconfig_t *lcfg = rcu_dereference(isc__lctx->logconfig); |
703 | 0 | if (lcfg != NULL) { |
704 | 0 | LOCK(&isc__lctx->lock); |
705 | 0 | ISC_LIST_FOREACH(lcfg->channels, channel, link) { |
706 | 0 | if (channel->type == ISC_LOG_TOFILE && |
707 | 0 | FILE_STREAM(channel) != NULL) |
708 | 0 | { |
709 | 0 | (void)fclose(FILE_STREAM(channel)); |
710 | 0 | FILE_STREAM(channel) = NULL; |
711 | 0 | } |
712 | 0 | } |
713 | 0 | UNLOCK(&isc__lctx->lock); |
714 | 0 | } |
715 | 0 | rcu_read_unlock(); |
716 | 0 | } |
717 | | |
718 | | /**** |
719 | | **** Internal functions |
720 | | ****/ |
721 | | |
722 | | static void |
723 | | assignchannel(isc_logconfig_t *lcfg, const isc_logcategory_t category, |
724 | 0 | const isc_logmodule_t module, isc_logchannel_t *channel) { |
725 | 0 | REQUIRE(VALID_CONFIG(lcfg)); |
726 | 0 | REQUIRE(channel != NULL); |
727 | |
|
728 | 0 | isc_log_t *lctx = lcfg->lctx; |
729 | |
|
730 | 0 | REQUIRE(category >= ISC_LOGCATEGORY_DEFAULT && |
731 | 0 | category < ISC_LOGCATEGORY_MAX); |
732 | 0 | REQUIRE(module >= ISC_LOGMODULE_DEFAULT && module < ISC_LOGMODULE_MAX); |
733 | |
|
734 | 0 | isc_logchannellist_t *new_item = isc_mem_get(lctx->mctx, |
735 | 0 | sizeof(*new_item)); |
736 | |
|
737 | 0 | new_item->channel = channel; |
738 | 0 | new_item->module = module; |
739 | 0 | ISC_LIST_INITANDPREPEND(lcfg->channellists[category], new_item, link); |
740 | | |
741 | | /* |
742 | | * Remember the highest logging level set by any channel in the |
743 | | * logging config, so isc_log_doit() can quickly return if the |
744 | | * message is too high to be logged by any channel. |
745 | | */ |
746 | 0 | if (channel->type != ISC_LOG_TONULL) { |
747 | 0 | if (lcfg->highest_level < channel->level) { |
748 | 0 | lcfg->highest_level = channel->level; |
749 | 0 | } |
750 | 0 | if (channel->level == ISC_LOG_DYNAMIC) { |
751 | 0 | lcfg->dynamic = true; |
752 | 0 | } |
753 | 0 | } |
754 | 0 | } |
755 | | |
756 | | static void |
757 | 0 | sync_highest_level(isc_logconfig_t *lcfg) { |
758 | 0 | atomic_store(&isc__lctx->highest_level, lcfg->highest_level); |
759 | 0 | atomic_store(&isc__lctx->dynamic, lcfg->dynamic); |
760 | 0 | } |
761 | | |
762 | | static isc_result_t |
763 | 0 | greatest_version(isc_logfile_t *file, int versions, int *greatestp) { |
764 | 0 | char *digit_end; |
765 | 0 | char dirbuf[PATH_MAX + 1]; |
766 | 0 | const char *bname; |
767 | 0 | const char *dirname = "."; |
768 | 0 | int version, greatest = -1; |
769 | 0 | isc_dir_t dir; |
770 | 0 | isc_result_t result; |
771 | 0 | size_t bnamelen; |
772 | |
|
773 | 0 | bname = strrchr(file->name, '/'); |
774 | 0 | if (bname != NULL) { |
775 | | /* |
776 | | * Copy the complete file name to dirbuf. |
777 | | */ |
778 | 0 | size_t len = strlcpy(dirbuf, file->name, sizeof(dirbuf)); |
779 | 0 | if (len >= sizeof(dirbuf)) { |
780 | 0 | result = ISC_R_NOSPACE; |
781 | 0 | syslog(LOG_ERR, "unable to remove log files: %s", |
782 | 0 | isc_result_totext(result)); |
783 | 0 | return result; |
784 | 0 | } |
785 | | |
786 | | /* |
787 | | * Truncate after trailing '/' so the code works for |
788 | | * files in the root directory. |
789 | | */ |
790 | 0 | bname++; |
791 | 0 | dirbuf[bname - file->name] = '\0'; |
792 | 0 | dirname = dirbuf; |
793 | 0 | } else { |
794 | 0 | bname = file->name; |
795 | 0 | } |
796 | 0 | bnamelen = strlen(bname); |
797 | |
|
798 | 0 | isc_dir_init(&dir); |
799 | 0 | RETERR(isc_dir_open(&dir, dirname)); |
800 | |
|
801 | 0 | while (isc_dir_read(&dir) == ISC_R_SUCCESS) { |
802 | 0 | if (dir.entry.length > bnamelen && |
803 | 0 | strncmp(dir.entry.name, bname, bnamelen) == 0 && |
804 | 0 | dir.entry.name[bnamelen] == '.') |
805 | 0 | { |
806 | 0 | version = strtol(&dir.entry.name[bnamelen + 1], |
807 | 0 | &digit_end, 10); |
808 | | /* |
809 | | * Remove any backup files that exceed versions. |
810 | | */ |
811 | 0 | if (*digit_end == '\0' && version >= versions) { |
812 | 0 | int n = dirfd(dir.handle); |
813 | 0 | if (n >= 0) { |
814 | 0 | n = unlinkat(n, dir.entry.name, 0); |
815 | 0 | } |
816 | 0 | if (n < 0) { |
817 | 0 | result = isc_errno_toresult(errno); |
818 | 0 | if (result != ISC_R_SUCCESS && |
819 | 0 | result != ISC_R_FILENOTFOUND) |
820 | 0 | { |
821 | 0 | syslog(LOG_ERR, |
822 | 0 | "unable to remove log " |
823 | 0 | "file '%s%s': %s", |
824 | 0 | bname == file->name |
825 | 0 | ? "" |
826 | 0 | : dirname, |
827 | 0 | dir.entry.name, |
828 | 0 | isc_result_totext( |
829 | 0 | result)); |
830 | 0 | } |
831 | 0 | } |
832 | 0 | } else if (*digit_end == '\0' && version > greatest) { |
833 | 0 | greatest = version; |
834 | 0 | } |
835 | 0 | } |
836 | 0 | } |
837 | 0 | isc_dir_close(&dir); |
838 | |
|
839 | 0 | *greatestp = greatest; |
840 | 0 | return ISC_R_SUCCESS; |
841 | 0 | } |
842 | | |
843 | | static void |
844 | 0 | insert_sort(int64_t to_keep[], int64_t versions, int64_t version) { |
845 | 0 | int i = 0; |
846 | 0 | while (i < versions && version < to_keep[i]) { |
847 | 0 | i++; |
848 | 0 | } |
849 | 0 | if (i == versions) { |
850 | 0 | return; |
851 | 0 | } |
852 | 0 | if (i < versions - 1) { |
853 | 0 | memmove(&to_keep[i + 1], &to_keep[i], |
854 | 0 | sizeof(to_keep[0]) * (versions - i - 1)); |
855 | 0 | } |
856 | 0 | to_keep[i] = version; |
857 | 0 | } |
858 | | |
859 | | static int64_t |
860 | | last_to_keep(int64_t versions, isc_dir_t *dirp, const char *bname, |
861 | 0 | size_t bnamelen) { |
862 | 0 | int64_t to_keep[ISC_LOG_MAX_VERSIONS] = { 0 }; |
863 | 0 | int64_t version = 0; |
864 | |
|
865 | 0 | if (versions <= 0) { |
866 | 0 | return INT64_MAX; |
867 | 0 | } |
868 | | |
869 | 0 | if (versions > ISC_LOG_MAX_VERSIONS) { |
870 | 0 | versions = ISC_LOG_MAX_VERSIONS; |
871 | 0 | } |
872 | | /* |
873 | | * First we fill 'to_keep' structure using insertion sort |
874 | | */ |
875 | 0 | memset(to_keep, 0, sizeof(to_keep)); |
876 | 0 | while (isc_dir_read(dirp) == ISC_R_SUCCESS) { |
877 | 0 | char *digit_end = NULL; |
878 | 0 | char *ename = NULL; |
879 | |
|
880 | 0 | if (dirp->entry.length <= bnamelen || |
881 | 0 | strncmp(dirp->entry.name, bname, bnamelen) != 0 || |
882 | 0 | dirp->entry.name[bnamelen] != '.') |
883 | 0 | { |
884 | 0 | continue; |
885 | 0 | } |
886 | | |
887 | 0 | ename = &dirp->entry.name[bnamelen + 1]; |
888 | 0 | version = strtoull(ename, &digit_end, 10); |
889 | 0 | if (*digit_end == '\0') { |
890 | 0 | insert_sort(to_keep, versions, version); |
891 | 0 | } |
892 | 0 | } |
893 | |
|
894 | 0 | isc_dir_reset(dirp); |
895 | | |
896 | | /* |
897 | | * to_keep[versions - 1] is the last one we want to keep |
898 | | */ |
899 | 0 | return to_keep[versions - 1]; |
900 | 0 | } |
901 | | |
902 | | static isc_result_t |
903 | 0 | remove_old_tsversions(isc_logfile_t *file, int versions) { |
904 | 0 | char *digit_end; |
905 | 0 | char dirbuf[PATH_MAX + 1]; |
906 | 0 | const char *bname; |
907 | 0 | const char *dirname = "."; |
908 | 0 | int64_t version, last = INT64_MAX; |
909 | 0 | isc_dir_t dir; |
910 | 0 | isc_result_t result; |
911 | 0 | size_t bnamelen; |
912 | |
|
913 | 0 | bname = strrchr(file->name, '/'); |
914 | 0 | if (bname != NULL) { |
915 | | /* |
916 | | * Copy the complete file name to dirbuf. |
917 | | */ |
918 | 0 | size_t len = strlcpy(dirbuf, file->name, sizeof(dirbuf)); |
919 | 0 | if (len >= sizeof(dirbuf)) { |
920 | 0 | result = ISC_R_NOSPACE; |
921 | 0 | syslog(LOG_ERR, "unable to remove log files: %s", |
922 | 0 | isc_result_totext(result)); |
923 | 0 | return result; |
924 | 0 | } |
925 | | |
926 | | /* |
927 | | * Truncate after trailing '/' so the code works for |
928 | | * files in the root directory. |
929 | | */ |
930 | 0 | bname++; |
931 | 0 | dirbuf[bname - file->name] = '\0'; |
932 | 0 | dirname = dirbuf; |
933 | 0 | } else { |
934 | 0 | bname = file->name; |
935 | 0 | } |
936 | 0 | bnamelen = strlen(bname); |
937 | |
|
938 | 0 | isc_dir_init(&dir); |
939 | 0 | RETERR(isc_dir_open(&dir, dirname)); |
940 | |
|
941 | 0 | last = last_to_keep(versions, &dir, bname, bnamelen); |
942 | |
|
943 | 0 | while (isc_dir_read(&dir) == ISC_R_SUCCESS) { |
944 | 0 | if (dir.entry.length > bnamelen && |
945 | 0 | strncmp(dir.entry.name, bname, bnamelen) == 0 && |
946 | 0 | dir.entry.name[bnamelen] == '.') |
947 | 0 | { |
948 | 0 | version = strtoull(&dir.entry.name[bnamelen + 1], |
949 | 0 | &digit_end, 10); |
950 | | /* |
951 | | * Remove any backup files that exceed versions. |
952 | | */ |
953 | 0 | if (*digit_end == '\0' && version < last) { |
954 | 0 | int n = dirfd(dir.handle); |
955 | 0 | if (n >= 0) { |
956 | 0 | n = unlinkat(n, dir.entry.name, 0); |
957 | 0 | } |
958 | 0 | if (n < 0) { |
959 | 0 | result = isc_errno_toresult(errno); |
960 | 0 | if (result != ISC_R_SUCCESS && |
961 | 0 | result != ISC_R_FILENOTFOUND) |
962 | 0 | { |
963 | 0 | syslog(LOG_ERR, |
964 | 0 | "unable to remove log " |
965 | 0 | "file '%s%s': %s", |
966 | 0 | bname == file->name |
967 | 0 | ? "" |
968 | 0 | : dirname, |
969 | 0 | dir.entry.name, |
970 | 0 | isc_result_totext( |
971 | 0 | result)); |
972 | 0 | } |
973 | 0 | } |
974 | 0 | } |
975 | 0 | } |
976 | 0 | } |
977 | 0 | isc_dir_close(&dir); |
978 | 0 | return ISC_R_SUCCESS; |
979 | 0 | } |
980 | | |
981 | | static isc_result_t |
982 | 0 | roll_increment(isc_logfile_t *file) { |
983 | 0 | int i, n, greatest; |
984 | 0 | char current[PATH_MAX + 1]; |
985 | 0 | char newpath[PATH_MAX + 1]; |
986 | 0 | const char *path; |
987 | 0 | isc_result_t result = ISC_R_SUCCESS; |
988 | |
|
989 | 0 | REQUIRE(file != NULL); |
990 | 0 | REQUIRE(file->versions != 0); |
991 | |
|
992 | 0 | path = file->name; |
993 | |
|
994 | 0 | if (file->versions == ISC_LOG_ROLLINFINITE) { |
995 | | /* |
996 | | * Find the first missing entry in the log file sequence. |
997 | | */ |
998 | 0 | for (greatest = 0; greatest < INT_MAX; greatest++) { |
999 | 0 | n = snprintf(current, sizeof(current), "%s.%u", path, |
1000 | 0 | (unsigned int)greatest); |
1001 | 0 | if (n >= (int)sizeof(current) || n < 0 || |
1002 | 0 | !isc_file_exists(current)) |
1003 | 0 | { |
1004 | 0 | break; |
1005 | 0 | } |
1006 | 0 | } |
1007 | 0 | } else { |
1008 | | /* |
1009 | | * Get the largest existing version and remove any |
1010 | | * version greater than the permitted version. |
1011 | | */ |
1012 | 0 | RETERR(greatest_version(file, file->versions, &greatest)); |
1013 | | |
1014 | | /* |
1015 | | * Increment if greatest is not the actual maximum value. |
1016 | | */ |
1017 | 0 | if (greatest < file->versions - 1) { |
1018 | 0 | greatest++; |
1019 | 0 | } |
1020 | 0 | } |
1021 | | |
1022 | 0 | for (i = greatest; i > 0; i--) { |
1023 | 0 | result = ISC_R_SUCCESS; |
1024 | 0 | n = snprintf(current, sizeof(current), "%s.%u", path, |
1025 | 0 | (unsigned int)(i - 1)); |
1026 | 0 | if (n >= (int)sizeof(current) || n < 0) { |
1027 | 0 | result = ISC_R_NOSPACE; |
1028 | 0 | } |
1029 | 0 | if (result == ISC_R_SUCCESS) { |
1030 | 0 | n = snprintf(newpath, sizeof(newpath), "%s.%u", path, |
1031 | 0 | (unsigned int)i); |
1032 | 0 | if (n >= (int)sizeof(newpath) || n < 0) { |
1033 | 0 | result = ISC_R_NOSPACE; |
1034 | 0 | } |
1035 | 0 | } |
1036 | 0 | if (result == ISC_R_SUCCESS) { |
1037 | 0 | result = isc_file_rename(current, newpath); |
1038 | 0 | } |
1039 | 0 | if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) { |
1040 | 0 | syslog(LOG_ERR, |
1041 | 0 | "unable to rename log file '%s.%u' to " |
1042 | 0 | "'%s.%u': %s", |
1043 | 0 | path, i - 1, path, i, isc_result_totext(result)); |
1044 | 0 | } |
1045 | 0 | } |
1046 | |
|
1047 | 0 | n = snprintf(newpath, sizeof(newpath), "%s.0", path); |
1048 | 0 | if (n >= (int)sizeof(newpath) || n < 0) { |
1049 | 0 | result = ISC_R_NOSPACE; |
1050 | 0 | } else { |
1051 | 0 | result = isc_file_rename(path, newpath); |
1052 | 0 | } |
1053 | 0 | if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) { |
1054 | 0 | syslog(LOG_ERR, "unable to rename log file '%s' to '%s.0': %s", |
1055 | 0 | path, path, isc_result_totext(result)); |
1056 | 0 | } |
1057 | |
|
1058 | 0 | return ISC_R_SUCCESS; |
1059 | 0 | } |
1060 | | |
1061 | | static isc_result_t |
1062 | 0 | roll_timestamp(isc_logfile_t *file) { |
1063 | 0 | int n; |
1064 | 0 | char newts[PATH_MAX + 1]; |
1065 | 0 | char newpath[PATH_MAX + 1]; |
1066 | 0 | const char *path; |
1067 | 0 | isc_time_t now; |
1068 | 0 | isc_result_t result = ISC_R_SUCCESS; |
1069 | |
|
1070 | 0 | REQUIRE(file != NULL); |
1071 | 0 | REQUIRE(file->versions != 0); |
1072 | |
|
1073 | 0 | path = file->name; |
1074 | | |
1075 | | /* |
1076 | | * First find all the logfiles and remove the oldest ones |
1077 | | * Save one fewer than file->versions because we'll be renaming |
1078 | | * the existing file to a timestamped version after this. |
1079 | | */ |
1080 | 0 | if (file->versions != ISC_LOG_ROLLINFINITE) { |
1081 | 0 | remove_old_tsversions(file, file->versions - 1); |
1082 | 0 | } |
1083 | | |
1084 | | /* Then just rename the current logfile */ |
1085 | 0 | now = isc_time_now(); |
1086 | 0 | isc_time_formatshorttimestamp(&now, newts, PATH_MAX + 1); |
1087 | 0 | n = snprintf(newpath, sizeof(newpath), "%s.%s", path, newts); |
1088 | 0 | if (n >= (int)sizeof(newpath) || n < 0) { |
1089 | 0 | result = ISC_R_NOSPACE; |
1090 | 0 | } else { |
1091 | 0 | result = isc_file_rename(path, newpath); |
1092 | 0 | } |
1093 | 0 | if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) { |
1094 | 0 | syslog(LOG_ERR, "unable to rename log file '%s' to '%s.0': %s", |
1095 | 0 | path, path, isc_result_totext(result)); |
1096 | 0 | } |
1097 | |
|
1098 | 0 | return ISC_R_SUCCESS; |
1099 | 0 | } |
1100 | | |
1101 | | isc_result_t |
1102 | 0 | isc_logfile_roll(isc_logfile_t *file) { |
1103 | 0 | isc_result_t result; |
1104 | |
|
1105 | 0 | REQUIRE(file != NULL); |
1106 | | |
1107 | | /* |
1108 | | * Do nothing (not even excess version trimming) if ISC_LOG_ROLLNEVER |
1109 | | * is specified. Apparently complete external control over the log |
1110 | | * files is desired. |
1111 | | */ |
1112 | 0 | if (file->versions == ISC_LOG_ROLLNEVER) { |
1113 | 0 | return ISC_R_SUCCESS; |
1114 | 0 | } else if (file->versions == 0) { |
1115 | 0 | result = isc_file_remove(file->name); |
1116 | 0 | if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) { |
1117 | 0 | syslog(LOG_ERR, "unable to remove log file '%s': %s", |
1118 | 0 | file->name, isc_result_totext(result)); |
1119 | 0 | } |
1120 | 0 | return ISC_R_SUCCESS; |
1121 | 0 | } |
1122 | | |
1123 | 0 | switch (file->suffix) { |
1124 | 0 | case isc_log_rollsuffix_increment: |
1125 | 0 | return roll_increment(file); |
1126 | 0 | case isc_log_rollsuffix_timestamp: |
1127 | 0 | return roll_timestamp(file); |
1128 | 0 | default: |
1129 | 0 | return ISC_R_UNEXPECTED; |
1130 | 0 | } |
1131 | 0 | } |
1132 | | |
1133 | | static isc_result_t |
1134 | 0 | isc_log_open(isc_logchannel_t *channel) { |
1135 | 0 | struct stat statbuf; |
1136 | 0 | bool regular_file; |
1137 | 0 | bool roll = false; |
1138 | 0 | isc_result_t result = ISC_R_SUCCESS; |
1139 | 0 | const char *path; |
1140 | |
|
1141 | 0 | REQUIRE(channel->type == ISC_LOG_TOFILE); |
1142 | 0 | REQUIRE(FILE_STREAM(channel) == NULL); |
1143 | |
|
1144 | 0 | path = FILE_NAME(channel); |
1145 | |
|
1146 | 0 | REQUIRE(path != NULL && *path != '\0'); |
1147 | | |
1148 | | /* |
1149 | | * Determine type of file; only regular files will be |
1150 | | * version renamed, and only if the base file exists |
1151 | | * and either has no size limit or has reached its size limit. |
1152 | | */ |
1153 | 0 | if (stat(path, &statbuf) == 0) { |
1154 | 0 | regular_file = S_ISREG(statbuf.st_mode) ? true : false; |
1155 | | /* XXXDCL if not regular_file complain? */ |
1156 | 0 | if ((FILE_MAXSIZE(channel) == 0 && |
1157 | 0 | FILE_VERSIONS(channel) != ISC_LOG_ROLLNEVER) || |
1158 | 0 | (FILE_MAXSIZE(channel) > 0 && |
1159 | 0 | statbuf.st_size >= FILE_MAXSIZE(channel))) |
1160 | 0 | { |
1161 | 0 | roll = regular_file; |
1162 | 0 | } |
1163 | 0 | } else if (errno == ENOENT) { |
1164 | 0 | regular_file = true; |
1165 | 0 | POST(regular_file); |
1166 | 0 | } else { |
1167 | 0 | result = ISC_R_INVALIDFILE; |
1168 | 0 | } |
1169 | | |
1170 | | /* |
1171 | | * Version control. |
1172 | | */ |
1173 | 0 | if (result == ISC_R_SUCCESS && roll) { |
1174 | 0 | if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER) { |
1175 | 0 | return ISC_R_MAXSIZE; |
1176 | 0 | } |
1177 | 0 | result = isc_logfile_roll(&channel->destination.file); |
1178 | 0 | if (result != ISC_R_SUCCESS) { |
1179 | 0 | if ((channel->flags & ISC_LOG_OPENERR) == 0) { |
1180 | 0 | syslog(LOG_ERR, |
1181 | 0 | "isc_log_open: isc_logfile_roll '%s' " |
1182 | 0 | "failed: %s", |
1183 | 0 | FILE_NAME(channel), |
1184 | 0 | isc_result_totext(result)); |
1185 | 0 | channel->flags |= ISC_LOG_OPENERR; |
1186 | 0 | } |
1187 | 0 | return result; |
1188 | 0 | } |
1189 | 0 | } |
1190 | | |
1191 | 0 | result = isc_stdio_open(path, "a", &FILE_STREAM(channel)); |
1192 | |
|
1193 | 0 | return result; |
1194 | 0 | } |
1195 | | |
1196 | | ISC_NO_SANITIZE_THREAD bool |
1197 | 0 | isc_log_wouldlog(int level) { |
1198 | | /* |
1199 | | * Try to avoid locking the mutex for messages which can't |
1200 | | * possibly be logged to any channels -- primarily debugging |
1201 | | * messages that the debug level is not high enough to print. |
1202 | | * |
1203 | | * If the level is (mathematically) less than or equal to the |
1204 | | * highest_level, or if there is a dynamic channel and the level is |
1205 | | * less than or equal to the debug level, the main loop must be |
1206 | | * entered to see if the message should really be output. |
1207 | | */ |
1208 | 0 | if (isc__lctx == NULL) { |
1209 | 0 | return false; |
1210 | 0 | } |
1211 | 0 | if (forcelog) { |
1212 | 0 | return true; |
1213 | 0 | } |
1214 | | |
1215 | 0 | int highest_level = atomic_load_acquire(&isc__lctx->highest_level); |
1216 | 0 | if (level <= highest_level) { |
1217 | 0 | return true; |
1218 | 0 | } |
1219 | 0 | if (atomic_load_acquire(&isc__lctx->dynamic)) { |
1220 | 0 | int debug_level = atomic_load_acquire(&isc__lctx->debug_level); |
1221 | 0 | if (level <= debug_level) { |
1222 | 0 | return true; |
1223 | 0 | } |
1224 | 0 | } |
1225 | | |
1226 | 0 | return false; |
1227 | 0 | } |
1228 | | |
1229 | | static void |
1230 | | isc_log_doit(isc_logcategory_t category, isc_logmodule_t module, int level, |
1231 | 0 | const char *format, va_list args) { |
1232 | 0 | int syslog_level; |
1233 | 0 | const char *time_string; |
1234 | 0 | char local_time[64] = { 0 }; |
1235 | 0 | char iso8601z_string[64] = { 0 }; |
1236 | 0 | char iso8601l_string[64] = { 0 }; |
1237 | 0 | char iso8601tz_string[64] = { 0 }; |
1238 | 0 | char level_string[24] = { 0 }; |
1239 | 0 | struct stat statbuf; |
1240 | 0 | bool matched = false; |
1241 | 0 | bool printtime, iso8601, utc, tzinfo, printtag, printcolon; |
1242 | 0 | bool printcategory, printmodule, printlevel, buffered; |
1243 | 0 | isc_logchannel_t *channel; |
1244 | 0 | isc_logchannellist_t *category_channels; |
1245 | 0 | int_fast32_t dlevel; |
1246 | 0 | isc_result_t result; |
1247 | |
|
1248 | 0 | REQUIRE(isc__lctx == NULL || VALID_CONTEXT(isc__lctx)); |
1249 | 0 | REQUIRE(category > ISC_LOGCATEGORY_DEFAULT && |
1250 | 0 | category < ISC_LOGCATEGORY_MAX); |
1251 | 0 | REQUIRE(module > ISC_LOGMODULE_DEFAULT && module < ISC_LOGMODULE_MAX); |
1252 | 0 | REQUIRE(level != ISC_LOG_DYNAMIC); |
1253 | 0 | REQUIRE(format != NULL); |
1254 | |
|
1255 | 0 | if (!isc_log_wouldlog(level)) { |
1256 | 0 | return; |
1257 | 0 | } |
1258 | | |
1259 | 0 | rcu_read_lock(); |
1260 | 0 | LOCK(&isc__lctx->lock); |
1261 | |
|
1262 | 0 | isc__lctx->buffer[0] = '\0'; |
1263 | |
|
1264 | 0 | isc_logconfig_t *lcfg = rcu_dereference(isc__lctx->logconfig); |
1265 | 0 | if (lcfg == NULL) { |
1266 | 0 | goto unlock; |
1267 | 0 | } |
1268 | | |
1269 | 0 | category_channels = ISC_LIST_HEAD(lcfg->channellists[category]); |
1270 | |
|
1271 | 0 | do { |
1272 | | /* |
1273 | | * If the channel list end was reached and a match was |
1274 | | * made, everything is finished. |
1275 | | */ |
1276 | 0 | if (category_channels == NULL && matched) { |
1277 | 0 | break; |
1278 | 0 | } |
1279 | | |
1280 | 0 | if (category_channels == NULL && !matched && |
1281 | 0 | category_channels != ISC_LIST_HEAD(lcfg->channellists[0])) |
1282 | 0 | { |
1283 | | /* |
1284 | | * No category/module pair was explicitly |
1285 | | * configured. Try the category named "default". |
1286 | | */ |
1287 | 0 | category_channels = |
1288 | 0 | ISC_LIST_HEAD(lcfg->channellists[0]); |
1289 | 0 | } |
1290 | |
|
1291 | 0 | if (category_channels == NULL && !matched) { |
1292 | | /* |
1293 | | * No matching module was explicitly configured |
1294 | | * for the category named "default". Use the |
1295 | | * internal default channel. |
1296 | | */ |
1297 | 0 | category_channels = &default_channel; |
1298 | 0 | } |
1299 | |
|
1300 | 0 | if (category_channels->module != ISC_LOGMODULE_DEFAULT && |
1301 | 0 | category_channels->module != module) |
1302 | 0 | { |
1303 | 0 | category_channels = ISC_LIST_NEXT(category_channels, |
1304 | 0 | link); |
1305 | 0 | continue; |
1306 | 0 | } |
1307 | | |
1308 | 0 | matched = true; |
1309 | |
|
1310 | 0 | channel = category_channels->channel; |
1311 | 0 | category_channels = ISC_LIST_NEXT(category_channels, link); |
1312 | |
|
1313 | 0 | if (!forcelog) { |
1314 | 0 | dlevel = atomic_load_acquire(&isc__lctx->debug_level); |
1315 | 0 | if (((channel->flags & ISC_LOG_DEBUGONLY) != 0) && |
1316 | 0 | dlevel == 0) |
1317 | 0 | { |
1318 | 0 | continue; |
1319 | 0 | } |
1320 | | |
1321 | 0 | if (channel->level == ISC_LOG_DYNAMIC) { |
1322 | 0 | if (dlevel < level) { |
1323 | 0 | continue; |
1324 | 0 | } |
1325 | 0 | } else if (channel->level < level) { |
1326 | 0 | continue; |
1327 | 0 | } |
1328 | 0 | } |
1329 | | |
1330 | 0 | if ((channel->flags & ISC_LOG_PRINTTIME) != 0 && |
1331 | 0 | local_time[0] == '\0') |
1332 | 0 | { |
1333 | 0 | isc_time_t isctime; |
1334 | |
|
1335 | 0 | isctime = isc_time_now(); |
1336 | |
|
1337 | 0 | isc_time_formattimestamp(&isctime, local_time, |
1338 | 0 | sizeof(local_time)); |
1339 | 0 | isc_time_formatISO8601ms(&isctime, iso8601z_string, |
1340 | 0 | sizeof(iso8601z_string)); |
1341 | 0 | isc_time_formatISO8601Lms(&isctime, iso8601l_string, |
1342 | 0 | sizeof(iso8601l_string)); |
1343 | 0 | isc_time_formatISO8601TZms(&isctime, iso8601tz_string, |
1344 | 0 | sizeof(iso8601tz_string)); |
1345 | 0 | } |
1346 | |
|
1347 | 0 | if ((channel->flags & ISC_LOG_PRINTLEVEL) != 0 && |
1348 | 0 | level_string[0] == '\0') |
1349 | 0 | { |
1350 | 0 | if (level < ISC_LOG_CRITICAL) { |
1351 | 0 | snprintf(level_string, sizeof(level_string), |
1352 | 0 | "level %d: ", level); |
1353 | 0 | } else if (level > ISC_LOG_DYNAMIC) { |
1354 | 0 | snprintf(level_string, sizeof(level_string), |
1355 | 0 | "%s %d: ", log_level_strings[0], |
1356 | 0 | level); |
1357 | 0 | } else { |
1358 | 0 | snprintf(level_string, sizeof(level_string), |
1359 | 0 | "%s: ", log_level_strings[-level]); |
1360 | 0 | } |
1361 | 0 | } |
1362 | | |
1363 | | /* |
1364 | | * Only format the message once. |
1365 | | */ |
1366 | 0 | if (isc__lctx->buffer[0] == '\0') { |
1367 | 0 | (void)vsnprintf(isc__lctx->buffer, |
1368 | 0 | sizeof(isc__lctx->buffer), format, |
1369 | 0 | args); |
1370 | 0 | } |
1371 | |
|
1372 | 0 | utc = ((channel->flags & ISC_LOG_UTC) != 0); |
1373 | 0 | tzinfo = ((channel->flags & ISC_LOG_TZINFO) != 0); |
1374 | 0 | iso8601 = ((channel->flags & ISC_LOG_ISO8601) != 0); |
1375 | 0 | printtime = ((channel->flags & ISC_LOG_PRINTTIME) != 0); |
1376 | 0 | printtag = ((channel->flags & |
1377 | 0 | (ISC_LOG_PRINTTAG | ISC_LOG_PRINTPREFIX)) != 0 && |
1378 | 0 | lcfg->tag != NULL); |
1379 | 0 | printcolon = ((channel->flags & ISC_LOG_PRINTTAG) != 0 && |
1380 | 0 | lcfg->tag != NULL); |
1381 | 0 | printcategory = ((channel->flags & ISC_LOG_PRINTCATEGORY) != 0); |
1382 | 0 | printmodule = ((channel->flags & ISC_LOG_PRINTMODULE) != 0); |
1383 | 0 | printlevel = ((channel->flags & ISC_LOG_PRINTLEVEL) != 0); |
1384 | 0 | buffered = ((channel->flags & ISC_LOG_BUFFERED) != 0); |
1385 | |
|
1386 | 0 | if (printtime) { |
1387 | 0 | if (iso8601) { |
1388 | 0 | if (utc) { |
1389 | 0 | time_string = iso8601z_string; |
1390 | 0 | } else if (tzinfo) { |
1391 | 0 | time_string = iso8601tz_string; |
1392 | 0 | } else { |
1393 | 0 | time_string = iso8601l_string; |
1394 | 0 | } |
1395 | 0 | } else { |
1396 | 0 | time_string = local_time; |
1397 | 0 | } |
1398 | 0 | } else { |
1399 | 0 | time_string = ""; |
1400 | 0 | } |
1401 | |
|
1402 | 0 | switch (channel->type) { |
1403 | 0 | case ISC_LOG_TOFILE: |
1404 | 0 | if (FILE_MAXREACHED(channel)) { |
1405 | | /* |
1406 | | * If the file can be rolled, OR |
1407 | | * If the file no longer exists, OR |
1408 | | * If the file is less than the maximum |
1409 | | * size, (such as if it had been renamed |
1410 | | * and a new one touched, or it was |
1411 | | * truncated in place) |
1412 | | * ... then close it to trigger |
1413 | | * reopening. |
1414 | | */ |
1415 | 0 | if (FILE_VERSIONS(channel) != |
1416 | 0 | ISC_LOG_ROLLNEVER || |
1417 | 0 | (stat(FILE_NAME(channel), &statbuf) != 0 && |
1418 | 0 | errno == ENOENT) || |
1419 | 0 | statbuf.st_size < FILE_MAXSIZE(channel)) |
1420 | 0 | { |
1421 | 0 | if (FILE_STREAM(channel) != NULL) { |
1422 | 0 | (void)fclose( |
1423 | 0 | FILE_STREAM(channel)); |
1424 | 0 | FILE_STREAM(channel) = NULL; |
1425 | 0 | } |
1426 | 0 | FILE_MAXREACHED(channel) = false; |
1427 | 0 | } else { |
1428 | | /* |
1429 | | * Eh, skip it. |
1430 | | */ |
1431 | 0 | break; |
1432 | 0 | } |
1433 | 0 | } |
1434 | | |
1435 | 0 | if (FILE_STREAM(channel) == NULL) { |
1436 | 0 | result = isc_log_open(channel); |
1437 | 0 | if (result != ISC_R_SUCCESS && |
1438 | 0 | result != ISC_R_MAXSIZE && |
1439 | 0 | (channel->flags & ISC_LOG_OPENERR) == 0) |
1440 | 0 | { |
1441 | 0 | syslog(LOG_ERR, |
1442 | 0 | "isc_log_open '%s' " |
1443 | 0 | "failed: %s", |
1444 | 0 | FILE_NAME(channel), |
1445 | 0 | isc_result_totext(result)); |
1446 | 0 | channel->flags |= ISC_LOG_OPENERR; |
1447 | 0 | } |
1448 | 0 | if (result != ISC_R_SUCCESS) { |
1449 | 0 | break; |
1450 | 0 | } |
1451 | 0 | channel->flags &= ~ISC_LOG_OPENERR; |
1452 | 0 | } |
1453 | 0 | FALLTHROUGH; |
1454 | |
|
1455 | 0 | case ISC_LOG_TOFILEDESC: |
1456 | 0 | fprintf(FILE_STREAM(channel), "%s%s%s%s%s%s%s%s%s%s\n", |
1457 | 0 | printtime ? time_string : "", |
1458 | 0 | printtime ? " " : "", printtag ? lcfg->tag : "", |
1459 | 0 | printcolon ? ": " : "", |
1460 | 0 | printcategory ? categories_description[category] |
1461 | 0 | : "", |
1462 | 0 | printcategory ? ": " : "", |
1463 | 0 | printmodule ? modules_description[module] : "", |
1464 | 0 | printmodule ? ": " : "", |
1465 | 0 | printlevel ? level_string : "", |
1466 | 0 | isc__lctx->buffer); |
1467 | |
|
1468 | 0 | if (!buffered) { |
1469 | 0 | fflush(FILE_STREAM(channel)); |
1470 | 0 | } |
1471 | | |
1472 | | /* |
1473 | | * If the file now exceeds its maximum size |
1474 | | * threshold, note it so that it will not be |
1475 | | * logged to any more. |
1476 | | */ |
1477 | 0 | if (FILE_MAXSIZE(channel) > 0) { |
1478 | 0 | INSIST(channel->type == ISC_LOG_TOFILE); |
1479 | | |
1480 | | /* XXXDCL NT fstat/fileno */ |
1481 | | /* XXXDCL complain if fstat fails? */ |
1482 | 0 | if (fstat(fileno(FILE_STREAM(channel)), |
1483 | 0 | &statbuf) >= 0 && |
1484 | 0 | statbuf.st_size > FILE_MAXSIZE(channel)) |
1485 | 0 | { |
1486 | 0 | FILE_MAXREACHED(channel) = true; |
1487 | 0 | } |
1488 | 0 | } |
1489 | |
|
1490 | 0 | break; |
1491 | | |
1492 | 0 | case ISC_LOG_TOSYSLOG: |
1493 | 0 | if (level > 0) { |
1494 | 0 | syslog_level = LOG_DEBUG; |
1495 | 0 | } else if (level < ISC_LOG_CRITICAL) { |
1496 | 0 | syslog_level = LOG_CRIT; |
1497 | 0 | } else { |
1498 | 0 | syslog_level = syslog_map[-level]; |
1499 | 0 | } |
1500 | |
|
1501 | 0 | (void)syslog( |
1502 | 0 | FACILITY(channel) | syslog_level, |
1503 | 0 | "%s%s%s%s%s%s%s%s%s%s", |
1504 | 0 | printtime ? time_string : "", |
1505 | 0 | printtime ? " " : "", printtag ? lcfg->tag : "", |
1506 | 0 | printcolon ? ": " : "", |
1507 | 0 | printcategory ? categories_description[category] |
1508 | 0 | : "", |
1509 | 0 | printcategory ? ": " : "", |
1510 | 0 | printmodule ? modules_description[module] : "", |
1511 | 0 | printmodule ? ": " : "", |
1512 | 0 | printlevel ? level_string : "", |
1513 | 0 | isc__lctx->buffer); |
1514 | 0 | break; |
1515 | | |
1516 | 0 | case ISC_LOG_TONULL: |
1517 | 0 | break; |
1518 | 0 | } |
1519 | 0 | } while (1); |
1520 | | |
1521 | 0 | unlock: |
1522 | 0 | UNLOCK(&isc__lctx->lock); |
1523 | 0 | rcu_read_unlock(); |
1524 | 0 | } |
1525 | | |
1526 | | void |
1527 | 0 | isc_log_setforcelog(bool v) { |
1528 | 0 | forcelog = v; |
1529 | 0 | } |
1530 | | |
1531 | | void |
1532 | 2 | isc__log_initialize(void) { |
1533 | 2 | REQUIRE(isc__lctx == NULL); |
1534 | | |
1535 | 2 | isc_mem_t *mctx = NULL; |
1536 | | |
1537 | 2 | isc_mem_create("log", &mctx); |
1538 | 2 | isc_mem_setdebugging(mctx, 0); |
1539 | | |
1540 | 2 | isc__lctx = isc_mem_get(mctx, sizeof(*isc__lctx)); |
1541 | 2 | *isc__lctx = (isc_log_t){ |
1542 | 2 | .magic = LCTX_MAGIC, |
1543 | 2 | .mctx = mctx, /* implicit attach */ |
1544 | 2 | }; |
1545 | | |
1546 | 2 | isc_mutex_init(&isc__lctx->lock); |
1547 | | |
1548 | | /* Create default logging configuration */ |
1549 | 2 | isc_logconfig_t *lcfg = NULL; |
1550 | 2 | isc_logconfig_create(&lcfg); |
1551 | | |
1552 | 2 | atomic_init(&isc__lctx->highest_level, lcfg->highest_level); |
1553 | 2 | atomic_init(&isc__lctx->dynamic, lcfg->dynamic); |
1554 | | |
1555 | 2 | isc__lctx->logconfig = lcfg; |
1556 | 2 | } |
1557 | | |
1558 | | void |
1559 | 0 | isc__log_shutdown(void) { |
1560 | | /* |
1561 | | * There is a data race when the QP database reclaims chunks on the |
1562 | | * call_rcu threads and tries to log the number of reclaimed chunks |
1563 | | * while the server is shutting down. Work around this by adding |
1564 | | * an rcu_barrier() before shutting down the global logging context. |
1565 | | */ |
1566 | 0 | rcu_barrier(); |
1567 | |
|
1568 | 0 | REQUIRE(VALID_CONTEXT(isc__lctx)); |
1569 | |
|
1570 | 0 | isc_mem_t *mctx = isc__lctx->mctx; |
1571 | | |
1572 | | /* Stop the logging as a first thing */ |
1573 | 0 | atomic_store_release(&isc__lctx->debug_level, 0); |
1574 | 0 | atomic_store_release(&isc__lctx->highest_level, 0); |
1575 | 0 | atomic_store_release(&isc__lctx->dynamic, false); |
1576 | |
|
1577 | 0 | if (isc__lctx->logconfig != NULL) { |
1578 | 0 | isc_logconfig_destroy(&isc__lctx->logconfig); |
1579 | 0 | } |
1580 | |
|
1581 | 0 | isc_mutex_destroy(&isc__lctx->lock); |
1582 | |
|
1583 | | isc_mem_putanddetach(&mctx, isc__lctx, sizeof(*isc__lctx)); |
1584 | 0 | } |