ed815ca681ff6e6a272237e5f05d3425e4664d8a
[strongswan.git] / src / pluto / log.c
1 /* error logging functions
2 * Copyright (C) 1997 Angelos D. Keromytis.
3 * Copyright (C) 1998-2001 D. Hugh Redelmeier.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 *
15 * RCSID $Id$
16 */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include <stdarg.h>
22 #include <syslog.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <signal.h> /* used only if MSG_NOSIGNAL not defined */
27 #include <sys/queue.h>
28 #include <libgen.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31
32 #include <freeswan.h>
33
34 #include "constants.h"
35 #include "defs.h"
36 #include "log.h"
37 #include "server.h"
38 #include "state.h"
39 #include "connections.h"
40 #include "kernel.h"
41 #include "whack.h" /* needs connections.h */
42 #include "timer.h"
43
44 /* close one per-peer log */
45 static void perpeer_logclose(struct connection *c); /* forward */
46
47
48 bool
49 log_to_stderr = TRUE, /* should log go to stderr? */
50 log_to_syslog = TRUE, /* should log go to syslog? */
51 log_to_perpeer= FALSE; /* should log go to per-IP file? */
52
53 bool
54 logged_txt_warning = FALSE; /* should we complain about finding KEY? */
55
56 /* should we complain when we find no local id */
57 bool
58 logged_myid_fqdn_txt_warning = FALSE,
59 logged_myid_ip_txt_warning = FALSE,
60 logged_myid_fqdn_key_warning = FALSE,
61 logged_myid_ip_key_warning = FALSE;
62
63 /* may include trailing / */
64 const char *base_perpeer_logdir = PERPEERLOGDIR;
65 static int perpeer_count = 0;
66
67 /* from sys/queue.h */
68 static TAILQ_HEAD(perpeer, connection) perpeer_list;
69
70
71 /* Context for logging.
72 *
73 * Global variables: must be carefully adjusted at transaction boundaries!
74 * If the context provides a whack file descriptor, messages
75 * should be copied to it -- see whack_log()
76 */
77 int whack_log_fd = NULL_FD; /* only set during whack_handle() */
78 struct state *cur_state = NULL; /* current state, for diagnostics */
79 struct connection *cur_connection = NULL; /* current connection, for diagnostics */
80 const ip_address *cur_from = NULL; /* source of current current message */
81 u_int16_t cur_from_port; /* host order */
82
83 void
84 init_log(const char *program)
85 {
86 if (log_to_stderr)
87 setbuf(stderr, NULL);
88 if (log_to_syslog)
89 openlog(program, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_AUTHPRIV);
90
91 TAILQ_INIT(&perpeer_list);
92 }
93
94 void
95 close_peerlog(void)
96 {
97 /* exit if the queue has not been initialized */
98 if (perpeer_list.tqh_first == NULL)
99 return;
100
101 /* end of queue is given by pointer to "HEAD" */
102 while (TAILQ_LAST(&perpeer_list, perpeer) != (void *)&perpeer_list)
103 perpeer_logclose(TAILQ_LAST(&perpeer_list, perpeer));
104 }
105
106 void
107 close_log(void)
108 {
109 if (log_to_syslog)
110 closelog();
111
112 close_peerlog();
113 }
114
115 /* Sanitize character string in situ: turns dangerous characters into \OOO.
116 * With a bit of work, we could use simpler reps for \\, \r, etc.,
117 * but this is only to protect against something that shouldn't be used.
118 * Truncate resulting string to what fits in buffer.
119 */
120 static size_t
121 sanitize(char *buf, size_t size)
122 {
123 # define UGLY_WIDTH 4 /* width for ugly character: \OOO */
124 size_t len;
125 size_t added = 0;
126 char *p;
127
128 passert(size >= UGLY_WIDTH); /* need room to swing cat */
129
130 /* find right side of string to be sanitized and count
131 * number of columns to be added. Stop on end of string
132 * or lack of room for more result.
133 */
134 for (p = buf; *p != '\0' && &p[added] < &buf[size - UGLY_WIDTH]; )
135 {
136 unsigned char c = *p++;
137
138 if (c == '\\' || !isprint(c))
139 added += UGLY_WIDTH - 1;
140 }
141
142 /* at this point, p points after last original character to be
143 * included. added is how many characters are added to sanitize.
144 * so p[added] will point after last sanitized character.
145 */
146
147 p[added] = '\0';
148 len = &p[added] - buf;
149
150 /* scan backwards, copying characters to their new home
151 * and inserting the expansions for ugly characters.
152 * It is finished when no more shifting is required.
153 * This is a predecrement loop.
154 */
155 while (added != 0)
156 {
157 char fmtd[UGLY_WIDTH + 1];
158 unsigned char c;
159
160 while ((c = *--p) != '\\' && isprint(c))
161 p[added] = c;
162 added -= UGLY_WIDTH - 1;
163 snprintf(fmtd, sizeof(fmtd), "\\%03o", c);
164 memcpy(p + added, fmtd, UGLY_WIDTH);
165 }
166 return len;
167 # undef UGLY_WIDTH
168 }
169
170 /* format a string for the log, with suitable prefixes.
171 * A format starting with ~ indicates that this is a reprocessing
172 * of the message, so prefixing and quoting is suppressed.
173 */
174 static void
175 fmt_log(char *buf, size_t buf_len, const char *fmt, va_list ap)
176 {
177 bool reproc = *fmt == '~';
178 size_t ps;
179 struct connection *c = cur_state != NULL ? cur_state->st_connection
180 : cur_connection;
181
182 buf[0] = '\0';
183 if (reproc)
184 fmt++; /* ~ at start of format suppresses this prefix */
185 else if (c != NULL)
186 {
187 /* start with name of connection */
188 char *const be = buf + buf_len;
189 char *bp = buf;
190
191 snprintf(bp, be - bp, "\"%s\"", c->name);
192 bp += strlen(bp);
193
194 /* if it fits, put in any connection instance information */
195 if (be - bp > CONN_INST_BUF)
196 {
197 fmt_conn_instance(c, bp);
198 bp += strlen(bp);
199 }
200
201 if (cur_state != NULL)
202 {
203 /* state number */
204 snprintf(bp, be - bp, " #%lu", cur_state->st_serialno);
205 bp += strlen(bp);
206 }
207 snprintf(bp, be - bp, ": ");
208 }
209 else if (cur_from != NULL)
210 {
211 /* peer's IP address */
212 /* Note: must not use ip_str() because our caller might! */
213 char ab[ADDRTOT_BUF];
214
215 (void) addrtot(cur_from, 0, ab, sizeof(ab));
216 snprintf(buf, buf_len, "packet from %s:%u: "
217 , ab, (unsigned)cur_from_port);
218 }
219
220 ps = strlen(buf);
221 vsnprintf(buf + ps, buf_len - ps, fmt, ap);
222 if (!reproc)
223 (void)sanitize(buf, buf_len);
224 }
225
226 static void
227 perpeer_logclose(struct connection *c)
228 {
229 /* only free/close things if we had used them! */
230 if (c->log_file != NULL)
231 {
232 passert(perpeer_count > 0);
233
234 TAILQ_REMOVE(&perpeer_list, c, log_link);
235 perpeer_count--;
236 fclose(c->log_file);
237 c->log_file=NULL;
238 }
239 }
240
241 void
242 perpeer_logfree(struct connection *c)
243 {
244 perpeer_logclose(c);
245 if (c->log_file_name != NULL)
246 {
247 free(c->log_file_name);
248 c->log_file_name = NULL;
249 c->log_file_err = FALSE;
250 }
251 }
252
253 /* open the per-peer log */
254 static void
255 open_peerlog(struct connection *c)
256 {
257 syslog(LOG_INFO, "opening log file for conn %s", c->name);
258
259 if (c->log_file_name == NULL)
260 {
261 char peername[ADDRTOT_BUF], dname[ADDRTOT_BUF];
262 int peernamelen, lf_len;
263
264 addrtot(&c->spd.that.host_addr, 'Q', peername, sizeof(peername));
265 peernamelen = strlen(peername);
266
267 /* copy IP address, turning : and . into / */
268 {
269 char c, *p, *q;
270
271 p = peername;
272 q = dname;
273 do {
274 c = *p++;
275 if (c == '.' || c == ':')
276 c = '/';
277 *q++ = c;
278 } while (c != '\0');
279 }
280
281 lf_len = peernamelen * 2
282 + strlen(base_perpeer_logdir)
283 + sizeof("//.log")
284 + 1;
285 c->log_file_name = malloc(lf_len);
286
287 fprintf(stderr, "base dir |%s| dname |%s| peername |%s|"
288 , base_perpeer_logdir, dname, peername);
289 snprintf(c->log_file_name, lf_len, "%s/%s/%s.log"
290 , base_perpeer_logdir, dname, peername);
291
292 syslog(LOG_DEBUG, "conn %s logfile is %s", c->name, c->log_file_name);
293 }
294
295 /* now open the file, creating directories if necessary */
296
297 { /* create the directory */
298 char *dname;
299 int bpl_len = strlen(base_perpeer_logdir);
300 char *slashloc;
301
302 dname = clone_str(c->log_file_name);
303 dname = dirname(dname);
304
305 if (access(dname, W_OK) != 0)
306 {
307 if (errno != ENOENT)
308 {
309 if (c->log_file_err)
310 {
311 syslog(LOG_CRIT, "can not write to %s: %s"
312 , dname, strerror(errno));
313 c->log_file_err = TRUE;
314 free(dname);
315 return;
316 }
317 }
318
319 /* directory does not exist, walk path creating dirs */
320 /* start at base_perpeer_logdir */
321 slashloc = dname + bpl_len;
322 slashloc++; /* since, by construction there is a slash
323 right there */
324
325 while (*slashloc != '\0')
326 {
327 char saveslash;
328
329 /* look for next slash */
330 while (*slashloc != '\0' && *slashloc != '/') slashloc++;
331
332 saveslash = *slashloc;
333
334 *slashloc = '\0';
335
336 if (mkdir(dname, 0750) != 0 && errno != EEXIST)
337 {
338 syslog(LOG_CRIT, "can not create dir %s: %s"
339 , dname, strerror(errno));
340 c->log_file_err = TRUE;
341 free(dname);
342 return;
343 }
344 syslog(LOG_DEBUG, "created new directory %s", dname);
345 *slashloc = saveslash;
346 slashloc++;
347 }
348 }
349 free(dname);
350 }
351
352 c->log_file = fopen(c->log_file_name, "a");
353 if (c->log_file == NULL)
354 {
355 if (c->log_file_err)
356 {
357 syslog(LOG_CRIT, "logging system can not open %s: %s"
358 , c->log_file_name, strerror(errno));
359 c->log_file_err = TRUE;
360 }
361 return;
362 }
363
364 /* look for a connection to close! */
365 while (perpeer_count >= MAX_PEERLOG_COUNT)
366 {
367 /* can not be NULL because perpeer_count > 0 */
368 passert(TAILQ_LAST(&perpeer_list, perpeer) != (void *)&perpeer_list);
369
370 perpeer_logclose(TAILQ_LAST(&perpeer_list, perpeer));
371 }
372
373 /* insert this into the list */
374 TAILQ_INSERT_HEAD(&perpeer_list, c, log_link);
375 passert(c->log_file != NULL);
376 perpeer_count++;
377 }
378
379 /* log a line to cur_connection's log */
380 static void
381 peerlog(const char *prefix, const char *m)
382 {
383 if (cur_connection == NULL)
384 {
385 /* we can not log it in this case. Oh well. */
386 return;
387 }
388
389 if (cur_connection->log_file == NULL)
390 {
391 open_peerlog(cur_connection);
392 }
393
394 /* despite our attempts above, we may not be able to open the file. */
395 if (cur_connection->log_file != NULL)
396 {
397 char datebuf[32];
398 time_t n;
399 struct tm *t;
400
401 time(&n);
402 t = localtime(&n);
403
404 strftime(datebuf, sizeof(datebuf), "%Y-%m-%d %T", t);
405 fprintf(cur_connection->log_file, "%s %s%s\n", datebuf, prefix, m);
406
407 /* now move it to the front of the list */
408 TAILQ_REMOVE(&perpeer_list, cur_connection, log_link);
409 TAILQ_INSERT_HEAD(&perpeer_list, cur_connection, log_link);
410 }
411 }
412
413 void
414 plog(const char *message, ...)
415 {
416 va_list args;
417 char m[LOG_WIDTH]; /* longer messages will be truncated */
418
419 va_start(args, message);
420 fmt_log(m, sizeof(m), message, args);
421 va_end(args);
422
423 if (log_to_stderr)
424 fprintf(stderr, "%s\n", m);
425 if (log_to_syslog)
426 syslog(LOG_WARNING, "%s", m);
427 if (log_to_perpeer)
428 peerlog("", m);
429
430 whack_log(RC_LOG, "~%s", m);
431 }
432
433 void
434 loglog(int mess_no, const char *message, ...)
435 {
436 va_list args;
437 char m[LOG_WIDTH]; /* longer messages will be truncated */
438
439 va_start(args, message);
440 fmt_log(m, sizeof(m), message, args);
441 va_end(args);
442
443 if (log_to_stderr)
444 fprintf(stderr, "%s\n", m);
445 if (log_to_syslog)
446 syslog(LOG_WARNING, "%s", m);
447 if (log_to_perpeer)
448 peerlog("", m);
449
450 whack_log(mess_no, "~%s", m);
451 }
452
453 void
454 log_errno_routine(int e, const char *message, ...)
455 {
456 va_list args;
457 char m[LOG_WIDTH]; /* longer messages will be truncated */
458
459 va_start(args, message);
460 fmt_log(m, sizeof(m), message, args);
461 va_end(args);
462
463 if (log_to_stderr)
464 fprintf(stderr, "ERROR: %s. Errno %d: %s\n", m, e, strerror(e));
465 if (log_to_syslog)
466 syslog(LOG_ERR, "ERROR: %s. Errno %d: %s", m, e, strerror(e));
467 if (log_to_perpeer)
468 {
469 peerlog(strerror(e), m);
470 }
471
472 whack_log(RC_LOG_SERIOUS
473 , "~ERROR: %s. Errno %d: %s", m, e, strerror(e));
474 }
475
476 void
477 exit_log(const char *message, ...)
478 {
479 va_list args;
480 char m[LOG_WIDTH]; /* longer messages will be truncated */
481
482 va_start(args, message);
483 fmt_log(m, sizeof(m), message, args);
484 va_end(args);
485
486 if (log_to_stderr)
487 fprintf(stderr, "FATAL ERROR: %s\n", m);
488 if (log_to_syslog)
489 syslog(LOG_ERR, "FATAL ERROR: %s", m);
490 if (log_to_perpeer)
491 peerlog("FATAL ERROR: ", m);
492
493 whack_log(RC_LOG_SERIOUS, "~FATAL ERROR: %s", m);
494
495 exit_pluto(1);
496 }
497
498 void
499 exit_log_errno_routine(int e, const char *message, ...)
500 {
501 va_list args;
502 char m[LOG_WIDTH]; /* longer messages will be truncated */
503
504 va_start(args, message);
505 fmt_log(m, sizeof(m), message, args);
506 va_end(args);
507
508 if (log_to_stderr)
509 fprintf(stderr, "FATAL ERROR: %s. Errno %d: %s\n", m, e, strerror(e));
510 if (log_to_syslog)
511 syslog(LOG_ERR, "FATAL ERROR: %s. Errno %d: %s", m, e, strerror(e));
512 if (log_to_perpeer)
513 peerlog(strerror(e), m);
514
515 whack_log(RC_LOG_SERIOUS
516 , "~FATAL ERROR: %s. Errno %d: %s", m, e, strerror(e));
517
518 exit_pluto(1);
519 }
520
521 /* emit message to whack.
522 * form is "ddd statename text" where
523 * - ddd is a decimal status code (RC_*) as described in whack.h
524 * - text is a human-readable annotation
525 */
526 #ifdef DEBUG
527 static volatile sig_atomic_t dying_breath = FALSE;
528 #endif
529
530 void
531 whack_log(int mess_no, const char *message, ...)
532 {
533 int wfd = whack_log_fd != NULL_FD ? whack_log_fd
534 : cur_state != NULL ? cur_state->st_whack_sock
535 : NULL_FD;
536
537 if (wfd != NULL_FD
538 #ifdef DEBUG
539 || dying_breath
540 #endif
541 )
542 {
543 va_list args;
544 char m[LOG_WIDTH]; /* longer messages will be truncated */
545 int prelen = snprintf(m, sizeof(m), "%03d ", mess_no);
546
547 passert(prelen >= 0);
548
549 va_start(args, message);
550 fmt_log(m+prelen, sizeof(m)-prelen, message, args);
551 va_end(args);
552
553 #if DEBUG
554 if (dying_breath)
555 {
556 /* status output copied to log */
557 if (log_to_stderr)
558 fprintf(stderr, "%s\n", m + prelen);
559 if (log_to_syslog)
560 syslog(LOG_WARNING, "%s", m + prelen);
561 if (log_to_perpeer)
562 peerlog("", m);
563 }
564 #endif
565
566 if (wfd != NULL_FD)
567 {
568 /* write to whack socket, but suppress possible SIGPIPE */
569 size_t len = strlen(m);
570 #ifdef MSG_NOSIGNAL /* depends on version of glibc??? */
571 m[len] = '\n'; /* don't need NUL, do need NL */
572 (void) send(wfd, m, len + 1, MSG_NOSIGNAL);
573 #else /* !MSG_NOSIGNAL */
574 int r;
575 struct sigaction act
576 , oldact;
577
578 m[len] = '\n'; /* don't need NUL, do need NL */
579 act.sa_handler = SIG_IGN;
580 sigemptyset(&act.sa_mask);
581 act.sa_flags = 0; /* no nothing */
582 r = sigaction(SIGPIPE, &act, &oldact);
583 passert(r == 0);
584
585 (void) write(wfd, m, len + 1);
586
587 r = sigaction(SIGPIPE, &oldact, NULL);
588 passert(r == 0);
589 #endif /* !MSG_NOSIGNAL */
590 }
591 }
592 }
593
594 /* Build up a diagnostic in a static buffer.
595 * Although this would be a generally useful function, it is very
596 * hard to come up with a discipline that prevents different uses
597 * from interfering. It is intended that by limiting it to building
598 * diagnostics, we will avoid this problem.
599 * Juggling is performed to allow an argument to be a previous
600 * result: the new string may safely depend on the old one. This
601 * restriction is not checked in any way: violators will produce
602 * confusing results (without crashing!).
603 */
604 char diag_space[sizeof(diag_space)];
605
606 err_t
607 builddiag(const char *fmt, ...)
608 {
609 static char diag_space[LOG_WIDTH]; /* longer messages will be truncated */
610 char t[sizeof(diag_space)]; /* build result here first */
611 va_list args;
612
613 va_start(args, fmt);
614 t[0] = '\0'; /* in case nothing terminates string */
615 vsnprintf(t, sizeof(t), fmt, args);
616 va_end(args);
617 strcpy(diag_space, t);
618 return diag_space;
619 }
620
621 /* Debugging message support */
622
623 #ifdef DEBUG
624
625 void
626 switch_fail(int n, const char *file_str, unsigned long line_no)
627 {
628 char buf[30];
629
630 snprintf(buf, sizeof(buf), "case %d unexpected", n);
631 passert_fail(buf, file_str, line_no);
632 }
633
634 void
635 passert_fail(const char *pred_str, const char *file_str, unsigned long line_no)
636 {
637 /* we will get a possibly unplanned prefix. Hope it works */
638 loglog(RC_LOG_SERIOUS, "ASSERTION FAILED at %s:%lu: %s", file_str, line_no, pred_str);
639 if (!dying_breath)
640 {
641 dying_breath = TRUE;
642 show_status(TRUE, NULL);
643 }
644 abort(); /* exiting correctly doesn't always work */
645 }
646
647 void
648 pexpect_log(const char *pred_str, const char *file_str, unsigned long line_no)
649 {
650 /* we will get a possibly unplanned prefix. Hope it works */
651 loglog(RC_LOG_SERIOUS, "EXPECTATION FAILED at %s:%lu: %s", file_str, line_no, pred_str);
652 }
653
654 lset_t
655 base_debugging = DBG_NONE, /* default to reporting nothing */
656 cur_debugging = DBG_NONE;
657
658 void
659 extra_debugging(const struct connection *c)
660 {
661 if(c == NULL)
662 {
663 reset_debugging();
664 return;
665 }
666
667 if (c!= NULL && c->extra_debugging != 0)
668 {
669 plog("enabling for connection: %s"
670 , bitnamesof(debug_bit_names, c->extra_debugging & ~cur_debugging));
671 cur_debugging |= c->extra_debugging;
672 }
673 }
674
675 /* log a debugging message (prefixed by "| ") */
676
677 void
678 DBG_log(const char *message, ...)
679 {
680 va_list args;
681 char m[LOG_WIDTH]; /* longer messages will be truncated */
682
683 va_start(args, message);
684 vsnprintf(m, sizeof(m), message, args);
685 va_end(args);
686
687 (void)sanitize(m, sizeof(m));
688
689 if (log_to_stderr)
690 fprintf(stderr, "| %s\n", m);
691 if (log_to_syslog)
692 syslog(LOG_DEBUG, "| %s", m);
693 if (log_to_perpeer)
694 peerlog("| ", m);
695 }
696
697 /* dump raw bytes in hex to stderr (for lack of any better destination) */
698
699 void
700 DBG_dump(const char *label, const void *p, size_t len)
701 {
702 # define DUMP_LABEL_WIDTH 20 /* arbitrary modest boundary */
703 # define DUMP_WIDTH (4 * (1 + 4 * 3) + 1)
704 char buf[DUMP_LABEL_WIDTH + DUMP_WIDTH];
705 char *bp;
706 const unsigned char *cp = p;
707
708 bp = buf;
709
710 if (label != NULL && label[0] != '\0')
711 {
712 /* Handle the label. Care must be taken to avoid buffer overrun. */
713 size_t llen = strlen(label);
714
715 if (llen + 1 > sizeof(buf))
716 {
717 DBG_log("%s", label);
718 }
719 else
720 {
721 strcpy(buf, label);
722 if (buf[llen-1] == '\n')
723 {
724 buf[llen-1] = '\0'; /* get rid of newline */
725 DBG_log("%s", buf);
726 }
727 else if (llen < DUMP_LABEL_WIDTH)
728 {
729 bp = buf + llen;
730 }
731 else
732 {
733 DBG_log("%s", buf);
734 }
735 }
736 }
737
738 do {
739 int i, j;
740
741 for (i = 0; len!=0 && i!=4; i++)
742 {
743 *bp++ = ' ';
744 for (j = 0; len!=0 && j!=4; len--, j++)
745 {
746 static const char hexdig[] = "0123456789abcdef";
747
748 *bp++ = ' ';
749 *bp++ = hexdig[(*cp >> 4) & 0xF];
750 *bp++ = hexdig[*cp & 0xF];
751 cp++;
752 }
753 }
754 *bp = '\0';
755 DBG_log("%s", buf);
756 bp = buf;
757 } while (len != 0);
758 # undef DUMP_LABEL_WIDTH
759 # undef DUMP_WIDTH
760 }
761
762 #endif /* DEBUG */
763
764 void
765 show_status(bool all, const char *name)
766 {
767 if (all)
768 {
769 show_ifaces_status();
770 show_myid_status();
771 show_debug_status();
772 whack_log(RC_COMMENT, BLANK_FORMAT); /* spacer */
773 }
774 show_connections_status(all, name);
775 show_states_status(all, name);
776 #ifdef KLIPS
777 show_shunt_status();
778 #endif
779 }
780
781 /* ip_str: a simple to use variant of addrtot.
782 * It stores its result in a static buffer.
783 * This means that newer calls overwrite the storage of older calls.
784 * Note: this is not used in any of the logging functions, so their
785 * callers may use it.
786 */
787 const char *
788 ip_str(const ip_address *src)
789 {
790 static char buf[ADDRTOT_BUF];
791
792 addrtot(src, 0, buf, sizeof(buf));
793 return buf;
794 }
795
796 /*
797 * a routine that attempts to schedule itself daily.
798 *
799 */
800
801 void
802 daily_log_reset(void)
803 {
804 /* now perform actions */
805 logged_txt_warning = FALSE;
806
807 logged_myid_fqdn_txt_warning = FALSE;
808 logged_myid_ip_txt_warning = FALSE;
809 logged_myid_fqdn_key_warning = FALSE;
810 logged_myid_ip_key_warning = FALSE;
811 }
812
813 void
814 daily_log_event(void)
815 {
816 struct tm *ltime;
817 time_t n, interval;
818
819 /* attempt to schedule oneself to midnight, local time
820 * do this by getting seconds in the day, and delaying
821 * by 86400 - hour*3600+minutes*60+seconds.
822 */
823 time(&n);
824 ltime = localtime(&n);
825 interval = (24 * 60 * 60)
826 - (ltime->tm_sec
827 + ltime->tm_min * 60
828 + ltime->tm_hour * 3600);
829
830 event_schedule(EVENT_LOG_DAILY, interval, NULL);
831
832 daily_log_reset();
833 }
834
835 /*
836 * Local Variables:
837 * c-basic-offset:4
838 * c-style: pluto
839 * End:
840 */