No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / libbind / dist / isc / logging.c
blob0ae4cdf697d0e56df62d8e3d9627d07cf986dd86
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1996-1999, 2001, 2003 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 #if !defined(LINT) && !defined(CODECENTER)
21 static const char rcsid[] = "Id: logging.c,v 1.9 2008/11/14 02:36:51 marka Exp";
22 #endif /* not lint */
24 #include "port_before.h"
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <limits.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <stdarg.h>
36 #include <syslog.h>
37 #include <errno.h>
38 #include <time.h>
39 #include <unistd.h>
41 #include <isc/assertions.h>
42 #include <isc/logging.h>
43 #include <isc/memcluster.h>
44 #include <isc/misc.h>
46 #include "port_after.h"
48 #include "logging_p.h"
50 static const int syslog_priority[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE,
51 LOG_WARNING, LOG_ERR, LOG_CRIT };
53 static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
54 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
56 static const char *level_text[] = {
57 "info: ", "notice: ", "warning: ", "error: ", "critical: "
60 static void
61 version_rename(log_channel chan) {
62 unsigned int ver;
63 char old_name[PATH_MAX+1];
64 char new_name[PATH_MAX+1];
66 ver = chan->out.file.versions;
67 if (ver < 1)
68 return;
69 if (ver > LOG_MAX_VERSIONS)
70 ver = LOG_MAX_VERSIONS;
72 * Need to have room for '.nn' (XXX assumes LOG_MAX_VERSIONS < 100)
74 if (strlen(chan->out.file.name) > (size_t)(PATH_MAX-3))
75 return;
76 for (ver--; ver > 0; ver--) {
77 sprintf(old_name, "%s.%d", chan->out.file.name, ver-1);
78 sprintf(new_name, "%s.%d", chan->out.file.name, ver);
79 (void)isc_movefile(old_name, new_name);
81 sprintf(new_name, "%s.0", chan->out.file.name);
82 (void)isc_movefile(chan->out.file.name, new_name);
85 FILE *
86 log_open_stream(log_channel chan) {
87 FILE *stream;
88 int fd, flags;
89 struct stat sb;
90 int regular;
92 if (chan == NULL || chan->type != log_file) {
93 errno = EINVAL;
94 return (NULL);
98 * Don't open already open streams
100 if (chan->out.file.stream != NULL)
101 return (chan->out.file.stream);
103 if (stat(chan->out.file.name, &sb) < 0) {
104 if (errno != ENOENT) {
105 syslog(LOG_ERR,
106 "log_open_stream: stat of %s failed: %s",
107 chan->out.file.name, strerror(errno));
108 chan->flags |= LOG_CHANNEL_BROKEN;
109 return (NULL);
111 regular = 1;
112 } else
113 regular = (sb.st_mode & S_IFREG);
115 if (chan->out.file.versions) {
116 if (!regular) {
117 syslog(LOG_ERR,
118 "log_open_stream: want versions but %s isn't a regular file",
119 chan->out.file.name);
120 chan->flags |= LOG_CHANNEL_BROKEN;
121 errno = EINVAL;
122 return (NULL);
126 flags = O_WRONLY|O_CREAT|O_APPEND;
128 if ((chan->flags & LOG_TRUNCATE) != 0) {
129 if (regular) {
130 (void)unlink(chan->out.file.name);
131 flags |= O_EXCL;
132 } else {
133 syslog(LOG_ERR,
134 "log_open_stream: want truncation but %s isn't a regular file",
135 chan->out.file.name);
136 chan->flags |= LOG_CHANNEL_BROKEN;
137 errno = EINVAL;
138 return (NULL);
142 fd = open(chan->out.file.name, flags,
143 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
144 if (fd < 0) {
145 syslog(LOG_ERR, "log_open_stream: open(%s) failed: %s",
146 chan->out.file.name, strerror(errno));
147 chan->flags |= LOG_CHANNEL_BROKEN;
148 return (NULL);
150 stream = fdopen(fd, "a");
151 if (stream == NULL) {
152 syslog(LOG_ERR, "log_open_stream: fdopen() failed");
153 chan->flags |= LOG_CHANNEL_BROKEN;
154 return (NULL);
156 (void) fchown(fd, chan->out.file.owner, chan->out.file.group);
158 chan->out.file.stream = stream;
159 return (stream);
163 log_close_stream(log_channel chan) {
164 FILE *stream;
166 if (chan == NULL || chan->type != log_file) {
167 errno = EINVAL;
168 return (0);
170 stream = chan->out.file.stream;
171 chan->out.file.stream = NULL;
172 if (stream != NULL && fclose(stream) == EOF)
173 return (-1);
174 return (0);
177 void
178 log_close_debug_channels(log_context lc) {
179 log_channel_list lcl;
180 int i;
182 for (i = 0; i < lc->num_categories; i++)
183 for (lcl = lc->categories[i]; lcl != NULL; lcl = lcl->next)
184 if (lcl->channel->type == log_file &&
185 lcl->channel->out.file.stream != NULL &&
186 lcl->channel->flags & LOG_REQUIRE_DEBUG)
187 (void)log_close_stream(lcl->channel);
190 FILE *
191 log_get_stream(log_channel chan) {
192 if (chan == NULL || chan->type != log_file) {
193 errno = EINVAL;
194 return (NULL);
196 return (chan->out.file.stream);
199 char *
200 log_get_filename(log_channel chan) {
201 if (chan == NULL || chan->type != log_file) {
202 errno = EINVAL;
203 return (NULL);
205 return (chan->out.file.name);
209 log_check_channel(log_context lc, int level, log_channel chan) {
210 int debugging, chan_level;
212 REQUIRE(lc != NULL);
214 debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0);
217 * If not debugging, short circuit debugging messages very early.
219 if (level > 0 && !debugging)
220 return (0);
222 if ((chan->flags & (LOG_CHANNEL_BROKEN|LOG_CHANNEL_OFF)) != 0)
223 return (0);
225 /* Some channels only log when debugging is on. */
226 if ((chan->flags & LOG_REQUIRE_DEBUG) && !debugging)
227 return (0);
229 /* Some channels use the global level. */
230 if ((chan->flags & LOG_USE_CONTEXT_LEVEL) != 0) {
231 chan_level = lc->level;
232 } else
233 chan_level = chan->level;
235 if (level > chan_level)
236 return (0);
238 return (1);
242 log_check(log_context lc, int category, int level) {
243 log_channel_list lcl;
244 int debugging;
246 REQUIRE(lc != NULL);
248 debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0);
251 * If not debugging, short circuit debugging messages very early.
253 if (level > 0 && !debugging)
254 return (0);
256 if (category < 0 || category > lc->num_categories)
257 category = 0; /*%< use default */
258 lcl = lc->categories[category];
259 if (lcl == NULL) {
260 category = 0;
261 lcl = lc->categories[0];
264 for ( /* nothing */; lcl != NULL; lcl = lcl->next) {
265 if (log_check_channel(lc, level, lcl->channel))
266 return (1);
268 return (0);
271 void
272 log_vwrite(log_context lc, int category, int level, const char *format,
273 va_list args) {
274 log_channel_list lcl;
275 int pri, debugging, did_vsprintf = 0;
276 int original_category;
277 FILE *stream;
278 log_channel chan;
279 struct timeval tv;
280 struct tm *local_tm;
281 #ifdef HAVE_TIME_R
282 struct tm tm_tmp;
283 #endif
284 time_t tt;
285 const char *category_name;
286 const char *level_str;
287 char time_buf[256];
288 char level_buf[256];
290 REQUIRE(lc != NULL);
292 debugging = (lc->flags & LOG_OPTION_DEBUG);
295 * If not debugging, short circuit debugging messages very early.
297 if (level > 0 && !debugging)
298 return;
300 if (category < 0 || category > lc->num_categories)
301 category = 0; /*%< use default */
302 original_category = category;
303 lcl = lc->categories[category];
304 if (lcl == NULL) {
305 category = 0;
306 lcl = lc->categories[0];
310 * Get the current time and format it.
312 time_buf[0]='\0';
313 if (gettimeofday(&tv, NULL) < 0) {
314 syslog(LOG_INFO, "gettimeofday failed in log_vwrite()");
315 } else {
316 tt = tv.tv_sec;
317 #ifdef HAVE_TIME_R
318 local_tm = localtime_r(&tt, &tm_tmp);
319 #else
320 local_tm = localtime(&tt);
321 #endif
322 if (local_tm != NULL) {
323 sprintf(time_buf, "%02d-%s-%4d %02d:%02d:%02d.%03ld ",
324 local_tm->tm_mday, months[local_tm->tm_mon],
325 local_tm->tm_year+1900, local_tm->tm_hour,
326 local_tm->tm_min, local_tm->tm_sec,
327 (long)tv.tv_usec/1000);
332 * Make a string representation of the current category and level
335 if (lc->category_names != NULL &&
336 lc->category_names[original_category] != NULL)
337 category_name = lc->category_names[original_category];
338 else
339 category_name = "";
341 if (level >= log_critical) {
342 if (level >= 0) {
343 sprintf(level_buf, "debug %d: ", level);
344 level_str = level_buf;
345 } else
346 level_str = level_text[-level-1];
347 } else {
348 sprintf(level_buf, "level %d: ", level);
349 level_str = level_buf;
353 * Write the message to channels.
355 for ( /* nothing */; lcl != NULL; lcl = lcl->next) {
356 chan = lcl->channel;
358 if (!log_check_channel(lc, level, chan))
359 continue;
361 if (!did_vsprintf) {
362 (void)vsprintf(lc->buffer, format, args);
363 if (strlen(lc->buffer) > (size_t)LOG_BUFFER_SIZE) {
364 syslog(LOG_CRIT,
365 "memory overrun in log_vwrite()");
366 exit(1);
368 did_vsprintf = 1;
371 switch (chan->type) {
372 case log_syslog:
373 if (level >= log_critical)
374 pri = (level >= 0) ? 0 : -level;
375 else
376 pri = -log_critical;
377 syslog(chan->out.facility|syslog_priority[pri],
378 "%s%s%s%s",
379 (chan->flags & LOG_TIMESTAMP) ? time_buf : "",
380 (chan->flags & LOG_PRINT_CATEGORY) ?
381 category_name : "",
382 (chan->flags & LOG_PRINT_LEVEL) ?
383 level_str : "",
384 lc->buffer);
385 break;
386 case log_file:
387 stream = chan->out.file.stream;
388 if (stream == NULL) {
389 stream = log_open_stream(chan);
390 if (stream == NULL)
391 break;
393 if (chan->out.file.max_size != ULONG_MAX) {
394 long pos;
396 pos = ftell(stream);
397 if (pos >= 0 &&
398 (unsigned long)pos >
399 chan->out.file.max_size) {
401 * try to roll over the log files,
402 * ignoring all all return codes
403 * except the open (we don't want
404 * to write any more anyway)
406 log_close_stream(chan);
407 version_rename(chan);
408 stream = log_open_stream(chan);
409 if (stream == NULL)
410 break;
413 fprintf(stream, "%s%s%s%s\n",
414 (chan->flags & LOG_TIMESTAMP) ? time_buf : "",
415 (chan->flags & LOG_PRINT_CATEGORY) ?
416 category_name : "",
417 (chan->flags & LOG_PRINT_LEVEL) ?
418 level_str : "",
419 lc->buffer);
420 fflush(stream);
421 break;
422 case log_null:
423 break;
424 default:
425 syslog(LOG_ERR,
426 "unknown channel type in log_vwrite()");
431 void
432 log_write(log_context lc, int category, int level, const char *format, ...) {
433 va_list args;
435 va_start(args, format);
436 log_vwrite(lc, category, level, format, args);
437 va_end(args);
441 * Functions to create, set, or destroy contexts
445 log_new_context(int num_categories, char **category_names, log_context *lc) {
446 log_context nlc;
448 nlc = memget(sizeof (struct log_context));
449 if (nlc == NULL) {
450 errno = ENOMEM;
451 return (-1);
453 nlc->num_categories = num_categories;
454 nlc->category_names = category_names;
455 nlc->categories = memget(num_categories * sizeof (log_channel_list));
456 if (nlc->categories == NULL) {
457 memput(nlc, sizeof (struct log_context));
458 errno = ENOMEM;
459 return (-1);
461 memset(nlc->categories, '\0',
462 num_categories * sizeof (log_channel_list));
463 nlc->flags = 0U;
464 nlc->level = 0;
465 *lc = nlc;
466 return (0);
469 void
470 log_free_context(log_context lc) {
471 log_channel_list lcl, lcl_next;
472 log_channel chan;
473 int i;
475 REQUIRE(lc != NULL);
477 for (i = 0; i < lc->num_categories; i++)
478 for (lcl = lc->categories[i]; lcl != NULL; lcl = lcl_next) {
479 lcl_next = lcl->next;
480 chan = lcl->channel;
481 (void)log_free_channel(chan);
482 memput(lcl, sizeof (struct log_channel_list));
484 memput(lc->categories,
485 lc->num_categories * sizeof (log_channel_list));
486 memput(lc, sizeof (struct log_context));
490 log_add_channel(log_context lc, int category, log_channel chan) {
491 log_channel_list lcl;
493 if (lc == NULL || category < 0 || category >= lc->num_categories) {
494 errno = EINVAL;
495 return (-1);
498 lcl = memget(sizeof (struct log_channel_list));
499 if (lcl == NULL) {
500 errno = ENOMEM;
501 return(-1);
503 lcl->channel = chan;
504 lcl->next = lc->categories[category];
505 lc->categories[category] = lcl;
506 chan->references++;
507 return (0);
511 log_remove_channel(log_context lc, int category, log_channel chan) {
512 log_channel_list lcl, prev_lcl, next_lcl;
513 int found = 0;
515 if (lc == NULL || category < 0 || category >= lc->num_categories) {
516 errno = EINVAL;
517 return (-1);
520 for (prev_lcl = NULL, lcl = lc->categories[category];
521 lcl != NULL;
522 lcl = next_lcl) {
523 next_lcl = lcl->next;
524 if (lcl->channel == chan) {
525 log_free_channel(chan);
526 if (prev_lcl != NULL)
527 prev_lcl->next = next_lcl;
528 else
529 lc->categories[category] = next_lcl;
530 memput(lcl, sizeof (struct log_channel_list));
532 * We just set found instead of returning because
533 * the channel might be on the list more than once.
535 found = 1;
536 } else
537 prev_lcl = lcl;
539 if (!found) {
540 errno = ENOENT;
541 return (-1);
543 return (0);
547 log_option(log_context lc, int option, int value) {
548 if (lc == NULL) {
549 errno = EINVAL;
550 return (-1);
552 switch (option) {
553 case LOG_OPTION_DEBUG:
554 if (value)
555 lc->flags |= option;
556 else
557 lc->flags &= ~option;
558 break;
559 case LOG_OPTION_LEVEL:
560 lc->level = value;
561 break;
562 default:
563 errno = EINVAL;
564 return (-1);
566 return (0);
570 log_category_is_active(log_context lc, int category) {
571 if (lc == NULL) {
572 errno = EINVAL;
573 return (-1);
575 if (category >= 0 && category < lc->num_categories &&
576 lc->categories[category] != NULL)
577 return (1);
578 return (0);
581 log_channel
582 log_new_syslog_channel(unsigned int flags, int level, int facility) {
583 log_channel chan;
585 chan = memget(sizeof (struct log_channel));
586 if (chan == NULL) {
587 errno = ENOMEM;
588 return (NULL);
590 chan->type = log_syslog;
591 chan->flags = flags;
592 chan->level = level;
593 chan->out.facility = facility;
594 chan->references = 0;
595 return (chan);
598 log_channel
599 log_new_file_channel(unsigned int flags, int level,
600 const char *name, FILE *stream, unsigned int versions,
601 unsigned long max_size) {
602 log_channel chan;
604 chan = memget(sizeof (struct log_channel));
605 if (chan == NULL) {
606 errno = ENOMEM;
607 return (NULL);
609 chan->type = log_file;
610 chan->flags = flags;
611 chan->level = level;
612 if (name != NULL) {
613 size_t len;
615 len = strlen(name);
617 * Quantize length to a multiple of 256. There's space for the
618 * NUL, since if len is a multiple of 256, the size chosen will
619 * be the next multiple.
621 chan->out.file.name_size = ((len / 256) + 1) * 256;
622 chan->out.file.name = memget(chan->out.file.name_size);
623 if (chan->out.file.name == NULL) {
624 memput(chan, sizeof (struct log_channel));
625 errno = ENOMEM;
626 return (NULL);
628 /* This is safe. */
629 strcpy(chan->out.file.name, name);
630 } else {
631 chan->out.file.name_size = 0;
632 chan->out.file.name = NULL;
634 chan->out.file.stream = stream;
635 chan->out.file.versions = versions;
636 chan->out.file.max_size = max_size;
637 chan->out.file.owner = getuid();
638 chan->out.file.group = getgid();
639 chan->references = 0;
640 return (chan);
644 log_set_file_owner(log_channel chan, uid_t owner, gid_t group) {
645 if (chan->type != log_file) {
646 errno = EBADF;
647 return (-1);
649 chan->out.file.owner = owner;
650 chan->out.file.group = group;
651 return (0);
654 log_channel
655 log_new_null_channel() {
656 log_channel chan;
658 chan = memget(sizeof (struct log_channel));
659 if (chan == NULL) {
660 errno = ENOMEM;
661 return (NULL);
663 chan->type = log_null;
664 chan->flags = LOG_CHANNEL_OFF;
665 chan->level = log_info;
666 chan->references = 0;
667 return (chan);
671 log_inc_references(log_channel chan) {
672 if (chan == NULL) {
673 errno = EINVAL;
674 return (-1);
676 chan->references++;
677 return (0);
681 log_dec_references(log_channel chan) {
682 if (chan == NULL || chan->references <= 0) {
683 errno = EINVAL;
684 return (-1);
686 chan->references--;
687 return (0);
690 log_channel_type
691 log_get_channel_type(log_channel chan) {
692 REQUIRE(chan != NULL);
694 return (chan->type);
698 log_free_channel(log_channel chan) {
699 if (chan == NULL || chan->references <= 0) {
700 errno = EINVAL;
701 return (-1);
703 chan->references--;
704 if (chan->references == 0) {
705 if (chan->type == log_file) {
706 if ((chan->flags & LOG_CLOSE_STREAM) &&
707 chan->out.file.stream != NULL)
708 (void)fclose(chan->out.file.stream);
709 if (chan->out.file.name != NULL)
710 memput(chan->out.file.name,
711 chan->out.file.name_size);
713 memput(chan, sizeof (struct log_channel));
715 return (0);
718 /*! \file */