log.c 46.4 KB
Newer Older
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
5
6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8
9
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
10
11
 */

12
/*! \file */
13
14

#include <errno.h>
15
#include <inttypes.h>
16
#include <stdbool.h>
17
18
#include <stdlib.h>
#include <limits.h>
Brian Wellington's avatar
Brian Wellington committed
19
#include <time.h>
20

Mark Andrews's avatar
Mark Andrews committed
21
#include <sys/types.h>	/* dev_t FreeBSD 2.1 */
22
23

#include <isc/dir.h>
24
#include <isc/file.h>
25
#include <isc/log.h>
26
#include <isc/magic.h>
27
#include <isc/mem.h>
Samuel Thibault's avatar
Samuel Thibault committed
28
#include <isc/platform.h>
29
#include <isc/print.h>
Mark Andrews's avatar
Mark Andrews committed
30
#include <isc/stat.h>
31
#include <isc/stdio.h>
32
#include <isc/string.h>
33
#include <isc/time.h>
34
#include <isc/util.h>
35

36
#define LCTX_MAGIC		ISC_MAGIC('L', 'c', 't', 'x')
37
#define VALID_CONTEXT(lctx)	ISC_MAGIC_VALID(lctx, LCTX_MAGIC)
38
39

#define LCFG_MAGIC		ISC_MAGIC('L', 'c', 'f', 'g')
40
#define VALID_CONFIG(lcfg)	ISC_MAGIC_VALID(lcfg, LCFG_MAGIC)
41

42
43
44
/*
 * XXXDCL make dynamic?
 */
45
46
#define LOG_BUFFER_SIZE	(8 * 1024)

47
/*!
48
49
50
 * This is the structure that holds each named channel.  A simple linked
 * list chains all of the channels together, so an individual channel is
 * found by doing strcmp()s with the names down the list.  Their should
Francis Dupont's avatar
Francis Dupont committed
51
 * be no performance penalty from this as it is expected that the number
52
53
54
55
 * of named channels will be no more than a dozen or so, and name lookups
 * from the head of the list are only done when isc_log_usechannel() is
 * called, which should also be very infrequent.
 */
56
57
typedef struct isc_logchannel isc_logchannel_t;

58
struct isc_logchannel {
59
60
61
62
63
64
	char *				name;
	unsigned int			type;
	int 				level;
	unsigned int			flags;
	isc_logdestination_t 		destination;
	ISC_LINK(isc_logchannel_t)	link;
65
66
};

67
/*!
68
 * The logchannellist structure associates categories and modules with
69
70
71
72
73
74
75
76
77
 * channels.  First the appropriate channellist is found based on the
 * category, and then each structure in the linked list is checked for
 * a matching module.  It is expected that the number of channels
 * associated with any given category will be very short, no more than
 * three or four in the more unusual cases.
 */
typedef struct isc_logchannellist isc_logchannellist_t;

struct isc_logchannellist {
David Lawrence's avatar
David Lawrence committed
78
	const isc_logmodule_t *		module;
79
80
	isc_logchannel_t *		channel;
	ISC_LINK(isc_logchannellist_t)	link;
81
82
};

83
/*!
84
85
86
87
88
89
 * This structure is used to remember messages for pruning via
 * isc_log_[v]write1().
 */
typedef struct isc_logmessage isc_logmessage_t;

struct isc_logmessage {
90
91
92
93
94
	char *				text;
	isc_time_t			time;
	ISC_LINK(isc_logmessage_t)	link;
};

95
/*!
96
97
98
99
100
 * The isc_logconfig structure is used to store the configurable information
 * about where messages are actually supposed to be sent -- the information
 * that could changed based on some configuration file, as opposed to the
 * the category/module specification of isc_log_[v]write[1] that is compiled
 * into a program, or the debug_level which is dynamic state information.
101
102
103
104
 */
struct isc_logconfig {
	unsigned int			magic;
	isc_log_t *			lctx;
105
106
	ISC_LIST(isc_logchannel_t)	channels;
	ISC_LIST(isc_logchannellist_t) *channellists;
107
108
	unsigned int			channellist_count;
	unsigned int			duplicate_interval;
109
	int				highest_level;
110
	char *				tag;
111
	bool				dynamic;
112
113
};

114
/*!
115
 * This isc_log structure provides the context for the isc_log functions.
116
 * The log context locks itself in isc_log_doit, the internal backend to
117
 * isc_log_write.  The locking is necessary both to provide exclusive access
118
 * to the buffer into which the message is formatted and to guard against
119
120
121
122
123
 * competing threads trying to write to the same syslog resource.  (On
 * some systems, such as BSD/OS, stdio is thread safe but syslog is not.)
 * Unfortunately, the lock cannot guard against a _different_ logging
 * context in the same program competing for syslog's attention.  Thus
 * There Can Be Only One, but this is not enforced.
124
 * XXXDCL enforce it?
125
126
127
128
 *
 * Note that the category and module information is not locked.
 * This is because in the usual case, only one isc_log_t is ever created
 * in a program, and the category/module registration happens only once.
129
 * XXXDCL it might be wise to add more locking overall.
130
131
 */
struct isc_log {
132
	/* Not locked. */
133
134
	unsigned int			magic;
	isc_mem_t *			mctx;
135
	isc_logcategory_t *		categories;
136
	unsigned int			category_count;
137
	isc_logmodule_t *		modules;
138
	unsigned int			module_count;
139
140
141
142
	int				debug_level;
	isc_mutex_t			lock;
	/* Locked by isc_log lock. */
	isc_logconfig_t * 		logconfig;
143
144
	char 				buffer[LOG_BUFFER_SIZE];
	ISC_LIST(isc_logmessage_t)	messages;
145
146
};

147
/*!
148
149
 * Used when ISC_LOG_PRINTLEVEL is enabled for a channel.
 */
150
static const char *log_level_strings[] = {
151
152
153
154
155
156
	"debug",
	"info",
	"notice",
	"warning",
	"error",
	"critical"
157
158
};

159
/*!
160
 * Used to convert ISC_LOG_* priorities into syslog priorities.
161
 * XXXDCL This will need modification for NT.
162
163
 */
static const int syslog_map[] = {
164
165
166
167
168
169
	LOG_DEBUG,
	LOG_INFO,
	LOG_NOTICE,
	LOG_WARNING,
	LOG_ERR,
	LOG_CRIT
170
171
};

172
/*!
173
 * When adding new categories, a corresponding ISC_LOGCATEGORY_foo
174
 * definition needs to be added to <isc/log.h>.
175
176
177
178
179
 *
 * The default category is provided so that the internal default can
 * be overridden.  Since the default is always looked up as the first
 * channellist in the log context, it must come first in isc_categories[].
 */
180
LIBISC_EXTERNAL_DATA isc_logcategory_t isc_categories[] = {
181
	{ "default", 0 },	/* "default" must come first. */
182
	{ "general", 0 },
183
184
185
	{ NULL, 0 }
};

186
187
/*!
 * See above comment for categories on LIBISC_EXTERNAL_DATA, and apply it to modules.
188
 */
189
LIBISC_EXTERNAL_DATA isc_logmodule_t isc_modules[] = {
190
	{ "socket", 0 },
191
	{ "time", 0 },
192
	{ "interface", 0 },
Michael Graff's avatar
Michael Graff committed
193
	{ "timer", 0 },
Mark Andrews's avatar
Mark Andrews committed
194
	{ "file", 0 },
Evan Hunt's avatar
Evan Hunt committed
195
	{ "netmgr", 0 },
Evan Hunt's avatar
Evan Hunt committed
196
	{ "other", 0 },
197
198
199
	{ NULL, 0 }
};

200
/*!
201
 * This essentially constant structure must be filled in at run time,
202
203
 * because its channel member is pointed to a channel that is created
 * dynamically with isc_log_createchannel.
204
 */
205
static isc_logchannellist_t default_channel;
206

207
/*!
208
209
 * libisc logs to this context.
 */
210
LIBISC_EXTERNAL_DATA isc_log_t *isc_lctx = NULL;
211

212
/*!
213
214
 * Forward declarations.
 */
215
static isc_result_t
216
assignchannel(isc_logconfig_t *lcfg, unsigned int category_id,
David Lawrence's avatar
David Lawrence committed
217
	      const isc_logmodule_t *module, isc_logchannel_t *channel);
218

219
220
221
static isc_result_t
sync_channellist(isc_logconfig_t *lcfg);

222
static isc_result_t
223
greatest_version(isc_logfile_t *file, int versions, int *greatest);
224

225
226
static void
isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category,
227
	     isc_logmodule_t *module, int level, bool write_once,
228
	     const char *format, va_list args)
Ondřej Surý's avatar
Ondřej Surý committed
229
     ISC_FORMAT_PRINTF(6, 0);
230

231
232
/*@{*/
/*!
233
234
235
 * Convenience macros.
 */

236
237
238
239
#define FACILITY(channel)	 (channel->destination.facility)
#define FILE_NAME(channel)	 (channel->destination.file.name)
#define FILE_STREAM(channel)	 (channel->destination.file.stream)
#define FILE_VERSIONS(channel)	 (channel->destination.file.versions)
240
#define FILE_SUFFIX(channel)	 (channel->destination.file.suffix)
241
242
#define FILE_MAXSIZE(channel)	 (channel->destination.file.maximum_size)
#define FILE_MAXREACHED(channel) (channel->destination.file.maximum_reached)
243

244
/*@}*/
245
246
247
248
249
250
251
252
/****
 **** Public interfaces.
 ****/

/*
 * Establish a new logging context, with default channels.
 */
isc_result_t
253
isc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp) {
254
	isc_log_t *lctx;
255
	isc_logconfig_t *lcfg = NULL;
256
	isc_result_t result;
257
258
259

	REQUIRE(mctx != NULL);
	REQUIRE(lctxp != NULL && *lctxp == NULL);
260
	REQUIRE(lcfgp == NULL || *lcfgp == NULL);
261

262
	lctx = isc_mem_get(mctx, sizeof(*lctx));
263
	if (lctx != NULL) {
264
265
		lctx->mctx = NULL;
		isc_mem_attach(mctx, &lctx->mctx);
266
		lctx->categories = NULL;
267
		lctx->category_count = 0;
268
		lctx->modules = NULL;
269
270
		lctx->module_count = 0;
		lctx->debug_level = 0;
271

272
		ISC_LIST_INIT(lctx->messages);
273

Ondřej Surý's avatar
Ondřej Surý committed
274
		isc_mutex_init(&lctx->lock);
275
276
277
278
279
280
281
282
283

		/*
		 * Normally setting the magic number is the last step done
		 * in a creation function, but a valid log context is needed
		 * by isc_log_registercategories and isc_logconfig_create.
		 * If either fails, the lctx is destroyed and not returned
		 * to the caller.
		 */
		lctx->magic = LCTX_MAGIC;
284

285
		isc_log_registercategories(lctx, isc_categories);
286
		isc_log_registermodules(lctx, isc_modules);
287
		result = isc_logconfig_create(lctx, &lcfg);
288

289
290
291
	} else
		result = ISC_R_NOMEMORY;

292
293
294
	if (result == ISC_R_SUCCESS)
		result = sync_channellist(lcfg);

295
296
297
298
299
300
	if (result == ISC_R_SUCCESS) {
		lctx->logconfig = lcfg;

		*lctxp = lctx;
		if (lcfgp != NULL)
			*lcfgp = lcfg;
301

302
303
304
	} else {
		if (lcfg != NULL)
			isc_logconfig_destroy(&lcfg);
305
306
		if (lctx != NULL)
			isc_log_destroy(&lctx);
307
	}
308
309
310
311
312
313
314
315
316

	return (result);
}

isc_result_t
isc_logconfig_create(isc_log_t *lctx, isc_logconfig_t **lcfgp) {
	isc_logconfig_t *lcfg;
	isc_logdestination_t destination;
	isc_result_t result = ISC_R_SUCCESS;
317
	int level = ISC_LOG_INFO;
318
319
320
321

	REQUIRE(lcfgp != NULL && *lcfgp == NULL);
	REQUIRE(VALID_CONTEXT(lctx));

322
	lcfg = isc_mem_get(lctx->mctx, sizeof(*lcfg));
323

324
	{
325
326
327
328
		lcfg->lctx = lctx;
		lcfg->channellists = NULL;
		lcfg->channellist_count = 0;
		lcfg->duplicate_interval = 0;
329
		lcfg->highest_level = level;
330
		lcfg->tag = NULL;
331
		lcfg->dynamic = false;
332
		ISC_LIST_INIT(lcfg->channels);
333
		lcfg->magic = LCFG_MAGIC;
334
	}
335
336
337

	/*
	 * Create the default channels:
Mark Andrews's avatar
Mark Andrews committed
338
	 *   	default_syslog, default_stderr, default_debug and null.
339
340
	 */
	if (result == ISC_R_SUCCESS) {
341
		destination.facility = LOG_DAEMON;
342
		result = isc_log_createchannel(lcfg, "default_syslog",
343
					       ISC_LOG_TOSYSLOG, level,
344
345
346
347
348
349
350
					       &destination, 0);
	}

	if (result == ISC_R_SUCCESS) {
		destination.file.stream = stderr;
		destination.file.name = NULL;
		destination.file.versions = ISC_LOG_ROLLNEVER;
351
		destination.file.suffix = isc_log_rollsuffix_increment;
352
		destination.file.maximum_size = 0;
353
		result = isc_log_createchannel(lcfg, "default_stderr",
354
					       ISC_LOG_TOFILEDESC,
355
					       level,
356
357
358
359
360
					       &destination,
					       ISC_LOG_PRINTTIME);
	}

	if (result == ISC_R_SUCCESS) {
361
362
363
364
365
366
367
		/*
		 * Set the default category's channel to default_stderr,
		 * which is at the head of the channels list because it was
		 * just created.
		 */
		default_channel.channel = ISC_LIST_HEAD(lcfg->channels);

368
369
370
		destination.file.stream = stderr;
		destination.file.name = NULL;
		destination.file.versions = ISC_LOG_ROLLNEVER;
371
		destination.file.suffix = isc_log_rollsuffix_increment;
372
		destination.file.maximum_size = 0;
373
		result = isc_log_createchannel(lcfg, "default_debug",
374
375
376
					       ISC_LOG_TOFILEDESC,
					       ISC_LOG_DYNAMIC,
					       &destination,
377
					       ISC_LOG_PRINTTIME);
378
379
380
	}

	if (result == ISC_R_SUCCESS)
381
		result = isc_log_createchannel(lcfg, "null",
382
383
384
385
386
					       ISC_LOG_TONULL,
					       ISC_LOG_DYNAMIC,
					       NULL, 0);

	if (result == ISC_R_SUCCESS)
387
388
389
390
391
392
393
394
395
396
397
		*lcfgp = lcfg;

	else
		if (lcfg != NULL)
			isc_logconfig_destroy(&lcfg);

	return (result);
}

isc_logconfig_t *
isc_logconfig_get(isc_log_t *lctx) {
398
399
400
401
	REQUIRE(VALID_CONTEXT(lctx));

	ENSURE(lctx->logconfig != NULL);

402
403
	return (lctx->logconfig);
}
404

405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
isc_result_t
isc_logconfig_use(isc_log_t *lctx, isc_logconfig_t *lcfg) {
	isc_logconfig_t *old_cfg;
	isc_result_t result;

	REQUIRE(VALID_CONTEXT(lctx));
	REQUIRE(VALID_CONFIG(lcfg));
	REQUIRE(lcfg->lctx == lctx);

	/*
	 * Ensure that lcfg->channellist_count == lctx->category_count.
	 * They won't be equal if isc_log_usechannel has not been called
	 * since any call to isc_log_registercategories.
	 */
	result = sync_channellist(lcfg);
	if (result != ISC_R_SUCCESS)
421
422
		return (result);

423
424
425
426
427
428
429
430
	LOCK(&lctx->lock);

	old_cfg = lctx->logconfig;
	lctx->logconfig = lcfg;

	UNLOCK(&lctx->lock);

	isc_logconfig_destroy(&old_cfg);
431
432
433
434
435
436
437

	return (ISC_R_SUCCESS);
}

void
isc_log_destroy(isc_log_t **lctxp) {
	isc_log_t *lctx;
438
	isc_logconfig_t *lcfg;
439
	isc_mem_t *mctx;
440
	isc_logmessage_t *message;
441
442
443
444
445
446

	REQUIRE(lctxp != NULL && VALID_CONTEXT(*lctxp));

	lctx = *lctxp;
	mctx = lctx->mctx;

447
448
449
450
451
452
	if (lctx->logconfig != NULL) {
		lcfg = lctx->logconfig;
		lctx->logconfig = NULL;
		isc_logconfig_destroy(&lcfg);
	}

453
	isc_mutex_destroy(&lctx->lock);
454
455
456
457
458
459
460
461
462
463

	while ((message = ISC_LIST_HEAD(lctx->messages)) != NULL) {
		ISC_LIST_UNLINK(lctx->messages, message, link);

		isc_mem_put(mctx, message,
			    sizeof(*message) + strlen(message->text) + 1);
	}

	lctx->buffer[0] = '\0';
	lctx->debug_level = 0;
464
	lctx->categories = NULL;
465
	lctx->category_count = 0;
466
	lctx->modules = NULL;
467
468
469
470
	lctx->module_count = 0;
	lctx->mctx = NULL;
	lctx->magic = 0;

471
	isc_mem_putanddetach(&mctx, lctx, sizeof(*lctx));
472
473
474
475
476
477
478
479

	*lctxp = NULL;
}

void
isc_logconfig_destroy(isc_logconfig_t **lcfgp) {
	isc_logconfig_t *lcfg;
	isc_mem_t *mctx;
480
481
	isc_logchannel_t *channel;
	isc_logchannellist_t *item;
David Lawrence's avatar
David Lawrence committed
482
	char *filename;
483
484
485
486
487
488
489
490
491
492
493
494
495
496
	unsigned int i;

	REQUIRE(lcfgp != NULL && VALID_CONFIG(*lcfgp));

	lcfg = *lcfgp;

	/*
	 * This function cannot be called with a logconfig that is in
	 * use by a log context.
	 */
	REQUIRE(lcfg->lctx != NULL && lcfg->lctx->logconfig != lcfg);

	mctx = lcfg->lctx->mctx;

497
498
	while ((channel = ISC_LIST_HEAD(lcfg->channels)) != NULL) {
		ISC_LIST_UNLINK(lcfg->channels, channel, link);
499
500

		if (channel->type == ISC_LOG_TOFILE) {
David Lawrence's avatar
David Lawrence committed
501
502
503
504
505
506
507
508
			/*
			 * The filename for the channel may have ultimately
			 * started its life in user-land as a const string,
			 * but in isc_log_createchannel it gets copied
			 * into writable memory and is not longer truly const.
			 */
			DE_CONST(FILE_NAME(channel), filename);
			isc_mem_free(mctx, filename);
509

David Lawrence's avatar
David Lawrence committed
510
511
			if (FILE_STREAM(channel) != NULL)
				(void)fclose(FILE_STREAM(channel));
512
513
514
515
516
517
		}

		isc_mem_free(mctx, channel->name);
		isc_mem_put(mctx, channel, sizeof(*channel));
	}

518
	for (i = 0; i < lcfg->channellist_count; i++)
519
520
521
		while ((item = ISC_LIST_HEAD(lcfg->channellists[i])) != NULL) {
			ISC_LIST_UNLINK(lcfg->channellists[i], item, link);
			isc_mem_put(mctx, item, sizeof(*item));
522
523
		}

524
525
526
527
	if (lcfg->channellist_count > 0)
		isc_mem_put(mctx, lcfg->channellists,
			    lcfg->channellist_count *
			    sizeof(ISC_LIST(isc_logchannellist_t)));
528

529
	lcfg->dynamic = false;
530
	if (lcfg->tag != NULL)
531
		isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
532
	lcfg->tag = NULL;
533
	lcfg->highest_level = 0;
534
535
	lcfg->duplicate_interval = 0;
	lcfg->magic = 0;
536

537
	isc_mem_put(mctx, lcfg, sizeof(*lcfg));
538

539
	*lcfgp = NULL;
540
541
}

542
void
543
544
545
546
isc_log_registercategories(isc_log_t *lctx, isc_logcategory_t categories[]) {
	isc_logcategory_t *catp;

	REQUIRE(VALID_CONTEXT(lctx));
547
	REQUIRE(categories != NULL && categories[0].name != NULL);
548

549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
	/*
	 * XXXDCL This somewhat sleazy situation of using the last pointer
	 * in one category array to point to the next array exists because
	 * this registration function returns void and I didn't want to have
	 * change everything that used it by making it return an isc_result_t.
	 * It would need to do that if it had to allocate memory to store
	 * pointers to each array passed in.
	 */
	if (lctx->categories == NULL)
		lctx->categories = categories;

	else {
		/*
		 * Adjust the last (NULL) pointer of the already registered
		 * categories to point to the incoming array.
		 */
565
		for (catp = lctx->categories; catp->name != NULL; )
566
			if (catp->id == UINT_MAX)
David Lawrence's avatar
David Lawrence committed
567
568
569
570
571
				/*
				 * The name pointer points to the next array.
				 * Ick.
				 */
				DE_CONST(catp->name, catp);
572
573
			else
				catp++;
574
575
576
577

		catp->name = (void *)categories;
		catp->id = UINT_MAX;
	}
578
579
580
581

	/*
	 * Update the id number of the category with its new global id.
	 */
582
583
584
585
586
587
588
589
590
	for (catp = categories; catp->name != NULL; catp++)
		catp->id = lctx->category_count++;
}

isc_logcategory_t *
isc_log_categorybyname(isc_log_t *lctx, const char *name) {
	isc_logcategory_t *catp;

	REQUIRE(VALID_CONTEXT(lctx));
591
	REQUIRE(name != NULL);
592

593
	for (catp = lctx->categories; catp->name != NULL; )
594
		if (catp->id == UINT_MAX)
David Lawrence's avatar
David Lawrence committed
595
596
597
598
599
			/*
			 * catp is neither modified nor returned to the
			 * caller, so removing its const qualifier is ok.
			 */
			DE_CONST(catp->name, catp);
600
		else {
601
			if (strcmp(catp->name, name) == 0)
David Lawrence's avatar
David Lawrence committed
602
				return (catp);
603
604
			catp++;
		}
605

606
	return (NULL);
607
608
609
610
611
612
613
}

void
isc_log_registermodules(isc_log_t *lctx, isc_logmodule_t modules[]) {
	isc_logmodule_t *modp;

	REQUIRE(VALID_CONTEXT(lctx));
614
	REQUIRE(modules != NULL && modules[0].name != NULL);
615

616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
	/*
	 * XXXDCL This somewhat sleazy situation of using the last pointer
	 * in one category array to point to the next array exists because
	 * this registration function returns void and I didn't want to have
	 * change everything that used it by making it return an isc_result_t.
	 * It would need to do that if it had to allocate memory to store
	 * pointers to each array passed in.
	 */
	if (lctx->modules == NULL)
		lctx->modules = modules;

	else {
		/*
		 * Adjust the last (NULL) pointer of the already registered
		 * modules to point to the incoming array.
		 */
632
		for (modp = lctx->modules; modp->name != NULL; )
633
			if (modp->id == UINT_MAX)
David Lawrence's avatar
David Lawrence committed
634
635
636
637
638
				/*
				 * The name pointer points to the next array.
				 * Ick.
				 */
				DE_CONST(modp->name, modp);
639
640
			else
				modp++;
641
642
643
644

		modp->name = (void *)modules;
		modp->id = UINT_MAX;
	}
645
646
647
648

	/*
	 * Update the id number of the module with its new global id.
	 */
649
650
651
652
653
654
655
656
657
	for (modp = modules; modp->name != NULL; modp++)
		modp->id = lctx->module_count++;
}

isc_logmodule_t *
isc_log_modulebyname(isc_log_t *lctx, const char *name) {
	isc_logmodule_t *modp;

	REQUIRE(VALID_CONTEXT(lctx));
658
	REQUIRE(name != NULL);
659

660
	for (modp = lctx->modules; modp->name != NULL; )
661
		if (modp->id == UINT_MAX)
David Lawrence's avatar
David Lawrence committed
662
			/*
663
			 * modp is neither modified nor returned to the
David Lawrence's avatar
David Lawrence committed
664
665
666
			 * caller, so removing its const qualifier is ok.
			 */
			DE_CONST(modp->name, modp);
667
		else {
668
			if (strcmp(modp->name, name) == 0)
David Lawrence's avatar
David Lawrence committed
669
				return (modp);
670
671
			modp++;
		}
672

673
	return (NULL);
674
675
676
}

isc_result_t
677
678
isc_log_createchannel(isc_logconfig_t *lcfg, const char *name,
		      unsigned int type, int level,
David Lawrence's avatar
David Lawrence committed
679
680
		      const isc_logdestination_t *destination,
		      unsigned int flags)
681
682
{
	isc_logchannel_t *channel;
683
	isc_mem_t *mctx;
684
	unsigned int permitted = ISC_LOG_PRINTALL | ISC_LOG_DEBUGONLY |
685
686
				 ISC_LOG_BUFFERED | ISC_LOG_ISO8601 |
				 ISC_LOG_UTC;
687

688
	REQUIRE(VALID_CONFIG(lcfg));
689
690
691
692
693
	REQUIRE(name != NULL);
	REQUIRE(type == ISC_LOG_TOSYSLOG   || type == ISC_LOG_TOFILE ||
		type == ISC_LOG_TOFILEDESC || type == ISC_LOG_TONULL);
	REQUIRE(destination != NULL || type == ISC_LOG_TONULL);
	REQUIRE(level >= ISC_LOG_CRITICAL);
694
	REQUIRE((flags & ~permitted) == 0);
695

696
	/* XXXDCL find duplicate names? */
697

698
699
	mctx = lcfg->lctx->mctx;

700
	channel = isc_mem_get(mctx, sizeof(*channel));
701

702
	channel->name = isc_mem_strdup(mctx, name);
703
704
705
706

	channel->type = type;
	channel->level = level;
	channel->flags = flags;
707
	ISC_LINK_INIT(channel, link);
708
709
710
711
712
713
714
715
716
717
718
719

	switch (type) {
	case ISC_LOG_TOSYSLOG:
		FACILITY(channel) = destination->facility;
		break;

	case ISC_LOG_TOFILE:
		/*
		 * The file name is copied because greatest_version wants
		 * to scribble on it, so it needs to be definitely in
		 * writable memory.
		 */
720
721
		FILE_NAME(channel) = isc_mem_strdup(mctx,
						    destination->file.name);
722
723
		FILE_STREAM(channel) = NULL;
		FILE_VERSIONS(channel) = destination->file.versions;
724
		FILE_SUFFIX(channel) = destination->file.suffix;
725
		FILE_MAXSIZE(channel) = destination->file.maximum_size;
726
		FILE_MAXREACHED(channel) = false;
727
728
729
730
731
732
733
		break;

	case ISC_LOG_TOFILEDESC:
		FILE_NAME(channel) = NULL;
		FILE_STREAM(channel) = destination->file.stream;
		FILE_MAXSIZE(channel) = 0;
		FILE_VERSIONS(channel) = ISC_LOG_ROLLNEVER;
734
		FILE_SUFFIX(channel) = isc_log_rollsuffix_increment;
735
736
737
738
739
740
741
		break;

	case ISC_LOG_TONULL:
		/* Nothing. */
		break;

	default:
742
		isc_mem_free(mctx, channel->name);
743
		isc_mem_put(mctx, channel, sizeof(*channel));
744
745
		return (ISC_R_UNEXPECTED);
	}
746

747
	ISC_LIST_PREPEND(lcfg->channels, channel, link);
748
749
750
751
752
753
754
755
756
757
758
759

	/*
	 * If default_stderr was redefined, make the default category
	 * point to the new default_stderr.
	 */
	if (strcmp(name, "default_stderr") == 0)
		default_channel.channel = channel;

	return (ISC_R_SUCCESS);
}

isc_result_t
760
isc_log_usechannel(isc_logconfig_t *lcfg, const char *name,
David Lawrence's avatar
David Lawrence committed
761
762
		   const isc_logcategory_t *category,
		   const isc_logmodule_t *module)
763
{
764
	isc_log_t *lctx;
765
	isc_logchannel_t *channel;
766
	isc_result_t result = ISC_R_SUCCESS;
767
768
	unsigned int i;

769
	REQUIRE(VALID_CONFIG(lcfg));
770
	REQUIRE(name != NULL);
771
772
773

	lctx = lcfg->lctx;

774
775
776
	REQUIRE(category == NULL || category->id < lctx->category_count);
	REQUIRE(module == NULL || module->id < lctx->module_count);

777
778
	for (channel = ISC_LIST_HEAD(lcfg->channels); channel != NULL;
	     channel = ISC_LIST_NEXT(channel, link))
779
780
781
782
783
784
785
		if (strcmp(name, channel->name) == 0)
			break;

	if (channel == NULL)
		return (ISC_R_NOTFOUND);

	if (category != NULL)
786
		result = assignchannel(lcfg, category->id, module, channel);
787
788
789
790
791
792
793

	else
		/*
		 * Assign to all categories.  Note that this includes
		 * the default channel.
		 */
		for (i = 0; i < lctx->category_count; i++) {
794
			result = assignchannel(lcfg, i, module, channel);
795
796
797
798
799
800
801
802
			if (result != ISC_R_SUCCESS)
				break;
		}

	return (result);
}

void
803
804
isc_log_write(isc_log_t *lctx, isc_logcategory_t *category,
	      isc_logmodule_t *module, int level, const char *format, ...)
805
806
807
808
{
	va_list args;

	/*
809
	 * Contract checking is done in isc_log_doit().
810
811
812
	 */

	va_start(args, format);
813
	isc_log_doit(lctx, category, module, level, false,
Ondřej Surý's avatar
Ondřej Surý committed
814
		     format, args);
815
816
817
	va_end(args);
}

818
void
819
isc_log_vwrite(isc_log_t *lctx, isc_logcategory_t *category,
820
821
822
823
824
825
	       isc_logmodule_t *module, int level,
	       const char *format, va_list args)
{
	/*
	 * Contract checking is done in isc_log_doit().
	 */
826
	isc_log_doit(lctx, category, module, level, false,
Ondřej Surý's avatar
Ondřej Surý committed
827
		     format, args);
828
829
830
}

void
831
832
isc_log_write1(isc_log_t *lctx, isc_logcategory_t *category,
	       isc_logmodule_t *module, int level, const char *format, ...)
833
834
835
836
837
838
839
840
{
	va_list args;

	/*
	 * Contract checking is done in isc_log_doit().
	 */

	va_start(args, format);
841
	isc_log_doit(lctx, category, module, level, true,
Ondřej Surý's avatar
Ondřej Surý committed
842
		     format, args);
843
844
845
846
	va_end(args);
}

void
847
isc_log_vwrite1(isc_log_t *lctx, isc_logcategory_t *category,
848
849
850
851
852
853
		isc_logmodule_t *module, int level,
		const char *format, va_list args)
{
	/*
	 * Contract checking is done in isc_log_doit().
	 */
854
	isc_log_doit(lctx, category, module, level, true,
Ondřej Surý's avatar
Ondřej Surý committed
855
		     format, args);
856
857
}

858
void
859
isc_log_setcontext(isc_log_t *lctx) {
860
861
862
	isc_lctx = lctx;
}

863
864
void
isc_log_setdebuglevel(isc_log_t *lctx, unsigned int level) {
865
866
	isc_logchannel_t *channel;

867
868
	REQUIRE(VALID_CONTEXT(lctx));

869
870
	LOCK(&lctx->lock);

871
	lctx->debug_level = level;
872
873
874
875
876
877
878
879
880
881
882
883
884
	/*
	 * Close ISC_LOG_DEBUGONLY channels if level is zero.
	 */
	if (lctx->debug_level == 0)
		for (channel = ISC_LIST_HEAD(lctx->logconfig->channels);
		     channel != NULL;
		     channel = ISC_LIST_NEXT(channel, link))
			if (channel->type == ISC_LOG_TOFILE &&
			    (channel->flags & ISC_LOG_DEBUGONLY) != 0 &&
			    FILE_STREAM(channel) != NULL) {
				(void)fclose(FILE_STREAM(channel));
				FILE_STREAM(channel) = NULL;
			}
885
	UNLOCK(&lctx->lock);
886
887
888
889
890
891
892
893
894
}

unsigned int
isc_log_getdebuglevel(isc_log_t *lctx) {
	REQUIRE(VALID_CONTEXT(lctx));

	return (lctx->debug_level);
}

895
void
896
897
isc_log_setduplicateinterval(isc_logconfig_t *lcfg, unsigned int interval) {
	REQUIRE(VALID_CONFIG(lcfg));
898

899
	lcfg->duplicate_interval = interval;
900
901
902
}

unsigned int
903
904
isc_log_getduplicateinterval(isc_logconfig_t *lcfg) {
	REQUIRE(VALID_CONTEXT(lcfg));
905

906
	return (lcfg->duplicate_interval);
907
}
908

David Lawrence's avatar
David Lawrence committed
909
910
isc_result_t
isc_log_settag(isc_logconfig_t *lcfg, const char *tag) {
911
	REQUIRE(VALID_CONFIG(lcfg));
912

David Lawrence's avatar
David Lawrence committed
913
	if (tag != NULL && *tag != '\0') {
914
915
		if (lcfg->tag != NULL)
			isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
David Lawrence's avatar
David Lawrence committed
916
917
918
919
920
		lcfg->tag = isc_mem_strdup(lcfg->lctx->mctx, tag);

	} else {
		if (lcfg->tag != NULL)
			isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
921
		lcfg->tag = NULL;
David Lawrence's avatar
David Lawrence committed
922
923
924
	}

	return (ISC_R_SUCCESS);
925
926
927
928
929
930
931
932
933
}

char *
isc_log_gettag(isc_logconfig_t *lcfg) {
	REQUIRE(VALID_CONFIG(lcfg));

	return (lcfg->tag);
}

934
935
936
/* XXXDCL NT  -- This interface will assuredly be changing. */
void
isc_log_opensyslog(const char *tag, int options, int facility) {
937
	(void)openlog(tag, options, facility);
938
939
940
941
942
943
944
945
}

void
isc_log_closefilelogs(isc_log_t *lctx) {
	isc_logchannel_t *channel;

	REQUIRE(VALID_CONTEXT(lctx));

946
	LOCK(&lctx->lock);
947
948
949
	for (channel = ISC_LIST_HEAD(lctx->logconfig->channels);
	     channel != NULL;
	     channel = ISC_LIST_NEXT(channel, link))
950

951
952
953
954
955
		if (channel->type == ISC_LOG_TOFILE &&
		    FILE_STREAM(channel) != NULL) {
			(void)fclose(FILE_STREAM(channel));
			FILE_STREAM(channel) = NULL;
		}
956
	UNLOCK(&lctx->lock);
957
958
959
960
961
962
}

/****
 **** Internal functions
 ****/

963
static isc_result_t
964
assignchannel(isc_logconfig_t *lcfg, unsigned int category_id,
David Lawrence's avatar
David Lawrence committed
965
	      const isc_logmodule_t *module, isc_logchannel_t *channel)
966
967
{
	isc_logchannellist_t *new_item;
968
969
970
971
972
973
	isc_log_t *lctx;
	isc_result_t result;

	REQUIRE(VALID_CONFIG(lcfg));

	lctx = lcfg->lctx;
974
975
976
977
978

	REQUIRE(category_id < lctx->category_count);
	REQUIRE(module == NULL || module->id < lctx->module_count);
	REQUIRE(channel != NULL);

979
980
981
982
983
984
985
	/*
	 * Ensure lcfg->channellist_count == lctx->category_count.
	 */
	result = sync_channellist(lcfg);
	if (result != ISC_R_SUCCESS)
		return (result);

986
	new_item = isc_mem_get(lctx->mctx, sizeof(*new_item));
987
988
989

	new_item->channel = channel;
	new_item->module = module;
990
	ISC_LIST_INITANDPREPEND(lcfg->channellists[category_id],
991
			       new_item, link);
992

993
994
995
996
997
	/*
	 * Remember the highest logging level set by any channel in the
	 * logging config, so isc_log_doit() can quickly return if the
	 * message is too high to be logged by any channel.
	 */
998
	if (channel->type != ISC_LOG_TONULL) {
999
1000
1001
		if (lcfg->highest_level < channel->level)
			lcfg->highest_level = channel->level;
		if (channel->level == ISC_LOG_DYNAMIC)
1002
			lcfg->dynamic = true;
1003
1004
	}

1005
1006
1007
	return (ISC_R_SUCCESS);
}

1008
1009
1010
1011
/*
 * This would ideally be part of isc_log_registercategories(), except then
 * that function would have to return isc_result_t instead of void.
 */
1012
1013
static isc_result_t
sync_channellist(isc_logconfig_t *lcfg) {
David Lawrence's avatar
David Lawrence committed
1014
	unsigned int bytes;
1015
	isc_log_t *lctx;
1016
	void *lists;
1017

1018
1019
1020
1021
1022
1023
1024
1025
1026
	REQUIRE(VALID_CONFIG(lcfg));

	lctx = lcfg->lctx;

	REQUIRE(lctx->category_count != 0);

	if (lctx->category_count == lcfg->channellist_count)
		return (ISC_R_SUCCESS);

1027
	bytes = lctx->category_count * sizeof(ISC_LIST(isc_logchannellist_t));
1028

1029
	lists = isc_mem_get(lctx->mctx, bytes);
1030
1031
1032
1033
1034

	memset(lists, 0, bytes);

	if (lcfg->channellist_count != 0) {
		bytes = lcfg->channellist_count *
1035
			sizeof(ISC_LIST(isc_logchannellist_t));
1036
		memmove(lists, lcfg->channellists, bytes);
1037
1038
1039
1040
1041
		isc_mem_put(lctx->mctx, lcfg->channellists, bytes);
	}

	lcfg->channellists = lists;
	lcfg->channellist_count = lctx->category_count;
1042