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";
24 #include "port_before.h"
26 #include <sys/types.h>
41 #include <isc/assertions.h>
42 #include <isc/logging.h>
43 #include <isc/memcluster.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: "
61 version_rename(log_channel chan
) {
63 char old_name
[PATH_MAX
+1];
64 char new_name
[PATH_MAX
+1];
66 ver
= chan
->out
.file
.versions
;
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))
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
);
86 log_open_stream(log_channel chan
) {
92 if (chan
== NULL
|| chan
->type
!= log_file
) {
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
) {
106 "log_open_stream: stat of %s failed: %s",
107 chan
->out
.file
.name
, strerror(errno
));
108 chan
->flags
|= LOG_CHANNEL_BROKEN
;
113 regular
= (sb
.st_mode
& S_IFREG
);
115 if (chan
->out
.file
.versions
) {
118 "log_open_stream: want versions but %s isn't a regular file",
119 chan
->out
.file
.name
);
120 chan
->flags
|= LOG_CHANNEL_BROKEN
;
126 flags
= O_WRONLY
|O_CREAT
|O_APPEND
;
128 if ((chan
->flags
& LOG_TRUNCATE
) != 0) {
130 (void)unlink(chan
->out
.file
.name
);
134 "log_open_stream: want truncation but %s isn't a regular file",
135 chan
->out
.file
.name
);
136 chan
->flags
|= LOG_CHANNEL_BROKEN
;
142 fd
= open(chan
->out
.file
.name
, flags
,
143 S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
145 syslog(LOG_ERR
, "log_open_stream: open(%s) failed: %s",
146 chan
->out
.file
.name
, strerror(errno
));
147 chan
->flags
|= LOG_CHANNEL_BROKEN
;
150 stream
= fdopen(fd
, "a");
151 if (stream
== NULL
) {
152 syslog(LOG_ERR
, "log_open_stream: fdopen() failed");
153 chan
->flags
|= LOG_CHANNEL_BROKEN
;
156 (void) fchown(fd
, chan
->out
.file
.owner
, chan
->out
.file
.group
);
158 chan
->out
.file
.stream
= stream
;
163 log_close_stream(log_channel chan
) {
166 if (chan
== NULL
|| chan
->type
!= log_file
) {
170 stream
= chan
->out
.file
.stream
;
171 chan
->out
.file
.stream
= NULL
;
172 if (stream
!= NULL
&& fclose(stream
) == EOF
)
178 log_close_debug_channels(log_context lc
) {
179 log_channel_list lcl
;
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
);
191 log_get_stream(log_channel chan
) {
192 if (chan
== NULL
|| chan
->type
!= log_file
) {
196 return (chan
->out
.file
.stream
);
200 log_get_filename(log_channel chan
) {
201 if (chan
== NULL
|| chan
->type
!= log_file
) {
205 return (chan
->out
.file
.name
);
209 log_check_channel(log_context lc
, int level
, log_channel chan
) {
210 int debugging
, chan_level
;
214 debugging
= ((lc
->flags
& LOG_OPTION_DEBUG
) != 0);
217 * If not debugging, short circuit debugging messages very early.
219 if (level
> 0 && !debugging
)
222 if ((chan
->flags
& (LOG_CHANNEL_BROKEN
|LOG_CHANNEL_OFF
)) != 0)
225 /* Some channels only log when debugging is on. */
226 if ((chan
->flags
& LOG_REQUIRE_DEBUG
) && !debugging
)
229 /* Some channels use the global level. */
230 if ((chan
->flags
& LOG_USE_CONTEXT_LEVEL
) != 0) {
231 chan_level
= lc
->level
;
233 chan_level
= chan
->level
;
235 if (level
> chan_level
)
242 log_check(log_context lc
, int category
, int level
) {
243 log_channel_list lcl
;
248 debugging
= ((lc
->flags
& LOG_OPTION_DEBUG
) != 0);
251 * If not debugging, short circuit debugging messages very early.
253 if (level
> 0 && !debugging
)
256 if (category
< 0 || category
> lc
->num_categories
)
257 category
= 0; /*%< use default */
258 lcl
= lc
->categories
[category
];
261 lcl
= lc
->categories
[0];
264 for ( /* nothing */; lcl
!= NULL
; lcl
= lcl
->next
) {
265 if (log_check_channel(lc
, level
, lcl
->channel
))
272 log_vwrite(log_context lc
, int category
, int level
, const char *format
,
274 log_channel_list lcl
;
275 int pri
, debugging
, did_vsprintf
= 0;
276 int original_category
;
285 const char *category_name
;
286 const char *level_str
;
292 debugging
= (lc
->flags
& LOG_OPTION_DEBUG
);
295 * If not debugging, short circuit debugging messages very early.
297 if (level
> 0 && !debugging
)
300 if (category
< 0 || category
> lc
->num_categories
)
301 category
= 0; /*%< use default */
302 original_category
= category
;
303 lcl
= lc
->categories
[category
];
306 lcl
= lc
->categories
[0];
310 * Get the current time and format it.
313 if (gettimeofday(&tv
, NULL
) < 0) {
314 syslog(LOG_INFO
, "gettimeofday failed in log_vwrite()");
318 local_tm
= localtime_r(&tt
, &tm_tmp
);
320 local_tm
= localtime(&tt
);
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
];
341 if (level
>= log_critical
) {
343 sprintf(level_buf
, "debug %d: ", level
);
344 level_str
= level_buf
;
346 level_str
= level_text
[-level
-1];
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
) {
358 if (!log_check_channel(lc
, level
, chan
))
362 (void)vsprintf(lc
->buffer
, format
, args
);
363 if (strlen(lc
->buffer
) > (size_t)LOG_BUFFER_SIZE
) {
365 "memory overrun in log_vwrite()");
371 switch (chan
->type
) {
373 if (level
>= log_critical
)
374 pri
= (level
>= 0) ? 0 : -level
;
377 syslog(chan
->out
.facility
|syslog_priority
[pri
],
379 (chan
->flags
& LOG_TIMESTAMP
) ? time_buf
: "",
380 (chan
->flags
& LOG_PRINT_CATEGORY
) ?
382 (chan
->flags
& LOG_PRINT_LEVEL
) ?
387 stream
= chan
->out
.file
.stream
;
388 if (stream
== NULL
) {
389 stream
= log_open_stream(chan
);
393 if (chan
->out
.file
.max_size
!= ULONG_MAX
) {
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
);
413 fprintf(stream
, "%s%s%s%s\n",
414 (chan
->flags
& LOG_TIMESTAMP
) ? time_buf
: "",
415 (chan
->flags
& LOG_PRINT_CATEGORY
) ?
417 (chan
->flags
& LOG_PRINT_LEVEL
) ?
426 "unknown channel type in log_vwrite()");
432 log_write(log_context lc
, int category
, int level
, const char *format
, ...) {
435 va_start(args
, format
);
436 log_vwrite(lc
, category
, level
, format
, args
);
441 * Functions to create, set, or destroy contexts
445 log_new_context(int num_categories
, char **category_names
, log_context
*lc
) {
448 nlc
= memget(sizeof (struct log_context
));
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
));
461 memset(nlc
->categories
, '\0',
462 num_categories
* sizeof (log_channel_list
));
470 log_free_context(log_context lc
) {
471 log_channel_list lcl
, lcl_next
;
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
;
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
) {
498 lcl
= memget(sizeof (struct log_channel_list
));
504 lcl
->next
= lc
->categories
[category
];
505 lc
->categories
[category
] = lcl
;
511 log_remove_channel(log_context lc
, int category
, log_channel chan
) {
512 log_channel_list lcl
, prev_lcl
, next_lcl
;
515 if (lc
== NULL
|| category
< 0 || category
>= lc
->num_categories
) {
520 for (prev_lcl
= NULL
, lcl
= lc
->categories
[category
];
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
;
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.
547 log_option(log_context lc
, int option
, int value
) {
553 case LOG_OPTION_DEBUG
:
557 lc
->flags
&= ~option
;
559 case LOG_OPTION_LEVEL
:
570 log_category_is_active(log_context lc
, int category
) {
575 if (category
>= 0 && category
< lc
->num_categories
&&
576 lc
->categories
[category
] != NULL
)
582 log_new_syslog_channel(unsigned int flags
, int level
, int facility
) {
585 chan
= memget(sizeof (struct log_channel
));
590 chan
->type
= log_syslog
;
593 chan
->out
.facility
= facility
;
594 chan
->references
= 0;
599 log_new_file_channel(unsigned int flags
, int level
,
600 const char *name
, FILE *stream
, unsigned int versions
,
601 unsigned long max_size
) {
604 chan
= memget(sizeof (struct log_channel
));
609 chan
->type
= log_file
;
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
));
629 strcpy(chan
->out
.file
.name
, name
);
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;
644 log_set_file_owner(log_channel chan
, uid_t owner
, gid_t group
) {
645 if (chan
->type
!= log_file
) {
649 chan
->out
.file
.owner
= owner
;
650 chan
->out
.file
.group
= group
;
655 log_new_null_channel() {
658 chan
= memget(sizeof (struct log_channel
));
663 chan
->type
= log_null
;
664 chan
->flags
= LOG_CHANNEL_OFF
;
665 chan
->level
= log_info
;
666 chan
->references
= 0;
671 log_inc_references(log_channel chan
) {
681 log_dec_references(log_channel chan
) {
682 if (chan
== NULL
|| chan
->references
<= 0) {
691 log_get_channel_type(log_channel chan
) {
692 REQUIRE(chan
!= NULL
);
698 log_free_channel(log_channel chan
) {
699 if (chan
== NULL
|| chan
->references
<= 0) {
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
));