1258945Sroberto/*
2280849Scy * Copyright (C) 2004-2007, 2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3258945Sroberto * Copyright (C) 1999-2003  Internet Software Consortium.
4258945Sroberto *
5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any
6258945Sroberto * purpose with or without fee is hereby granted, provided that the above
7258945Sroberto * copyright notice and this permission notice appear in all copies.
8258945Sroberto *
9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11258945Sroberto * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15258945Sroberto * PERFORMANCE OF THIS SOFTWARE.
16258945Sroberto */
17258945Sroberto
18280849Scy/* $Id$ */
19258945Sroberto
20258945Sroberto/*! \file
21258945Sroberto * \author  Principal Authors: DCL */
22258945Sroberto
23258945Sroberto#include <config.h>
24258945Sroberto
25258945Sroberto#include <errno.h>
26258945Sroberto#include <stdlib.h>
27258945Sroberto#include <limits.h>
28258945Sroberto#include <time.h>
29258945Sroberto
30258945Sroberto#include <sys/types.h>	/* dev_t FreeBSD 2.1 */
31258945Sroberto
32258945Sroberto#include <isc/dir.h>
33258945Sroberto#include <isc/file.h>
34258945Sroberto#include <isc/log.h>
35258945Sroberto#include <isc/magic.h>
36258945Sroberto#include <isc/mem.h>
37258945Sroberto#include <isc/msgs.h>
38258945Sroberto#include <isc/print.h>
39258945Sroberto#include <isc/stat.h>
40258945Sroberto#include <isc/stdio.h>
41258945Sroberto#include <isc/string.h>
42258945Sroberto#include <isc/time.h>
43258945Sroberto#include <isc/util.h>
44280849Scy#include "ntp_stdlib.h"		/* NTP change for strlcpy, strlcat */
45258945Sroberto
46258945Sroberto#define LCTX_MAGIC		ISC_MAGIC('L', 'c', 't', 'x')
47258945Sroberto#define VALID_CONTEXT(lctx)	ISC_MAGIC_VALID(lctx, LCTX_MAGIC)
48258945Sroberto
49258945Sroberto#define LCFG_MAGIC		ISC_MAGIC('L', 'c', 'f', 'g')
50258945Sroberto#define VALID_CONFIG(lcfg)	ISC_MAGIC_VALID(lcfg, LCFG_MAGIC)
51258945Sroberto
52258945Sroberto/*
53258945Sroberto * XXXDCL make dynamic?
54258945Sroberto */
55258945Sroberto#define LOG_BUFFER_SIZE	(8 * 1024)
56258945Sroberto
57258945Sroberto#ifndef PATH_MAX
58258945Sroberto#define PATH_MAX 1024	/* AIX and others don't define this. */
59258945Sroberto#endif
60258945Sroberto
61258945Sroberto/*!
62258945Sroberto * This is the structure that holds each named channel.  A simple linked
63258945Sroberto * list chains all of the channels together, so an individual channel is
64258945Sroberto * found by doing strcmp()s with the names down the list.  Their should
65258945Sroberto * be no performance penalty from this as it is expected that the number
66258945Sroberto * of named channels will be no more than a dozen or so, and name lookups
67258945Sroberto * from the head of the list are only done when isc_log_usechannel() is
68258945Sroberto * called, which should also be very infrequent.
69258945Sroberto */
70258945Srobertotypedef struct isc_logchannel isc_logchannel_t;
71258945Sroberto
72258945Srobertostruct isc_logchannel {
73258945Sroberto	char *				name;
74258945Sroberto	unsigned int			type;
75258945Sroberto	int 				level;
76258945Sroberto	unsigned int			flags;
77258945Sroberto	isc_logdestination_t 		destination;
78258945Sroberto	ISC_LINK(isc_logchannel_t)	link;
79258945Sroberto};
80258945Sroberto
81258945Sroberto/*!
82258945Sroberto * The logchannellist structure associates categories and modules with
83258945Sroberto * channels.  First the appropriate channellist is found based on the
84258945Sroberto * category, and then each structure in the linked list is checked for
85258945Sroberto * a matching module.  It is expected that the number of channels
86258945Sroberto * associated with any given category will be very short, no more than
87258945Sroberto * three or four in the more unusual cases.
88258945Sroberto */
89258945Srobertotypedef struct isc_logchannellist isc_logchannellist_t;
90258945Sroberto
91258945Srobertostruct isc_logchannellist {
92258945Sroberto	const isc_logmodule_t *		module;
93258945Sroberto	isc_logchannel_t *		channel;
94258945Sroberto	ISC_LINK(isc_logchannellist_t)	link;
95258945Sroberto};
96258945Sroberto
97258945Sroberto/*!
98258945Sroberto * This structure is used to remember messages for pruning via
99258945Sroberto * isc_log_[v]write1().
100258945Sroberto */
101258945Srobertotypedef struct isc_logmessage isc_logmessage_t;
102258945Sroberto
103258945Srobertostruct isc_logmessage {
104258945Sroberto	char *				text;
105258945Sroberto	isc_time_t			time;
106258945Sroberto	ISC_LINK(isc_logmessage_t)	link;
107258945Sroberto};
108258945Sroberto
109258945Sroberto/*!
110258945Sroberto * The isc_logconfig structure is used to store the configurable information
111258945Sroberto * about where messages are actually supposed to be sent -- the information
112258945Sroberto * that could changed based on some configuration file, as opposed to the
113258945Sroberto * the category/module specification of isc_log_[v]write[1] that is compiled
114258945Sroberto * into a program, or the debug_level which is dynamic state information.
115258945Sroberto */
116258945Srobertostruct isc_logconfig {
117258945Sroberto	unsigned int			magic;
118258945Sroberto	isc_log_t *			lctx;
119258945Sroberto	ISC_LIST(isc_logchannel_t)	channels;
120258945Sroberto	ISC_LIST(isc_logchannellist_t) *channellists;
121258945Sroberto	unsigned int			channellist_count;
122258945Sroberto	unsigned int			duplicate_interval;
123258945Sroberto	int				highest_level;
124258945Sroberto	char *				tag;
125258945Sroberto	isc_boolean_t			dynamic;
126258945Sroberto};
127258945Sroberto
128258945Sroberto/*!
129258945Sroberto * This isc_log structure provides the context for the isc_log functions.
130258945Sroberto * The log context locks itself in isc_log_doit, the internal backend to
131258945Sroberto * isc_log_write.  The locking is necessary both to provide exclusive access
132258945Sroberto * to the buffer into which the message is formatted and to guard against
133258945Sroberto * competing threads trying to write to the same syslog resource.  (On
134258945Sroberto * some systems, such as BSD/OS, stdio is thread safe but syslog is not.)
135258945Sroberto * Unfortunately, the lock cannot guard against a _different_ logging
136258945Sroberto * context in the same program competing for syslog's attention.  Thus
137258945Sroberto * There Can Be Only One, but this is not enforced.
138258945Sroberto * XXXDCL enforce it?
139258945Sroberto *
140258945Sroberto * Note that the category and module information is not locked.
141258945Sroberto * This is because in the usual case, only one isc_log_t is ever created
142258945Sroberto * in a program, and the category/module registration happens only once.
143258945Sroberto * XXXDCL it might be wise to add more locking overall.
144258945Sroberto */
145258945Srobertostruct isc_log {
146258945Sroberto	/* Not locked. */
147258945Sroberto	unsigned int			magic;
148258945Sroberto	isc_mem_t *			mctx;
149258945Sroberto	isc_logcategory_t *		categories;
150258945Sroberto	unsigned int			category_count;
151258945Sroberto	isc_logmodule_t *		modules;
152258945Sroberto	unsigned int			module_count;
153258945Sroberto	int				debug_level;
154258945Sroberto	isc_mutex_t			lock;
155258945Sroberto	/* Locked by isc_log lock. */
156258945Sroberto	isc_logconfig_t * 		logconfig;
157258945Sroberto	char 				buffer[LOG_BUFFER_SIZE];
158258945Sroberto	ISC_LIST(isc_logmessage_t)	messages;
159258945Sroberto};
160258945Sroberto
161258945Sroberto/*!
162258945Sroberto * Used when ISC_LOG_PRINTLEVEL is enabled for a channel.
163258945Sroberto */
164258945Srobertostatic const char *log_level_strings[] = {
165258945Sroberto	"debug",
166258945Sroberto	"info",
167258945Sroberto	"notice",
168258945Sroberto	"warning",
169258945Sroberto	"error",
170258945Sroberto	"critical"
171258945Sroberto};
172258945Sroberto
173258945Sroberto/*!
174258945Sroberto * Used to convert ISC_LOG_* priorities into syslog priorities.
175258945Sroberto * XXXDCL This will need modification for NT.
176258945Sroberto */
177258945Srobertostatic const int syslog_map[] = {
178258945Sroberto	LOG_DEBUG,
179258945Sroberto	LOG_INFO,
180258945Sroberto	LOG_NOTICE,
181258945Sroberto	LOG_WARNING,
182258945Sroberto	LOG_ERR,
183258945Sroberto	LOG_CRIT
184258945Sroberto};
185258945Sroberto
186258945Sroberto/*!
187258945Sroberto * When adding new categories, a corresponding ISC_LOGCATEGORY_foo
188258945Sroberto * definition needs to be added to <isc/log.h>.
189258945Sroberto *
190258945Sroberto * The default category is provided so that the internal default can
191258945Sroberto * be overridden.  Since the default is always looked up as the first
192258945Sroberto * channellist in the log context, it must come first in isc_categories[].
193258945Sroberto */
194258945SrobertoLIBISC_EXTERNAL_DATA isc_logcategory_t isc_categories[] = {
195258945Sroberto	{ "default", 0 },	/* "default" must come first. */
196258945Sroberto	{ "general", 0 },
197258945Sroberto	{ NULL, 0 }
198258945Sroberto};
199258945Sroberto
200258945Sroberto/*!
201258945Sroberto * See above comment for categories on LIBISC_EXTERNAL_DATA, and apply it to modules.
202258945Sroberto */
203258945SrobertoLIBISC_EXTERNAL_DATA isc_logmodule_t isc_modules[] = {
204258945Sroberto	{ "socket", 0 },
205258945Sroberto	{ "time", 0 },
206258945Sroberto	{ "interface", 0 },
207258945Sroberto	{ "timer", 0 },
208258945Sroberto	{ "file", 0 },
209258945Sroberto	{ NULL, 0 }
210258945Sroberto};
211258945Sroberto
212258945Sroberto/*!
213258945Sroberto * This essentially constant structure must be filled in at run time,
214258945Sroberto * because its channel member is pointed to a channel that is created
215258945Sroberto * dynamically with isc_log_createchannel.
216258945Sroberto */
217258945Srobertostatic isc_logchannellist_t default_channel;
218258945Sroberto
219258945Sroberto/*!
220258945Sroberto * libisc logs to this context.
221258945Sroberto */
222258945SrobertoLIBISC_EXTERNAL_DATA isc_log_t *isc_lctx = NULL;
223258945Sroberto
224258945Sroberto/*!
225258945Sroberto * Forward declarations.
226258945Sroberto */
227258945Srobertostatic isc_result_t
228258945Srobertoassignchannel(isc_logconfig_t *lcfg, unsigned int category_id,
229258945Sroberto	      const isc_logmodule_t *module, isc_logchannel_t *channel);
230258945Sroberto
231258945Srobertostatic isc_result_t
232258945Srobertosync_channellist(isc_logconfig_t *lcfg);
233258945Sroberto
234258945Srobertostatic isc_result_t
235258945Srobertogreatest_version(isc_logchannel_t *channel, int *greatest);
236258945Sroberto
237258945Srobertostatic isc_result_t
238258945Srobertoroll_log(isc_logchannel_t *channel);
239258945Sroberto
240258945Srobertostatic void
241258945Srobertoisc_log_doit(isc_log_t *lctx, isc_logcategory_t *category,
242258945Sroberto	     isc_logmodule_t *module, int level, isc_boolean_t write_once,
243258945Sroberto	     isc_msgcat_t *msgcat, int msgset, int msg,
244258945Sroberto	     const char *format, va_list args)
245258945Sroberto     ISC_FORMAT_PRINTF(9, 0);
246258945Sroberto
247258945Sroberto/*@{*/
248258945Sroberto/*!
249258945Sroberto * Convenience macros.
250258945Sroberto */
251258945Sroberto
252258945Sroberto#define FACILITY(channel)	 (channel->destination.facility)
253258945Sroberto#define FILE_NAME(channel)	 (channel->destination.file.name)
254258945Sroberto#define FILE_STREAM(channel)	 (channel->destination.file.stream)
255258945Sroberto#define FILE_VERSIONS(channel)	 (channel->destination.file.versions)
256258945Sroberto#define FILE_MAXSIZE(channel)	 (channel->destination.file.maximum_size)
257258945Sroberto#define FILE_MAXREACHED(channel) (channel->destination.file.maximum_reached)
258258945Sroberto
259258945Sroberto/*@}*/
260258945Sroberto/****
261258945Sroberto **** Public interfaces.
262258945Sroberto ****/
263258945Sroberto
264258945Sroberto/*
265258945Sroberto * Establish a new logging context, with default channels.
266258945Sroberto */
267258945Srobertoisc_result_t
268258945Srobertoisc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp) {
269258945Sroberto	isc_log_t *lctx;
270258945Sroberto	isc_logconfig_t *lcfg = NULL;
271258945Sroberto	isc_result_t result;
272258945Sroberto
273258945Sroberto	REQUIRE(mctx != NULL);
274258945Sroberto	REQUIRE(lctxp != NULL && *lctxp == NULL);
275258945Sroberto	REQUIRE(lcfgp == NULL || *lcfgp == NULL);
276258945Sroberto
277258945Sroberto	lctx = isc_mem_get(mctx, sizeof(*lctx));
278258945Sroberto	if (lctx != NULL) {
279258945Sroberto		lctx->mctx = mctx;
280258945Sroberto		lctx->categories = NULL;
281258945Sroberto		lctx->category_count = 0;
282258945Sroberto		lctx->modules = NULL;
283258945Sroberto		lctx->module_count = 0;
284258945Sroberto		lctx->debug_level = 0;
285258945Sroberto
286258945Sroberto		ISC_LIST_INIT(lctx->messages);
287258945Sroberto
288258945Sroberto		result = isc_mutex_init(&lctx->lock);
289258945Sroberto		if (result != ISC_R_SUCCESS) {
290258945Sroberto			isc_mem_put(mctx, lctx, sizeof(*lctx));
291258945Sroberto			return (result);
292258945Sroberto		}
293258945Sroberto
294258945Sroberto		/*
295258945Sroberto		 * Normally setting the magic number is the last step done
296258945Sroberto		 * in a creation function, but a valid log context is needed
297258945Sroberto		 * by isc_log_registercategories and isc_logconfig_create.
298258945Sroberto		 * If either fails, the lctx is destroyed and not returned
299258945Sroberto		 * to the caller.
300258945Sroberto		 */
301258945Sroberto		lctx->magic = LCTX_MAGIC;
302258945Sroberto
303258945Sroberto		isc_log_registercategories(lctx, isc_categories);
304258945Sroberto		isc_log_registermodules(lctx, isc_modules);
305258945Sroberto		result = isc_logconfig_create(lctx, &lcfg);
306258945Sroberto
307258945Sroberto	} else
308258945Sroberto		result = ISC_R_NOMEMORY;
309258945Sroberto
310258945Sroberto	if (result == ISC_R_SUCCESS)
311258945Sroberto		result = sync_channellist(lcfg);
312258945Sroberto
313258945Sroberto	if (result == ISC_R_SUCCESS) {
314258945Sroberto		lctx->logconfig = lcfg;
315258945Sroberto
316258945Sroberto		*lctxp = lctx;
317258945Sroberto		if (lcfgp != NULL)
318258945Sroberto			*lcfgp = lcfg;
319258945Sroberto
320258945Sroberto	} else {
321258945Sroberto		if (lcfg != NULL)
322258945Sroberto			isc_logconfig_destroy(&lcfg);
323258945Sroberto		if (lctx != NULL)
324258945Sroberto			isc_log_destroy(&lctx);
325258945Sroberto	}
326258945Sroberto
327258945Sroberto	return (result);
328258945Sroberto}
329258945Sroberto
330258945Srobertoisc_result_t
331258945Srobertoisc_logconfig_create(isc_log_t *lctx, isc_logconfig_t **lcfgp) {
332258945Sroberto	isc_logconfig_t *lcfg;
333258945Sroberto	isc_logdestination_t destination;
334258945Sroberto	isc_result_t result = ISC_R_SUCCESS;
335258945Sroberto	int level = ISC_LOG_INFO;
336258945Sroberto
337258945Sroberto	REQUIRE(lcfgp != NULL && *lcfgp == NULL);
338258945Sroberto	REQUIRE(VALID_CONTEXT(lctx));
339258945Sroberto
340258945Sroberto	lcfg = isc_mem_get(lctx->mctx, sizeof(*lcfg));
341258945Sroberto
342258945Sroberto	if (lcfg != NULL) {
343258945Sroberto		lcfg->lctx = lctx;
344258945Sroberto		lcfg->channellists = NULL;
345258945Sroberto		lcfg->channellist_count = 0;
346258945Sroberto		lcfg->duplicate_interval = 0;
347258945Sroberto		lcfg->highest_level = level;
348258945Sroberto		lcfg->tag = NULL;
349258945Sroberto		lcfg->dynamic = ISC_FALSE;
350258945Sroberto
351258945Sroberto		ISC_LIST_INIT(lcfg->channels);
352258945Sroberto
353258945Sroberto		/*
354258945Sroberto		 * Normally the magic number is the last thing set in the
355258945Sroberto		 * structure, but isc_log_createchannel() needs a valid
356258945Sroberto		 * config.  If the channel creation fails, the lcfg is not
357258945Sroberto		 * returned to the caller.
358258945Sroberto		 */
359258945Sroberto		lcfg->magic = LCFG_MAGIC;
360258945Sroberto
361258945Sroberto	} else
362258945Sroberto		result = ISC_R_NOMEMORY;
363258945Sroberto
364258945Sroberto	/*
365258945Sroberto	 * Create the default channels:
366258945Sroberto	 *   	default_syslog, default_stderr, default_debug and null.
367258945Sroberto	 */
368258945Sroberto	if (result == ISC_R_SUCCESS) {
369258945Sroberto		destination.facility = LOG_DAEMON;
370258945Sroberto		result = isc_log_createchannel(lcfg, "default_syslog",
371258945Sroberto					       ISC_LOG_TOSYSLOG, level,
372258945Sroberto					       &destination, 0);
373258945Sroberto	}
374258945Sroberto
375258945Sroberto	if (result == ISC_R_SUCCESS) {
376258945Sroberto		destination.file.stream = stderr;
377258945Sroberto		destination.file.name = NULL;
378258945Sroberto		destination.file.versions = ISC_LOG_ROLLNEVER;
379258945Sroberto		destination.file.maximum_size = 0;
380258945Sroberto		result = isc_log_createchannel(lcfg, "default_stderr",
381258945Sroberto					       ISC_LOG_TOFILEDESC,
382258945Sroberto					       level,
383258945Sroberto					       &destination,
384258945Sroberto					       ISC_LOG_PRINTTIME);
385258945Sroberto	}
386258945Sroberto
387258945Sroberto	if (result == ISC_R_SUCCESS) {
388258945Sroberto		/*
389258945Sroberto		 * Set the default category's channel to default_stderr,
390258945Sroberto		 * which is at the head of the channels list because it was
391258945Sroberto		 * just created.
392258945Sroberto		 */
393258945Sroberto		default_channel.channel = ISC_LIST_HEAD(lcfg->channels);
394258945Sroberto
395258945Sroberto		destination.file.stream = stderr;
396258945Sroberto		destination.file.name = NULL;
397258945Sroberto		destination.file.versions = ISC_LOG_ROLLNEVER;
398258945Sroberto		destination.file.maximum_size = 0;
399258945Sroberto		result = isc_log_createchannel(lcfg, "default_debug",
400258945Sroberto					       ISC_LOG_TOFILEDESC,
401258945Sroberto					       ISC_LOG_DYNAMIC,
402258945Sroberto					       &destination,
403258945Sroberto					       ISC_LOG_PRINTTIME);
404258945Sroberto	}
405258945Sroberto
406258945Sroberto	if (result == ISC_R_SUCCESS)
407258945Sroberto		result = isc_log_createchannel(lcfg, "null",
408258945Sroberto					       ISC_LOG_TONULL,
409258945Sroberto					       ISC_LOG_DYNAMIC,
410258945Sroberto					       NULL, 0);
411258945Sroberto
412258945Sroberto	if (result == ISC_R_SUCCESS)
413258945Sroberto		*lcfgp = lcfg;
414258945Sroberto
415258945Sroberto	else
416258945Sroberto		if (lcfg != NULL)
417258945Sroberto			isc_logconfig_destroy(&lcfg);
418258945Sroberto
419258945Sroberto	return (result);
420258945Sroberto}
421258945Sroberto
422258945Srobertoisc_logconfig_t *
423258945Srobertoisc_logconfig_get(isc_log_t *lctx) {
424258945Sroberto	REQUIRE(VALID_CONTEXT(lctx));
425258945Sroberto
426258945Sroberto	ENSURE(lctx->logconfig != NULL);
427258945Sroberto
428258945Sroberto	return (lctx->logconfig);
429258945Sroberto}
430258945Sroberto
431258945Srobertoisc_result_t
432258945Srobertoisc_logconfig_use(isc_log_t *lctx, isc_logconfig_t *lcfg) {
433258945Sroberto	isc_logconfig_t *old_cfg;
434258945Sroberto	isc_result_t result;
435258945Sroberto
436258945Sroberto	REQUIRE(VALID_CONTEXT(lctx));
437258945Sroberto	REQUIRE(VALID_CONFIG(lcfg));
438258945Sroberto	REQUIRE(lcfg->lctx == lctx);
439258945Sroberto
440258945Sroberto	/*
441258945Sroberto	 * Ensure that lcfg->channellist_count == lctx->category_count.
442258945Sroberto	 * They won't be equal if isc_log_usechannel has not been called
443258945Sroberto	 * since any call to isc_log_registercategories.
444258945Sroberto	 */
445258945Sroberto	result = sync_channellist(lcfg);
446258945Sroberto	if (result != ISC_R_SUCCESS)
447258945Sroberto		return (result);
448258945Sroberto
449258945Sroberto	LOCK(&lctx->lock);
450258945Sroberto
451258945Sroberto	old_cfg = lctx->logconfig;
452258945Sroberto	lctx->logconfig = lcfg;
453258945Sroberto
454258945Sroberto	UNLOCK(&lctx->lock);
455258945Sroberto
456258945Sroberto	isc_logconfig_destroy(&old_cfg);
457258945Sroberto
458258945Sroberto	return (ISC_R_SUCCESS);
459258945Sroberto}
460258945Sroberto
461258945Srobertovoid
462258945Srobertoisc_log_destroy(isc_log_t **lctxp) {
463258945Sroberto	isc_log_t *lctx;
464258945Sroberto	isc_logconfig_t *lcfg;
465258945Sroberto	isc_mem_t *mctx;
466258945Sroberto	isc_logmessage_t *message;
467258945Sroberto
468258945Sroberto	REQUIRE(lctxp != NULL && VALID_CONTEXT(*lctxp));
469258945Sroberto
470258945Sroberto	lctx = *lctxp;
471258945Sroberto	mctx = lctx->mctx;
472258945Sroberto
473258945Sroberto	if (lctx->logconfig != NULL) {
474258945Sroberto		lcfg = lctx->logconfig;
475258945Sroberto		lctx->logconfig = NULL;
476258945Sroberto		isc_logconfig_destroy(&lcfg);
477258945Sroberto	}
478258945Sroberto
479258945Sroberto	DESTROYLOCK(&lctx->lock);
480258945Sroberto
481258945Sroberto	while ((message = ISC_LIST_HEAD(lctx->messages)) != NULL) {
482258945Sroberto		ISC_LIST_UNLINK(lctx->messages, message, link);
483258945Sroberto
484258945Sroberto		isc_mem_put(mctx, message,
485258945Sroberto			    sizeof(*message) + strlen(message->text) + 1);
486258945Sroberto	}
487258945Sroberto
488258945Sroberto	lctx->buffer[0] = '\0';
489258945Sroberto	lctx->debug_level = 0;
490258945Sroberto	lctx->categories = NULL;
491258945Sroberto	lctx->category_count = 0;
492258945Sroberto	lctx->modules = NULL;
493258945Sroberto	lctx->module_count = 0;
494258945Sroberto	lctx->mctx = NULL;
495258945Sroberto	lctx->magic = 0;
496258945Sroberto
497258945Sroberto	isc_mem_put(mctx, lctx, sizeof(*lctx));
498258945Sroberto
499258945Sroberto	*lctxp = NULL;
500258945Sroberto}
501258945Sroberto
502258945Srobertovoid
503258945Srobertoisc_logconfig_destroy(isc_logconfig_t **lcfgp) {
504258945Sroberto	isc_logconfig_t *lcfg;
505258945Sroberto	isc_mem_t *mctx;
506258945Sroberto	isc_logchannel_t *channel;
507258945Sroberto	isc_logchannellist_t *item;
508258945Sroberto	char *filename;
509258945Sroberto	unsigned int i;
510258945Sroberto
511258945Sroberto	REQUIRE(lcfgp != NULL && VALID_CONFIG(*lcfgp));
512258945Sroberto
513258945Sroberto	lcfg = *lcfgp;
514258945Sroberto
515258945Sroberto	/*
516258945Sroberto	 * This function cannot be called with a logconfig that is in
517258945Sroberto	 * use by a log context.
518258945Sroberto	 */
519258945Sroberto	REQUIRE(lcfg->lctx != NULL && lcfg->lctx->logconfig != lcfg);
520258945Sroberto
521258945Sroberto	mctx = lcfg->lctx->mctx;
522258945Sroberto
523258945Sroberto	while ((channel = ISC_LIST_HEAD(lcfg->channels)) != NULL) {
524258945Sroberto		ISC_LIST_UNLINK(lcfg->channels, channel, link);
525258945Sroberto
526258945Sroberto		if (channel->type == ISC_LOG_TOFILE) {
527258945Sroberto			/*
528258945Sroberto			 * The filename for the channel may have ultimately
529258945Sroberto			 * started its life in user-land as a const string,
530258945Sroberto			 * but in isc_log_createchannel it gets copied
531258945Sroberto			 * into writable memory and is not longer truly const.
532258945Sroberto			 */
533258945Sroberto			DE_CONST(FILE_NAME(channel), filename);
534258945Sroberto			isc_mem_free(mctx, filename);
535258945Sroberto
536258945Sroberto			if (FILE_STREAM(channel) != NULL)
537258945Sroberto				(void)fclose(FILE_STREAM(channel));
538258945Sroberto		}
539258945Sroberto
540258945Sroberto		isc_mem_free(mctx, channel->name);
541258945Sroberto		isc_mem_put(mctx, channel, sizeof(*channel));
542258945Sroberto	}
543258945Sroberto
544258945Sroberto	for (i = 0; i < lcfg->channellist_count; i++)
545258945Sroberto		while ((item = ISC_LIST_HEAD(lcfg->channellists[i])) != NULL) {
546258945Sroberto			ISC_LIST_UNLINK(lcfg->channellists[i], item, link);
547258945Sroberto			isc_mem_put(mctx, item, sizeof(*item));
548258945Sroberto		}
549258945Sroberto
550258945Sroberto	if (lcfg->channellist_count > 0)
551258945Sroberto		isc_mem_put(mctx, lcfg->channellists,
552258945Sroberto			    lcfg->channellist_count *
553258945Sroberto			    sizeof(ISC_LIST(isc_logchannellist_t)));
554258945Sroberto
555258945Sroberto	lcfg->dynamic = ISC_FALSE;
556258945Sroberto	if (lcfg->tag != NULL)
557258945Sroberto		isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
558258945Sroberto	lcfg->tag = NULL;
559258945Sroberto	lcfg->highest_level = 0;
560258945Sroberto	lcfg->duplicate_interval = 0;
561258945Sroberto	lcfg->magic = 0;
562258945Sroberto
563258945Sroberto	isc_mem_put(mctx, lcfg, sizeof(*lcfg));
564258945Sroberto
565258945Sroberto	*lcfgp = NULL;
566258945Sroberto}
567258945Sroberto
568258945Srobertovoid
569258945Srobertoisc_log_registercategories(isc_log_t *lctx, isc_logcategory_t categories[]) {
570258945Sroberto	isc_logcategory_t *catp;
571258945Sroberto
572258945Sroberto	REQUIRE(VALID_CONTEXT(lctx));
573258945Sroberto	REQUIRE(categories != NULL && categories[0].name != NULL);
574258945Sroberto
575258945Sroberto	/*
576258945Sroberto	 * XXXDCL This somewhat sleazy situation of using the last pointer
577258945Sroberto	 * in one category array to point to the next array exists because
578258945Sroberto	 * this registration function returns void and I didn't want to have
579258945Sroberto	 * change everything that used it by making it return an isc_result_t.
580258945Sroberto	 * It would need to do that if it had to allocate memory to store
581258945Sroberto	 * pointers to each array passed in.
582258945Sroberto	 */
583258945Sroberto	if (lctx->categories == NULL)
584258945Sroberto		lctx->categories = categories;
585258945Sroberto
586258945Sroberto	else {
587258945Sroberto		/*
588258945Sroberto		 * Adjust the last (NULL) pointer of the already registered
589258945Sroberto		 * categories to point to the incoming array.
590258945Sroberto		 */
591258945Sroberto		for (catp = lctx->categories; catp->name != NULL; )
592258945Sroberto			if (catp->id == UINT_MAX)
593258945Sroberto				/*
594258945Sroberto				 * The name pointer points to the next array.
595258945Sroberto				 * Ick.
596258945Sroberto				 */
597258945Sroberto				DE_CONST(catp->name, catp);
598258945Sroberto			else
599258945Sroberto				catp++;
600258945Sroberto
601258945Sroberto		catp->name = (void *)categories;
602258945Sroberto		catp->id = UINT_MAX;
603258945Sroberto	}
604258945Sroberto
605258945Sroberto	/*
606258945Sroberto	 * Update the id number of the category with its new global id.
607258945Sroberto	 */
608258945Sroberto	for (catp = categories; catp->name != NULL; catp++)
609258945Sroberto		catp->id = lctx->category_count++;
610258945Sroberto}
611258945Sroberto
612258945Srobertoisc_logcategory_t *
613258945Srobertoisc_log_categorybyname(isc_log_t *lctx, const char *name) {
614258945Sroberto	isc_logcategory_t *catp;
615258945Sroberto
616258945Sroberto	REQUIRE(VALID_CONTEXT(lctx));
617258945Sroberto	REQUIRE(name != NULL);
618258945Sroberto
619258945Sroberto	for (catp = lctx->categories; catp->name != NULL; )
620258945Sroberto		if (catp->id == UINT_MAX)
621258945Sroberto			/*
622258945Sroberto			 * catp is neither modified nor returned to the
623258945Sroberto			 * caller, so removing its const qualifier is ok.
624258945Sroberto			 */
625258945Sroberto			DE_CONST(catp->name, catp);
626258945Sroberto		else {
627258945Sroberto			if (strcmp(catp->name, name) == 0)
628258945Sroberto				return (catp);
629258945Sroberto			catp++;
630258945Sroberto		}
631258945Sroberto
632258945Sroberto	return (NULL);
633258945Sroberto}
634258945Sroberto
635258945Srobertovoid
636258945Srobertoisc_log_registermodules(isc_log_t *lctx, isc_logmodule_t modules[]) {
637258945Sroberto	isc_logmodule_t *modp;
638258945Sroberto
639258945Sroberto	REQUIRE(VALID_CONTEXT(lctx));
640258945Sroberto	REQUIRE(modules != NULL && modules[0].name != NULL);
641258945Sroberto
642258945Sroberto	/*
643258945Sroberto	 * XXXDCL This somewhat sleazy situation of using the last pointer
644258945Sroberto	 * in one category array to point to the next array exists because
645258945Sroberto	 * this registration function returns void and I didn't want to have
646258945Sroberto	 * change everything that used it by making it return an isc_result_t.
647258945Sroberto	 * It would need to do that if it had to allocate memory to store
648258945Sroberto	 * pointers to each array passed in.
649258945Sroberto	 */
650258945Sroberto	if (lctx->modules == NULL)
651258945Sroberto		lctx->modules = modules;
652258945Sroberto
653258945Sroberto	else {
654258945Sroberto		/*
655258945Sroberto		 * Adjust the last (NULL) pointer of the already registered
656258945Sroberto		 * modules to point to the incoming array.
657258945Sroberto		 */
658258945Sroberto		for (modp = lctx->modules; modp->name != NULL; )
659258945Sroberto			if (modp->id == UINT_MAX)
660258945Sroberto				/*
661258945Sroberto				 * The name pointer points to the next array.
662258945Sroberto				 * Ick.
663258945Sroberto				 */
664258945Sroberto				DE_CONST(modp->name, modp);
665258945Sroberto			else
666258945Sroberto				modp++;
667258945Sroberto
668258945Sroberto		modp->name = (void *)modules;
669258945Sroberto		modp->id = UINT_MAX;
670258945Sroberto	}
671258945Sroberto
672258945Sroberto	/*
673258945Sroberto	 * Update the id number of the module with its new global id.
674258945Sroberto	 */
675258945Sroberto	for (modp = modules; modp->name != NULL; modp++)
676258945Sroberto		modp->id = lctx->module_count++;
677258945Sroberto}
678258945Sroberto
679258945Srobertoisc_logmodule_t *
680258945Srobertoisc_log_modulebyname(isc_log_t *lctx, const char *name) {
681258945Sroberto	isc_logmodule_t *modp;
682258945Sroberto
683258945Sroberto	REQUIRE(VALID_CONTEXT(lctx));
684258945Sroberto	REQUIRE(name != NULL);
685258945Sroberto
686258945Sroberto	for (modp = lctx->modules; modp->name != NULL; )
687258945Sroberto		if (modp->id == UINT_MAX)
688258945Sroberto			/*
689258945Sroberto			 * modp is neither modified nor returned to the
690258945Sroberto			 * caller, so removing its const qualifier is ok.
691258945Sroberto			 */
692258945Sroberto			DE_CONST(modp->name, modp);
693258945Sroberto		else {
694258945Sroberto			if (strcmp(modp->name, name) == 0)
695258945Sroberto				return (modp);
696258945Sroberto			modp++;
697258945Sroberto		}
698258945Sroberto
699258945Sroberto	return (NULL);
700258945Sroberto}
701258945Sroberto
702258945Srobertoisc_result_t
703258945Srobertoisc_log_createchannel(isc_logconfig_t *lcfg, const char *name,
704258945Sroberto		      unsigned int type, int level,
705258945Sroberto		      const isc_logdestination_t *destination,
706258945Sroberto		      unsigned int flags)
707258945Sroberto{
708258945Sroberto	isc_logchannel_t *channel;
709258945Sroberto	isc_mem_t *mctx;
710258945Sroberto
711258945Sroberto	REQUIRE(VALID_CONFIG(lcfg));
712258945Sroberto	REQUIRE(name != NULL);
713258945Sroberto	REQUIRE(type == ISC_LOG_TOSYSLOG   || type == ISC_LOG_TOFILE ||
714258945Sroberto		type == ISC_LOG_TOFILEDESC || type == ISC_LOG_TONULL);
715258945Sroberto	REQUIRE(destination != NULL || type == ISC_LOG_TONULL);
716258945Sroberto	REQUIRE(level >= ISC_LOG_CRITICAL);
717258945Sroberto	REQUIRE((flags &
718258945Sroberto		 (unsigned int)~(ISC_LOG_PRINTALL | ISC_LOG_DEBUGONLY)) == 0);
719258945Sroberto
720258945Sroberto	/* XXXDCL find duplicate names? */
721258945Sroberto
722258945Sroberto	mctx = lcfg->lctx->mctx;
723258945Sroberto
724258945Sroberto	channel = isc_mem_get(mctx, sizeof(*channel));
725258945Sroberto	if (channel == NULL)
726258945Sroberto		return (ISC_R_NOMEMORY);
727258945Sroberto
728258945Sroberto	channel->name = isc_mem_strdup(mctx, name);
729258945Sroberto	if (channel->name == NULL) {
730258945Sroberto		isc_mem_put(mctx, channel, sizeof(*channel));
731258945Sroberto		return (ISC_R_NOMEMORY);
732258945Sroberto	}
733258945Sroberto
734258945Sroberto	channel->type = type;
735258945Sroberto	channel->level = level;
736258945Sroberto	channel->flags = flags;
737258945Sroberto	ISC_LINK_INIT(channel, link);
738258945Sroberto
739258945Sroberto	switch (type) {
740258945Sroberto	case ISC_LOG_TOSYSLOG:
741258945Sroberto		FACILITY(channel) = destination->facility;
742258945Sroberto		break;
743258945Sroberto
744258945Sroberto	case ISC_LOG_TOFILE:
745258945Sroberto		/*
746258945Sroberto		 * The file name is copied because greatest_version wants
747258945Sroberto		 * to scribble on it, so it needs to be definitely in
748258945Sroberto		 * writable memory.
749258945Sroberto		 */
750258945Sroberto		FILE_NAME(channel) =
751258945Sroberto			isc_mem_strdup(mctx, destination->file.name);
752258945Sroberto		FILE_STREAM(channel) = NULL;
753258945Sroberto		FILE_VERSIONS(channel) = destination->file.versions;
754258945Sroberto		FILE_MAXSIZE(channel) = destination->file.maximum_size;
755258945Sroberto		FILE_MAXREACHED(channel) = ISC_FALSE;
756258945Sroberto		break;
757258945Sroberto
758258945Sroberto	case ISC_LOG_TOFILEDESC:
759258945Sroberto		FILE_NAME(channel) = NULL;
760258945Sroberto		FILE_STREAM(channel) = destination->file.stream;
761258945Sroberto		FILE_MAXSIZE(channel) = 0;
762258945Sroberto		FILE_VERSIONS(channel) = ISC_LOG_ROLLNEVER;
763258945Sroberto		break;
764258945Sroberto
765258945Sroberto	case ISC_LOG_TONULL:
766258945Sroberto		/* Nothing. */
767258945Sroberto		break;
768258945Sroberto
769258945Sroberto	default:
770258945Sroberto		isc_mem_put(mctx, channel->name, strlen(channel->name) + 1);
771258945Sroberto		isc_mem_put(mctx, channel, sizeof(*channel));
772258945Sroberto		return (ISC_R_UNEXPECTED);
773258945Sroberto	}
774258945Sroberto
775258945Sroberto	ISC_LIST_PREPEND(lcfg->channels, channel, link);
776258945Sroberto
777258945Sroberto	/*
778258945Sroberto	 * If default_stderr was redefined, make the default category
779258945Sroberto	 * point to the new default_stderr.
780258945Sroberto	 */
781258945Sroberto	if (strcmp(name, "default_stderr") == 0)
782258945Sroberto		default_channel.channel = channel;
783258945Sroberto
784258945Sroberto	return (ISC_R_SUCCESS);
785258945Sroberto}
786258945Sroberto
787258945Srobertoisc_result_t
788258945Srobertoisc_log_usechannel(isc_logconfig_t *lcfg, const char *name,
789258945Sroberto		   const isc_logcategory_t *category,
790258945Sroberto		   const isc_logmodule_t *module)
791258945Sroberto{
792258945Sroberto	isc_log_t *lctx;
793258945Sroberto	isc_logchannel_t *channel;
794258945Sroberto	isc_result_t result = ISC_R_SUCCESS;
795258945Sroberto	unsigned int i;
796258945Sroberto
797258945Sroberto	REQUIRE(VALID_CONFIG(lcfg));
798258945Sroberto	REQUIRE(name != NULL);
799258945Sroberto
800258945Sroberto	lctx = lcfg->lctx;
801258945Sroberto
802258945Sroberto	REQUIRE(category == NULL || category->id < lctx->category_count);
803258945Sroberto	REQUIRE(module == NULL || module->id < lctx->module_count);
804258945Sroberto
805258945Sroberto	for (channel = ISC_LIST_HEAD(lcfg->channels); channel != NULL;
806258945Sroberto	     channel = ISC_LIST_NEXT(channel, link))
807258945Sroberto		if (strcmp(name, channel->name) == 0)
808258945Sroberto			break;
809258945Sroberto
810258945Sroberto	if (channel == NULL)
811258945Sroberto		return (ISC_R_NOTFOUND);
812258945Sroberto
813258945Sroberto	if (category != NULL)
814258945Sroberto		result = assignchannel(lcfg, category->id, module, channel);
815258945Sroberto
816258945Sroberto	else
817258945Sroberto		/*
818258945Sroberto		 * Assign to all categories.  Note that this includes
819258945Sroberto		 * the default channel.
820258945Sroberto		 */
821258945Sroberto		for (i = 0; i < lctx->category_count; i++) {
822258945Sroberto			result = assignchannel(lcfg, i, module, channel);
823258945Sroberto			if (result != ISC_R_SUCCESS)
824258945Sroberto				break;
825258945Sroberto		}
826258945Sroberto
827258945Sroberto	return (result);
828258945Sroberto}
829258945Sroberto
830258945Srobertovoid
831258945Srobertoisc_log_write(isc_log_t *lctx, isc_logcategory_t *category,
832258945Sroberto	      isc_logmodule_t *module, int level, const char *format, ...)
833258945Sroberto{
834258945Sroberto	va_list args;
835258945Sroberto
836258945Sroberto	/*
837258945Sroberto	 * Contract checking is done in isc_log_doit().
838258945Sroberto	 */
839258945Sroberto
840258945Sroberto	va_start(args, format);
841258945Sroberto	isc_log_doit(lctx, category, module, level, ISC_FALSE,
842258945Sroberto		     NULL, 0, 0, format, args);
843258945Sroberto	va_end(args);
844258945Sroberto}
845258945Sroberto
846258945Srobertovoid
847258945Srobertoisc_log_vwrite(isc_log_t *lctx, isc_logcategory_t *category,
848258945Sroberto	       isc_logmodule_t *module, int level,
849258945Sroberto	       const char *format, va_list args)
850258945Sroberto{
851258945Sroberto	/*
852258945Sroberto	 * Contract checking is done in isc_log_doit().
853258945Sroberto	 */
854258945Sroberto	isc_log_doit(lctx, category, module, level, ISC_FALSE,
855258945Sroberto		     NULL, 0, 0, format, args);
856258945Sroberto}
857258945Sroberto
858258945Srobertovoid
859258945Srobertoisc_log_write1(isc_log_t *lctx, isc_logcategory_t *category,
860258945Sroberto	       isc_logmodule_t *module, int level, const char *format, ...)
861258945Sroberto{
862258945Sroberto	va_list args;
863258945Sroberto
864258945Sroberto	/*
865258945Sroberto	 * Contract checking is done in isc_log_doit().
866258945Sroberto	 */
867258945Sroberto
868258945Sroberto	va_start(args, format);
869258945Sroberto	isc_log_doit(lctx, category, module, level, ISC_TRUE,
870258945Sroberto		     NULL, 0, 0, format, args);
871258945Sroberto	va_end(args);
872258945Sroberto}
873258945Sroberto
874258945Srobertovoid
875258945Srobertoisc_log_vwrite1(isc_log_t *lctx, isc_logcategory_t *category,
876258945Sroberto		isc_logmodule_t *module, int level,
877258945Sroberto		const char *format, va_list args)
878258945Sroberto{
879258945Sroberto	/*
880258945Sroberto	 * Contract checking is done in isc_log_doit().
881258945Sroberto	 */
882258945Sroberto	isc_log_doit(lctx, category, module, level, ISC_TRUE,
883258945Sroberto		     NULL, 0, 0, format, args);
884258945Sroberto}
885258945Sroberto
886258945Srobertovoid
887258945Srobertoisc_log_iwrite(isc_log_t *lctx, isc_logcategory_t *category,
888258945Sroberto	       isc_logmodule_t *module, int level,
889258945Sroberto	       isc_msgcat_t *msgcat, int msgset, int msg,
890258945Sroberto	       const char *format, ...)
891258945Sroberto{
892258945Sroberto	va_list args;
893258945Sroberto
894258945Sroberto	/*
895258945Sroberto	 * Contract checking is done in isc_log_doit().
896258945Sroberto	 */
897258945Sroberto
898258945Sroberto	va_start(args, format);
899258945Sroberto	isc_log_doit(lctx, category, module, level, ISC_FALSE,
900258945Sroberto		     msgcat, msgset, msg, format, args);
901258945Sroberto	va_end(args);
902258945Sroberto}
903258945Sroberto
904258945Srobertovoid
905258945Srobertoisc_log_ivwrite(isc_log_t *lctx, isc_logcategory_t *category,
906258945Sroberto	       isc_logmodule_t *module, int level,
907258945Sroberto	       isc_msgcat_t *msgcat, int msgset, int msg,
908258945Sroberto	       const char *format, va_list args)
909258945Sroberto{
910258945Sroberto	/*
911258945Sroberto	 * Contract checking is done in isc_log_doit().
912258945Sroberto	 */
913258945Sroberto	isc_log_doit(lctx, category, module, level, ISC_FALSE,
914258945Sroberto		     msgcat, msgset, msg, format, args);
915258945Sroberto}
916258945Sroberto
917258945Srobertovoid
918258945Srobertoisc_log_iwrite1(isc_log_t *lctx, isc_logcategory_t *category,
919258945Sroberto		isc_logmodule_t *module, int level,
920258945Sroberto		isc_msgcat_t *msgcat, int msgset, int msg,
921258945Sroberto		const char *format, ...)
922258945Sroberto{
923258945Sroberto	va_list args;
924258945Sroberto
925258945Sroberto	/*
926258945Sroberto	 * Contract checking is done in isc_log_doit().
927258945Sroberto	 */
928258945Sroberto
929258945Sroberto	va_start(args, format);
930258945Sroberto	isc_log_doit(lctx, category, module, level, ISC_TRUE,
931258945Sroberto		     msgcat, msgset, msg, format, args);
932258945Sroberto	va_end(args);
933258945Sroberto}
934258945Sroberto
935258945Srobertovoid
936258945Srobertoisc_log_ivwrite1(isc_log_t *lctx, isc_logcategory_t *category,
937258945Sroberto		 isc_logmodule_t *module, int level,
938258945Sroberto		 isc_msgcat_t *msgcat, int msgset, int msg,
939258945Sroberto		 const char *format, va_list args)
940258945Sroberto{
941258945Sroberto	/*
942258945Sroberto	 * Contract checking is done in isc_log_doit().
943258945Sroberto	 */
944258945Sroberto	isc_log_doit(lctx, category, module, level, ISC_TRUE,
945258945Sroberto		     msgcat, msgset, msg, format, args);
946258945Sroberto}
947258945Sroberto
948258945Srobertovoid
949258945Srobertoisc_log_setcontext(isc_log_t *lctx) {
950258945Sroberto	isc_lctx = lctx;
951258945Sroberto}
952258945Sroberto
953258945Srobertovoid
954258945Srobertoisc_log_setdebuglevel(isc_log_t *lctx, unsigned int level) {
955258945Sroberto	isc_logchannel_t *channel;
956258945Sroberto
957258945Sroberto	REQUIRE(VALID_CONTEXT(lctx));
958258945Sroberto
959258945Sroberto	LOCK(&lctx->lock);
960258945Sroberto
961258945Sroberto	lctx->debug_level = level;
962258945Sroberto	/*
963258945Sroberto	 * Close ISC_LOG_DEBUGONLY channels if level is zero.
964258945Sroberto	 */
965258945Sroberto	if (lctx->debug_level == 0)
966258945Sroberto		for (channel = ISC_LIST_HEAD(lctx->logconfig->channels);
967258945Sroberto		     channel != NULL;
968258945Sroberto		     channel = ISC_LIST_NEXT(channel, link))
969258945Sroberto			if (channel->type == ISC_LOG_TOFILE &&
970258945Sroberto			    (channel->flags & ISC_LOG_DEBUGONLY) != 0 &&
971258945Sroberto			    FILE_STREAM(channel) != NULL) {
972258945Sroberto				(void)fclose(FILE_STREAM(channel));
973258945Sroberto				FILE_STREAM(channel) = NULL;
974258945Sroberto			}
975258945Sroberto	UNLOCK(&lctx->lock);
976258945Sroberto}
977258945Sroberto
978258945Srobertounsigned int
979258945Srobertoisc_log_getdebuglevel(isc_log_t *lctx) {
980258945Sroberto	REQUIRE(VALID_CONTEXT(lctx));
981258945Sroberto
982258945Sroberto	return (lctx->debug_level);
983258945Sroberto}
984258945Sroberto
985258945Srobertovoid
986258945Srobertoisc_log_setduplicateinterval(isc_logconfig_t *lcfg, unsigned int interval) {
987258945Sroberto	REQUIRE(VALID_CONFIG(lcfg));
988258945Sroberto
989258945Sroberto	lcfg->duplicate_interval = interval;
990258945Sroberto}
991258945Sroberto
992258945Srobertounsigned int
993258945Srobertoisc_log_getduplicateinterval(isc_logconfig_t *lcfg) {
994258945Sroberto	REQUIRE(VALID_CONTEXT(lcfg));
995258945Sroberto
996258945Sroberto	return (lcfg->duplicate_interval);
997258945Sroberto}
998258945Sroberto
999258945Srobertoisc_result_t
1000258945Srobertoisc_log_settag(isc_logconfig_t *lcfg, const char *tag) {
1001258945Sroberto	REQUIRE(VALID_CONFIG(lcfg));
1002258945Sroberto
1003258945Sroberto	if (tag != NULL && *tag != '\0') {
1004258945Sroberto		if (lcfg->tag != NULL)
1005258945Sroberto			isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
1006258945Sroberto		lcfg->tag = isc_mem_strdup(lcfg->lctx->mctx, tag);
1007258945Sroberto		if (lcfg->tag == NULL)
1008258945Sroberto			return (ISC_R_NOMEMORY);
1009258945Sroberto
1010258945Sroberto	} else {
1011258945Sroberto		if (lcfg->tag != NULL)
1012258945Sroberto			isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
1013258945Sroberto		lcfg->tag = NULL;
1014258945Sroberto	}
1015258945Sroberto
1016258945Sroberto	return (ISC_R_SUCCESS);
1017258945Sroberto}
1018258945Sroberto
1019258945Srobertochar *
1020258945Srobertoisc_log_gettag(isc_logconfig_t *lcfg) {
1021258945Sroberto	REQUIRE(VALID_CONFIG(lcfg));
1022258945Sroberto
1023258945Sroberto	return (lcfg->tag);
1024258945Sroberto}
1025258945Sroberto
1026258945Sroberto/* XXXDCL NT  -- This interface will assuredly be changing. */
1027258945Srobertovoid
1028258945Srobertoisc_log_opensyslog(const char *tag, int options, int facility) {
1029258945Sroberto	(void)openlog(tag, options, facility);
1030258945Sroberto}
1031258945Sroberto
1032258945Srobertovoid
1033258945Srobertoisc_log_closefilelogs(isc_log_t *lctx) {
1034258945Sroberto	isc_logchannel_t *channel;
1035258945Sroberto
1036258945Sroberto	REQUIRE(VALID_CONTEXT(lctx));
1037258945Sroberto
1038258945Sroberto	LOCK(&lctx->lock);
1039258945Sroberto	for (channel = ISC_LIST_HEAD(lctx->logconfig->channels);
1040258945Sroberto	     channel != NULL;
1041258945Sroberto	     channel = ISC_LIST_NEXT(channel, link))
1042258945Sroberto
1043258945Sroberto		if (channel->type == ISC_LOG_TOFILE &&
1044258945Sroberto		    FILE_STREAM(channel) != NULL) {
1045258945Sroberto			(void)fclose(FILE_STREAM(channel));
1046258945Sroberto			FILE_STREAM(channel) = NULL;
1047258945Sroberto		}
1048258945Sroberto	UNLOCK(&lctx->lock);
1049258945Sroberto}
1050258945Sroberto
1051258945Sroberto/****
1052258945Sroberto **** Internal functions
1053258945Sroberto ****/
1054258945Sroberto
1055258945Srobertostatic isc_result_t
1056258945Srobertoassignchannel(isc_logconfig_t *lcfg, unsigned int category_id,
1057258945Sroberto	      const isc_logmodule_t *module, isc_logchannel_t *channel)
1058258945Sroberto{
1059258945Sroberto	isc_logchannellist_t *new_item;
1060258945Sroberto	isc_log_t *lctx;
1061258945Sroberto	isc_result_t result;
1062258945Sroberto
1063258945Sroberto	REQUIRE(VALID_CONFIG(lcfg));
1064258945Sroberto
1065258945Sroberto	lctx = lcfg->lctx;
1066258945Sroberto
1067258945Sroberto	REQUIRE(category_id < lctx->category_count);
1068258945Sroberto	REQUIRE(module == NULL || module->id < lctx->module_count);
1069258945Sroberto	REQUIRE(channel != NULL);
1070258945Sroberto
1071258945Sroberto	/*
1072258945Sroberto	 * Ensure lcfg->channellist_count == lctx->category_count.
1073258945Sroberto	 */
1074258945Sroberto	result = sync_channellist(lcfg);
1075258945Sroberto	if (result != ISC_R_SUCCESS)
1076258945Sroberto		return (result);
1077258945Sroberto
1078258945Sroberto	new_item = isc_mem_get(lctx->mctx, sizeof(*new_item));
1079258945Sroberto	if (new_item == NULL)
1080258945Sroberto		return (ISC_R_NOMEMORY);
1081258945Sroberto
1082258945Sroberto	new_item->channel = channel;
1083258945Sroberto	new_item->module = module;
1084258945Sroberto	ISC_LIST_INITANDPREPEND(lcfg->channellists[category_id],
1085258945Sroberto			       new_item, link);
1086258945Sroberto
1087258945Sroberto	/*
1088258945Sroberto	 * Remember the highest logging level set by any channel in the
1089258945Sroberto	 * logging config, so isc_log_doit() can quickly return if the
1090258945Sroberto	 * message is too high to be logged by any channel.
1091258945Sroberto	 */
1092258945Sroberto	if (channel->type != ISC_LOG_TONULL) {
1093258945Sroberto		if (lcfg->highest_level < channel->level)
1094258945Sroberto			lcfg->highest_level = channel->level;
1095258945Sroberto		if (channel->level == ISC_LOG_DYNAMIC)
1096258945Sroberto			lcfg->dynamic = ISC_TRUE;
1097258945Sroberto	}
1098258945Sroberto
1099258945Sroberto	return (ISC_R_SUCCESS);
1100258945Sroberto}
1101258945Sroberto
1102258945Sroberto/*
1103258945Sroberto * This would ideally be part of isc_log_registercategories(), except then
1104258945Sroberto * that function would have to return isc_result_t instead of void.
1105258945Sroberto */
1106258945Srobertostatic isc_result_t
1107258945Srobertosync_channellist(isc_logconfig_t *lcfg) {
1108258945Sroberto	unsigned int bytes;
1109258945Sroberto	isc_log_t *lctx;
1110258945Sroberto	void *lists;
1111258945Sroberto
1112258945Sroberto	REQUIRE(VALID_CONFIG(lcfg));
1113258945Sroberto
1114258945Sroberto	lctx = lcfg->lctx;
1115258945Sroberto
1116258945Sroberto	REQUIRE(lctx->category_count != 0);
1117258945Sroberto
1118258945Sroberto	if (lctx->category_count == lcfg->channellist_count)
1119258945Sroberto		return (ISC_R_SUCCESS);
1120258945Sroberto
1121258945Sroberto	bytes = lctx->category_count * sizeof(ISC_LIST(isc_logchannellist_t));
1122258945Sroberto
1123258945Sroberto	lists = isc_mem_get(lctx->mctx, bytes);
1124258945Sroberto
1125258945Sroberto	if (lists == NULL)
1126258945Sroberto		return (ISC_R_NOMEMORY);
1127258945Sroberto
1128258945Sroberto	memset(lists, 0, bytes);
1129258945Sroberto
1130258945Sroberto	if (lcfg->channellist_count != 0) {
1131258945Sroberto		bytes = lcfg->channellist_count *
1132258945Sroberto			sizeof(ISC_LIST(isc_logchannellist_t));
1133258945Sroberto		memcpy(lists, lcfg->channellists, bytes);
1134258945Sroberto		isc_mem_put(lctx->mctx, lcfg->channellists, bytes);
1135258945Sroberto	}
1136258945Sroberto
1137258945Sroberto	lcfg->channellists = lists;
1138258945Sroberto	lcfg->channellist_count = lctx->category_count;
1139258945Sroberto
1140258945Sroberto	return (ISC_R_SUCCESS);
1141258945Sroberto}
1142258945Sroberto
1143258945Srobertostatic isc_result_t
1144258945Srobertogreatest_version(isc_logchannel_t *channel, int *greatestp) {
1145258945Sroberto	/* XXXDCL HIGHLY NT */
1146285612Sdelphij	char *basenam, *digit_end;
1147258945Sroberto	const char *dirname;
1148258945Sroberto	int version, greatest = -1;
1149293650Sglebius	size_t basenamelen;
1150258945Sroberto	isc_dir_t dir;
1151258945Sroberto	isc_result_t result;
1152258945Sroberto	char sep = '/';
1153258945Sroberto#ifdef _WIN32
1154258945Sroberto	char *basename2;
1155258945Sroberto#endif
1156258945Sroberto
1157258945Sroberto	REQUIRE(channel->type == ISC_LOG_TOFILE);
1158258945Sroberto
1159258945Sroberto	/*
1160258945Sroberto	 * It is safe to DE_CONST the file.name because it was copied
1161258945Sroberto	 * with isc_mem_strdup in isc_log_createchannel.
1162258945Sroberto	 */
1163285612Sdelphij	basenam = strrchr(FILE_NAME(channel), sep);
1164258945Sroberto#ifdef _WIN32
1165258945Sroberto	basename2 = strrchr(FILE_NAME(channel), '\\');
1166285612Sdelphij	if ((basenam != NULL && basename2 != NULL && basename2 > basenam) ||
1167285612Sdelphij	    (basenam == NULL && basename2 != NULL)) {
1168285612Sdelphij		basenam = basename2;
1169258945Sroberto		sep = '\\';
1170258945Sroberto	}
1171258945Sroberto#endif
1172285612Sdelphij	if (basenam != NULL) {
1173285612Sdelphij		*basenam++ = '\0';
1174258945Sroberto		dirname = FILE_NAME(channel);
1175258945Sroberto	} else {
1176285612Sdelphij		DE_CONST(FILE_NAME(channel), basenam);
1177258945Sroberto		dirname = ".";
1178258945Sroberto	}
1179285612Sdelphij	basenamelen = strlen(basenam);
1180258945Sroberto
1181258945Sroberto	isc_dir_init(&dir);
1182258945Sroberto	result = isc_dir_open(&dir, dirname);
1183258945Sroberto
1184258945Sroberto	/*
1185258945Sroberto	 * Replace the file separator if it was taken out.
1186258945Sroberto	 */
1187285612Sdelphij	if (basenam != FILE_NAME(channel))
1188285612Sdelphij		*(basenam - 1) = sep;
1189258945Sroberto
1190258945Sroberto	/*
1191258945Sroberto	 * Return if the directory open failed.
1192258945Sroberto	 */
1193258945Sroberto	if (result != ISC_R_SUCCESS)
1194258945Sroberto		return (result);
1195258945Sroberto
1196258945Sroberto	while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
1197258945Sroberto		if (dir.entry.length > basenamelen &&
1198285612Sdelphij		    strncmp(dir.entry.name, basenam, basenamelen) == 0 &&
1199258945Sroberto		    dir.entry.name[basenamelen] == '.') {
1200258945Sroberto
1201258945Sroberto			version = strtol(&dir.entry.name[basenamelen + 1],
1202258945Sroberto					 &digit_end, 10);
1203258945Sroberto			if (*digit_end == '\0' && version > greatest)
1204258945Sroberto				greatest = version;
1205258945Sroberto		}
1206258945Sroberto	}
1207258945Sroberto	isc_dir_close(&dir);
1208258945Sroberto
1209258945Sroberto	*greatestp = ++greatest;
1210258945Sroberto
1211258945Sroberto	return (ISC_R_SUCCESS);
1212258945Sroberto}
1213258945Sroberto
1214258945Srobertostatic isc_result_t
1215258945Srobertoroll_log(isc_logchannel_t *channel) {
1216258945Sroberto	int i, n, greatest;
1217258945Sroberto	char current[PATH_MAX + 1];
1218258945Sroberto	char new[PATH_MAX + 1];
1219258945Sroberto	const char *path;
1220258945Sroberto	isc_result_t result;
1221258945Sroberto
1222258945Sroberto	/*
1223258945Sroberto	 * Do nothing (not even excess version trimming) if ISC_LOG_ROLLNEVER
1224258945Sroberto	 * is specified.  Apparently complete external control over the log
1225258945Sroberto	 * files is desired.
1226258945Sroberto	 */
1227258945Sroberto	if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER)
1228258945Sroberto		return (ISC_R_SUCCESS);
1229258945Sroberto
1230258945Sroberto	path = FILE_NAME(channel);
1231258945Sroberto
1232258945Sroberto	/*
1233258945Sroberto	 * Set greatest_version to the greatest existing version
1234258945Sroberto	 * (not the maximum requested version).  This is 1 based even
1235258945Sroberto	 * though the file names are 0 based, so an oldest log of log.1
1236258945Sroberto	 * is a greatest_version of 2.
1237258945Sroberto	 */
1238258945Sroberto	result = greatest_version(channel, &greatest);
1239258945Sroberto	if (result != ISC_R_SUCCESS)
1240258945Sroberto		return (result);
1241258945Sroberto
1242258945Sroberto	/*
1243258945Sroberto	 * Now greatest should be set to the highest version number desired.
1244258945Sroberto	 * Since the highest number is one less than FILE_VERSIONS(channel)
1245258945Sroberto	 * when not doing infinite log rolling, greatest will need to be
1246258945Sroberto	 * decremented when it is equal to -- or greater than --
1247258945Sroberto	 * FILE_VERSIONS(channel).  When greatest is less than
1248258945Sroberto	 * FILE_VERSIONS(channel), it is already suitable for use as
1249258945Sroberto	 * the maximum version number.
1250258945Sroberto	 */
1251258945Sroberto
1252258945Sroberto	if (FILE_VERSIONS(channel) == ISC_LOG_ROLLINFINITE ||
1253258945Sroberto	    FILE_VERSIONS(channel) > greatest)
1254258945Sroberto		;		/* Do nothing. */
1255258945Sroberto	else
1256258945Sroberto		/*
1257258945Sroberto		 * When greatest is >= FILE_VERSIONS(channel), it needs to
1258258945Sroberto		 * be reduced until it is FILE_VERSIONS(channel) - 1.
1259258945Sroberto		 * Remove any excess logs on the way to that value.
1260258945Sroberto		 */
1261258945Sroberto		while (--greatest >= FILE_VERSIONS(channel)) {
1262258945Sroberto			n = snprintf(current, sizeof(current), "%s.%d",
1263258945Sroberto				     path, greatest);
1264258945Sroberto			if (n >= (int)sizeof(current) || n < 0)
1265258945Sroberto				result = ISC_R_NOSPACE;
1266258945Sroberto			else
1267258945Sroberto				result = isc_file_remove(current);
1268258945Sroberto			if (result != ISC_R_SUCCESS &&
1269258945Sroberto			    result != ISC_R_FILENOTFOUND)
1270258945Sroberto				syslog(LOG_ERR,
1271258945Sroberto				       "unable to remove log file '%s.%d': %s",
1272258945Sroberto				       path, greatest,
1273258945Sroberto				       isc_result_totext(result));
1274258945Sroberto		}
1275258945Sroberto
1276258945Sroberto	for (i = greatest; i > 0; i--) {
1277258945Sroberto		result = ISC_R_SUCCESS;
1278258945Sroberto		n = snprintf(current, sizeof(current), "%s.%d", path, i - 1);
1279258945Sroberto		if (n >= (int)sizeof(current) || n < 0)
1280258945Sroberto			result = ISC_R_NOSPACE;
1281258945Sroberto		if (result == ISC_R_SUCCESS) {
1282258945Sroberto			n = snprintf(new, sizeof(new), "%s.%d", path, i);
1283258945Sroberto			if (n >= (int)sizeof(new) || n < 0)
1284258945Sroberto				result = ISC_R_NOSPACE;
1285258945Sroberto		}
1286258945Sroberto		if (result == ISC_R_SUCCESS)
1287258945Sroberto			result = isc_file_rename(current, new);
1288258945Sroberto		if (result != ISC_R_SUCCESS &&
1289258945Sroberto		    result != ISC_R_FILENOTFOUND)
1290258945Sroberto			syslog(LOG_ERR,
1291258945Sroberto			       "unable to rename log file '%s.%d' to "
1292258945Sroberto			       "'%s.%d': %s", path, i - 1, path, i,
1293258945Sroberto			       isc_result_totext(result));
1294258945Sroberto	}
1295258945Sroberto
1296258945Sroberto	if (FILE_VERSIONS(channel) != 0) {
1297258945Sroberto		n = snprintf(new, sizeof(new), "%s.0", path);
1298258945Sroberto		if (n >= (int)sizeof(new) || n < 0)
1299258945Sroberto			result = ISC_R_NOSPACE;
1300258945Sroberto		else
1301258945Sroberto			result = isc_file_rename(path, new);
1302258945Sroberto		if (result != ISC_R_SUCCESS &&
1303258945Sroberto		    result != ISC_R_FILENOTFOUND)
1304258945Sroberto			syslog(LOG_ERR,
1305258945Sroberto			       "unable to rename log file '%s' to '%s.0': %s",
1306258945Sroberto			       path, path, isc_result_totext(result));
1307258945Sroberto	} else {
1308258945Sroberto		result = isc_file_remove(path);
1309258945Sroberto		if (result != ISC_R_SUCCESS &&
1310258945Sroberto		    result != ISC_R_FILENOTFOUND)
1311258945Sroberto			syslog(LOG_ERR, "unable to remove log file '%s': %s",
1312258945Sroberto			       path, isc_result_totext(result));
1313258945Sroberto	}
1314258945Sroberto
1315258945Sroberto	return (ISC_R_SUCCESS);
1316258945Sroberto}
1317258945Sroberto
1318258945Srobertostatic isc_result_t
1319258945Srobertoisc_log_open(isc_logchannel_t *channel) {
1320258945Sroberto	struct stat statbuf;
1321258945Sroberto	isc_boolean_t regular_file;
1322258945Sroberto	isc_boolean_t roll = ISC_FALSE;
1323258945Sroberto	isc_result_t result = ISC_R_SUCCESS;
1324258945Sroberto	const char *path;
1325258945Sroberto
1326258945Sroberto	REQUIRE(channel->type == ISC_LOG_TOFILE);
1327258945Sroberto	REQUIRE(FILE_STREAM(channel) == NULL);
1328258945Sroberto
1329258945Sroberto	path = FILE_NAME(channel);
1330258945Sroberto
1331258945Sroberto	REQUIRE(path != NULL && *path != '\0');
1332258945Sroberto
1333258945Sroberto	/*
1334258945Sroberto	 * Determine type of file; only regular files will be
1335258945Sroberto	 * version renamed, and only if the base file exists
1336258945Sroberto	 * and either has no size limit or has reached its size limit.
1337258945Sroberto	 */
1338258945Sroberto	if (stat(path, &statbuf) == 0) {
1339258945Sroberto		regular_file = S_ISREG(statbuf.st_mode) ? ISC_TRUE : ISC_FALSE;
1340258945Sroberto		/* XXXDCL if not regular_file complain? */
1341258945Sroberto		if ((FILE_MAXSIZE(channel) == 0 &&
1342258945Sroberto		     FILE_VERSIONS(channel) != ISC_LOG_ROLLNEVER) ||
1343258945Sroberto		    (FILE_MAXSIZE(channel) > 0 &&
1344258945Sroberto		     statbuf.st_size >= FILE_MAXSIZE(channel)))
1345258945Sroberto			roll = regular_file;
1346280849Scy	} else if (errno == ENOENT) {
1347258945Sroberto		regular_file = ISC_TRUE;
1348280849Scy		POST(regular_file);
1349280849Scy	} else
1350258945Sroberto		result = ISC_R_INVALIDFILE;
1351258945Sroberto
1352258945Sroberto	/*
1353258945Sroberto	 * Version control.
1354258945Sroberto	 */
1355258945Sroberto	if (result == ISC_R_SUCCESS && roll) {
1356258945Sroberto		if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER)
1357258945Sroberto			return (ISC_R_MAXSIZE);
1358258945Sroberto		result = roll_log(channel);
1359258945Sroberto		if (result != ISC_R_SUCCESS) {
1360258945Sroberto			if ((channel->flags & ISC_LOG_OPENERR) == 0) {
1361258945Sroberto				syslog(LOG_ERR,
1362258945Sroberto				       "isc_log_open: roll_log '%s' "
1363258945Sroberto				       "failed: %s",
1364258945Sroberto				       FILE_NAME(channel),
1365258945Sroberto				       isc_result_totext(result));
1366258945Sroberto				channel->flags |= ISC_LOG_OPENERR;
1367258945Sroberto			}
1368258945Sroberto			return (result);
1369258945Sroberto		}
1370258945Sroberto	}
1371258945Sroberto
1372258945Sroberto	result = isc_stdio_open(path, "a", &FILE_STREAM(channel));
1373258945Sroberto
1374258945Sroberto	return (result);
1375258945Sroberto}
1376258945Sroberto
1377258945Srobertoisc_boolean_t
1378258945Srobertoisc_log_wouldlog(isc_log_t *lctx, int level) {
1379258945Sroberto	/*
1380258945Sroberto	 * Try to avoid locking the mutex for messages which can't
1381258945Sroberto	 * possibly be logged to any channels -- primarily debugging
1382258945Sroberto	 * messages that the debug level is not high enough to print.
1383258945Sroberto	 *
1384258945Sroberto	 * If the level is (mathematically) less than or equal to the
1385258945Sroberto	 * highest_level, or if there is a dynamic channel and the level is
1386258945Sroberto	 * less than or equal to the debug level, the main loop must be
1387258945Sroberto	 * entered to see if the message should really be output.
1388258945Sroberto	 *
1389258945Sroberto	 * NOTE: this is UNLOCKED access to the logconfig.  However,
1390258945Sroberto	 * the worst thing that can happen is that a bad decision is made
1391258945Sroberto	 * about returning without logging, and that's not a big concern,
1392258945Sroberto	 * because that's a risk anyway if the logconfig is being
1393258945Sroberto	 * dynamically changed.
1394258945Sroberto	 */
1395258945Sroberto
1396258945Sroberto	if (lctx == NULL || lctx->logconfig == NULL)
1397258945Sroberto		return (ISC_FALSE);
1398258945Sroberto
1399258945Sroberto	return (ISC_TF(level <= lctx->logconfig->highest_level ||
1400258945Sroberto		       (lctx->logconfig->dynamic &&
1401258945Sroberto			level <= lctx->debug_level)));
1402258945Sroberto}
1403258945Sroberto
1404258945Srobertostatic void
1405258945Srobertoisc_log_doit(isc_log_t *lctx, isc_logcategory_t *category,
1406258945Sroberto	     isc_logmodule_t *module, int level, isc_boolean_t write_once,
1407258945Sroberto	     isc_msgcat_t *msgcat, int msgset, int msg,
1408258945Sroberto	     const char *format, va_list args)
1409258945Sroberto{
1410258945Sroberto	int syslog_level;
1411258945Sroberto	char time_string[64];
1412258945Sroberto	char level_string[24];
1413280849Scy	size_t octets;
1414258945Sroberto	const char *iformat;
1415258945Sroberto	struct stat statbuf;
1416258945Sroberto	isc_boolean_t matched = ISC_FALSE;
1417258945Sroberto	isc_boolean_t printtime, printtag;
1418258945Sroberto	isc_boolean_t printcategory, printmodule, printlevel;
1419258945Sroberto	isc_logconfig_t *lcfg;
1420258945Sroberto	isc_logchannel_t *channel;
1421258945Sroberto	isc_logchannellist_t *category_channels;
1422258945Sroberto	isc_result_t result;
1423258945Sroberto
1424258945Sroberto	REQUIRE(lctx == NULL || VALID_CONTEXT(lctx));
1425258945Sroberto	REQUIRE(category != NULL);
1426258945Sroberto	REQUIRE(module != NULL);
1427258945Sroberto	REQUIRE(level != ISC_LOG_DYNAMIC);
1428258945Sroberto	REQUIRE(format != NULL);
1429258945Sroberto
1430258945Sroberto	/*
1431258945Sroberto	 * Programs can use libraries that use this logging code without
1432258945Sroberto	 * wanting to do any logging, thus the log context is allowed to
1433258945Sroberto	 * be non-existent.
1434258945Sroberto	 */
1435258945Sroberto	if (lctx == NULL)
1436258945Sroberto		return;
1437258945Sroberto
1438258945Sroberto	REQUIRE(category->id < lctx->category_count);
1439258945Sroberto	REQUIRE(module->id < lctx->module_count);
1440258945Sroberto
1441258945Sroberto	if (! isc_log_wouldlog(lctx, level))
1442258945Sroberto		return;
1443258945Sroberto
1444258945Sroberto	if (msgcat != NULL)
1445258945Sroberto		iformat = isc_msgcat_get(msgcat, msgset, msg, format);
1446258945Sroberto	else
1447258945Sroberto		iformat = format;
1448258945Sroberto
1449258945Sroberto	time_string[0]  = '\0';
1450258945Sroberto	level_string[0] = '\0';
1451258945Sroberto
1452258945Sroberto	LOCK(&lctx->lock);
1453258945Sroberto
1454258945Sroberto	lctx->buffer[0] = '\0';
1455258945Sroberto
1456258945Sroberto	lcfg = lctx->logconfig;
1457258945Sroberto
1458258945Sroberto	category_channels = ISC_LIST_HEAD(lcfg->channellists[category->id]);
1459258945Sroberto
1460258945Sroberto	/*
1461258945Sroberto	 * XXXDCL add duplicate filtering? (To not write multiple times to
1462258945Sroberto	 * the same source via various channels).
1463258945Sroberto	 */
1464258945Sroberto	do {
1465258945Sroberto		/*
1466258945Sroberto		 * If the channel list end was reached and a match was made,
1467258945Sroberto		 * everything is finished.
1468258945Sroberto		 */
1469258945Sroberto		if (category_channels == NULL && matched)
1470258945Sroberto			break;
1471258945Sroberto
1472258945Sroberto		if (category_channels == NULL && ! matched &&
1473258945Sroberto		    category_channels != ISC_LIST_HEAD(lcfg->channellists[0]))
1474258945Sroberto			/*
1475258945Sroberto			 * No category/module pair was explicitly configured.
1476258945Sroberto			 * Try the category named "default".
1477258945Sroberto			 */
1478258945Sroberto			category_channels =
1479258945Sroberto				ISC_LIST_HEAD(lcfg->channellists[0]);
1480258945Sroberto
1481258945Sroberto		if (category_channels == NULL && ! matched)
1482258945Sroberto			/*
1483258945Sroberto			 * No matching module was explicitly configured
1484258945Sroberto			 * for the category named "default".  Use the internal
1485258945Sroberto			 * default channel.
1486258945Sroberto			 */
1487258945Sroberto			category_channels = &default_channel;
1488258945Sroberto
1489258945Sroberto		if (category_channels->module != NULL &&
1490258945Sroberto		    category_channels->module != module) {
1491258945Sroberto			category_channels = ISC_LIST_NEXT(category_channels,
1492258945Sroberto							  link);
1493258945Sroberto			continue;
1494258945Sroberto		}
1495258945Sroberto
1496258945Sroberto		matched = ISC_TRUE;
1497258945Sroberto
1498258945Sroberto		channel = category_channels->channel;
1499258945Sroberto		category_channels = ISC_LIST_NEXT(category_channels, link);
1500258945Sroberto
1501258945Sroberto		if (((channel->flags & ISC_LOG_DEBUGONLY) != 0) &&
1502258945Sroberto		    lctx->debug_level == 0)
1503258945Sroberto			continue;
1504258945Sroberto
1505258945Sroberto		if (channel->level == ISC_LOG_DYNAMIC) {
1506258945Sroberto			if (lctx->debug_level < level)
1507258945Sroberto				continue;
1508258945Sroberto		} else if (channel->level < level)
1509258945Sroberto			continue;
1510258945Sroberto
1511258945Sroberto		if ((channel->flags & ISC_LOG_PRINTTIME) != 0 &&
1512258945Sroberto		    time_string[0] == '\0') {
1513258945Sroberto			isc_time_t isctime;
1514258945Sroberto
1515258945Sroberto			TIME_NOW(&isctime);
1516258945Sroberto			isc_time_formattimestamp(&isctime, time_string,
1517258945Sroberto						 sizeof(time_string));
1518258945Sroberto		}
1519258945Sroberto
1520258945Sroberto		if ((channel->flags & ISC_LOG_PRINTLEVEL) != 0 &&
1521258945Sroberto		    level_string[0] == '\0') {
1522258945Sroberto			if (level < ISC_LOG_CRITICAL)
1523258945Sroberto				snprintf(level_string, sizeof(level_string),
1524280849Scy					 "%s %d: ",
1525258945Sroberto					 isc_msgcat_get(isc_msgcat,
1526258945Sroberto							ISC_MSGSET_LOG,
1527258945Sroberto							ISC_MSG_LEVEL,
1528280849Scy							"level"),
1529258945Sroberto					 level);
1530258945Sroberto			else if (level > ISC_LOG_DYNAMIC)
1531258945Sroberto				snprintf(level_string, sizeof(level_string),
1532258945Sroberto					 "%s %d: ", log_level_strings[0],
1533258945Sroberto					 level);
1534258945Sroberto			else
1535258945Sroberto				snprintf(level_string, sizeof(level_string),
1536258945Sroberto					 "%s: ", log_level_strings[-level]);
1537258945Sroberto		}
1538258945Sroberto
1539258945Sroberto		/*
1540258945Sroberto		 * Only format the message once.
1541258945Sroberto		 */
1542258945Sroberto		if (lctx->buffer[0] == '\0') {
1543258945Sroberto			(void)vsnprintf(lctx->buffer, sizeof(lctx->buffer),
1544258945Sroberto					iformat, args);
1545258945Sroberto
1546258945Sroberto			/*
1547258945Sroberto			 * Check for duplicates.
1548258945Sroberto			 */
1549258945Sroberto			if (write_once) {
1550258945Sroberto				isc_logmessage_t *message, *new;
1551258945Sroberto				isc_time_t oldest;
1552258945Sroberto				isc_interval_t interval;
1553258945Sroberto
1554258945Sroberto				isc_interval_set(&interval,
1555258945Sroberto						 lcfg->duplicate_interval, 0);
1556258945Sroberto
1557258945Sroberto				/*
1558258945Sroberto				 * 'oldest' is the age of the oldest messages
1559258945Sroberto				 * which fall within the duplicate_interval
1560258945Sroberto				 * range.
1561258945Sroberto				 */
1562258945Sroberto				TIME_NOW(&oldest);
1563258945Sroberto				if (isc_time_subtract(&oldest, &interval, &oldest)
1564258945Sroberto				    != ISC_R_SUCCESS)
1565258945Sroberto					/*
1566258945Sroberto					 * Can't effectively do the checking
1567258945Sroberto					 * without having a valid time.
1568258945Sroberto					 */
1569258945Sroberto					message = NULL;
1570258945Sroberto				else
1571258945Sroberto					message =ISC_LIST_HEAD(lctx->messages);
1572258945Sroberto
1573258945Sroberto				while (message != NULL) {
1574258945Sroberto					if (isc_time_compare(&message->time,
1575258945Sroberto							     &oldest) < 0) {
1576258945Sroberto						/*
1577258945Sroberto						 * This message is older
1578258945Sroberto						 * than the duplicate_interval,
1579258945Sroberto						 * so it should be dropped from
1580258945Sroberto						 * the history.
1581258945Sroberto						 *
1582258945Sroberto						 * Setting the interval to be
1583258945Sroberto						 * to be longer will obviously
1584258945Sroberto						 * not cause the expired
1585258945Sroberto						 * message to spring back into
1586258945Sroberto						 * existence.
1587258945Sroberto						 */
1588258945Sroberto						new = ISC_LIST_NEXT(message,
1589258945Sroberto								    link);
1590258945Sroberto
1591258945Sroberto						ISC_LIST_UNLINK(lctx->messages,
1592258945Sroberto								message, link);
1593258945Sroberto
1594258945Sroberto						isc_mem_put(lctx->mctx,
1595258945Sroberto							message,
1596258945Sroberto							sizeof(*message) + 1 +
1597258945Sroberto							strlen(message->text));
1598258945Sroberto
1599258945Sroberto						message = new;
1600258945Sroberto						continue;
1601258945Sroberto					}
1602258945Sroberto
1603258945Sroberto					/*
1604258945Sroberto					 * This message is in the duplicate
1605258945Sroberto					 * filtering interval ...
1606258945Sroberto					 */
1607258945Sroberto					if (strcmp(lctx->buffer, message->text)
1608258945Sroberto					    == 0) {
1609258945Sroberto						/*
1610258945Sroberto						 * ... and it is a duplicate.
1611258945Sroberto						 * Unlock the mutex and
1612258945Sroberto						 * get the hell out of Dodge.
1613258945Sroberto						 */
1614258945Sroberto						UNLOCK(&lctx->lock);
1615258945Sroberto						return;
1616258945Sroberto					}
1617258945Sroberto
1618258945Sroberto					message = ISC_LIST_NEXT(message, link);
1619258945Sroberto				}
1620258945Sroberto
1621258945Sroberto				/*
1622258945Sroberto				 * It wasn't in the duplicate interval,
1623258945Sroberto				 * so add it to the message list.
1624258945Sroberto				 */
1625280849Scy				octets = strlen(lctx->buffer) + 1;
1626258945Sroberto				new = isc_mem_get(lctx->mctx,
1627258945Sroberto						  sizeof(isc_logmessage_t) +
1628280849Scy						  octets);
1629258945Sroberto				if (new != NULL) {
1630258945Sroberto					/*
1631258945Sroberto					 * Put the text immediately after
1632258945Sroberto					 * the struct.  The strcpy is safe.
1633258945Sroberto					 */
1634258945Sroberto					new->text = (char *)(new + 1);
1635280849Scy					strlcpy(new->text, lctx->buffer, octets);
1636258945Sroberto
1637258945Sroberto					TIME_NOW(&new->time);
1638258945Sroberto
1639258945Sroberto					ISC_LIST_APPEND(lctx->messages,
1640258945Sroberto							new, link);
1641258945Sroberto				}
1642258945Sroberto			}
1643258945Sroberto		}
1644258945Sroberto
1645258945Sroberto		printtime     = ISC_TF((channel->flags & ISC_LOG_PRINTTIME)
1646258945Sroberto				       != 0);
1647258945Sroberto		printtag      = ISC_TF((channel->flags & ISC_LOG_PRINTTAG)
1648258945Sroberto				       != 0 && lcfg->tag != NULL);
1649258945Sroberto		printcategory = ISC_TF((channel->flags & ISC_LOG_PRINTCATEGORY)
1650258945Sroberto				       != 0);
1651258945Sroberto		printmodule   = ISC_TF((channel->flags & ISC_LOG_PRINTMODULE)
1652258945Sroberto				       != 0);
1653258945Sroberto		printlevel    = ISC_TF((channel->flags & ISC_LOG_PRINTLEVEL)
1654258945Sroberto				       != 0);
1655258945Sroberto
1656258945Sroberto		switch (channel->type) {
1657258945Sroberto		case ISC_LOG_TOFILE:
1658258945Sroberto			if (FILE_MAXREACHED(channel)) {
1659258945Sroberto				/*
1660258945Sroberto				 * If the file can be rolled, OR
1661258945Sroberto				 * If the file no longer exists, OR
1662258945Sroberto				 * If the file is less than the maximum size,
1663258945Sroberto				 *    (such as if it had been renamed and
1664258945Sroberto				 *     a new one touched, or it was truncated
1665258945Sroberto				 *     in place)
1666258945Sroberto				 * ... then close it to trigger reopening.
1667258945Sroberto				 */
1668258945Sroberto				if (FILE_VERSIONS(channel) !=
1669258945Sroberto				    ISC_LOG_ROLLNEVER ||
1670258945Sroberto				    (stat(FILE_NAME(channel), &statbuf) != 0 &&
1671258945Sroberto				     errno == ENOENT) ||
1672258945Sroberto				    statbuf.st_size < FILE_MAXSIZE(channel)) {
1673258945Sroberto					(void)fclose(FILE_STREAM(channel));
1674258945Sroberto					FILE_STREAM(channel) = NULL;
1675258945Sroberto					FILE_MAXREACHED(channel) = ISC_FALSE;
1676258945Sroberto				} else
1677258945Sroberto					/*
1678258945Sroberto					 * Eh, skip it.
1679258945Sroberto					 */
1680258945Sroberto					break;
1681258945Sroberto			}
1682258945Sroberto
1683258945Sroberto			if (FILE_STREAM(channel) == NULL) {
1684258945Sroberto				result = isc_log_open(channel);
1685258945Sroberto				if (result != ISC_R_SUCCESS &&
1686258945Sroberto				    result != ISC_R_MAXSIZE &&
1687258945Sroberto				    (channel->flags & ISC_LOG_OPENERR) == 0) {
1688258945Sroberto					syslog(LOG_ERR,
1689258945Sroberto					       "isc_log_open '%s' failed: %s",
1690258945Sroberto					       FILE_NAME(channel),
1691258945Sroberto					       isc_result_totext(result));
1692258945Sroberto					channel->flags |= ISC_LOG_OPENERR;
1693258945Sroberto				}
1694258945Sroberto				if (result != ISC_R_SUCCESS)
1695258945Sroberto					break;
1696258945Sroberto				channel->flags &= ~ISC_LOG_OPENERR;
1697258945Sroberto			}
1698258945Sroberto			/* FALLTHROUGH */
1699258945Sroberto
1700258945Sroberto		case ISC_LOG_TOFILEDESC:
1701258945Sroberto			fprintf(FILE_STREAM(channel), "%s%s%s%s%s%s%s%s%s%s\n",
1702258945Sroberto				printtime     ? time_string	: "",
1703258945Sroberto				printtime     ? " "		: "",
1704258945Sroberto				printtag      ? lcfg->tag	: "",
1705258945Sroberto				printtag      ? ": "		: "",
1706258945Sroberto				printcategory ? category->name	: "",
1707258945Sroberto				printcategory ? ": "		: "",
1708258945Sroberto				printmodule   ? (module != NULL ? module->name
1709258945Sroberto								: "no_module")
1710258945Sroberto								: "",
1711258945Sroberto				printmodule   ? ": "		: "",
1712258945Sroberto				printlevel    ? level_string	: "",
1713258945Sroberto				lctx->buffer);
1714258945Sroberto
1715258945Sroberto			fflush(FILE_STREAM(channel));
1716258945Sroberto
1717258945Sroberto			/*
1718258945Sroberto			 * If the file now exceeds its maximum size
1719258945Sroberto			 * threshold, note it so that it will not be logged
1720258945Sroberto			 * to any more.
1721258945Sroberto			 */
1722258945Sroberto			if (FILE_MAXSIZE(channel) > 0) {
1723258945Sroberto				INSIST(channel->type == ISC_LOG_TOFILE);
1724258945Sroberto
1725258945Sroberto				/* XXXDCL NT fstat/fileno */
1726258945Sroberto				/* XXXDCL complain if fstat fails? */
1727258945Sroberto				if (fstat(fileno(FILE_STREAM(channel)),
1728258945Sroberto					  &statbuf) >= 0 &&
1729258945Sroberto				    statbuf.st_size > FILE_MAXSIZE(channel))
1730258945Sroberto					FILE_MAXREACHED(channel) = ISC_TRUE;
1731258945Sroberto			}
1732258945Sroberto
1733258945Sroberto			break;
1734258945Sroberto
1735258945Sroberto		case ISC_LOG_TOSYSLOG:
1736258945Sroberto			if (level > 0)
1737258945Sroberto				syslog_level = LOG_DEBUG;
1738258945Sroberto			else if (level < ISC_LOG_CRITICAL)
1739258945Sroberto				syslog_level = LOG_CRIT;
1740258945Sroberto			else
1741258945Sroberto				syslog_level = syslog_map[-level];
1742258945Sroberto
1743258945Sroberto			(void)syslog(FACILITY(channel) | syslog_level,
1744258945Sroberto			       "%s%s%s%s%s%s%s%s%s%s",
1745258945Sroberto			       printtime     ? time_string	: "",
1746258945Sroberto			       printtime     ? " "		: "",
1747258945Sroberto			       printtag      ? lcfg->tag	: "",
1748258945Sroberto			       printtag      ? ": "		: "",
1749258945Sroberto			       printcategory ? category->name	: "",
1750258945Sroberto			       printcategory ? ": "		: "",
1751258945Sroberto			       printmodule   ? (module != NULL	? module->name
1752258945Sroberto								: "no_module")
1753258945Sroberto								: "",
1754258945Sroberto			       printmodule   ? ": "		: "",
1755258945Sroberto			       printlevel    ? level_string	: "",
1756258945Sroberto			       lctx->buffer);
1757258945Sroberto			break;
1758258945Sroberto
1759258945Sroberto		case ISC_LOG_TONULL:
1760258945Sroberto			break;
1761258945Sroberto
1762258945Sroberto		}
1763258945Sroberto
1764258945Sroberto	} while (1);
1765258945Sroberto
1766258945Sroberto	UNLOCK(&lctx->lock);
1767258945Sroberto}
1768