1 /* $NetBSD: sane_time.c,v 1.2 2017/02/14 01:16:49 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* sane_time 3
6 /* SUMMARY
7 /* time(2) with backward time jump protection.
8 /* SYNOPSIS
9 /* #include <sane_time.h>
10 /*
11 /* time_t sane_time(void)
12 /*
13 /* DESCRIPTION
14 /* This module provides time(2) like call for applications
15 /* which need monotonically increasing time function rather
16 /* than the real exact time. It eliminates the need for various
17 /* workarounds all over the application which would handle
18 /* potential problems if time suddenly jumps backward.
19 /* Instead we choose to deal with this problem inside this
20 /* module and let the application focus on its own tasks.
21 /*
22 /* sane_time() returns the current timestamp as obtained from
23 /* time(2) call, at least most of the time. In case this routine
24 /* detects that time has jumped backward, it keeps returning
25 /* whatever timestamp it returned before, until this timestamp
26 /* and the time(2) timestamp become synchronized again.
27 /* Additionally, the returned timestamp is slowly increased to
28 /* prevent the faked clock from freezing for too long.
29 /* SEE ALSO
30 /* time(2) get current time
31 /* DIAGNOSTICS
32 /* Warning message is logged if backward time jump is detected.
33 /* LICENSE
34 /* .ad
35 /* .fi
36 /* The Secure Mailer license must be distributed with this software.
37 /* AUTHOR(S)
38 /* Patrik Rak
39 /* Modra 6
40 /* 155 00, Prague, Czech Republic
41 /*--*/
42
43 /* System library. */
44
45 #include <sys_defs.h>
46
47 /* Utility library. */
48
49 #include <msg.h>
50
51 /* Application-specific. */
52
53 #include "sane_time.h"
54
55 /*
56 * How many times shall we slow down the real clock when recovering from
57 * time jump.
58 */
59 #define SLEW_FACTOR 2
60
61 /* sane_time - get current time, protected against time warping */
62
sane_time(void)63 time_t sane_time(void)
64 {
65 time_t now;
66 static time_t last_time, last_real;
67 long delta;
68 static int fraction;
69 static int warned;
70
71 now = time((time_t *) 0);
72
73 if ((delta = now - last_time) < 0 && last_time != 0) {
74 if ((delta = now - last_real) < 0) {
75 msg_warn("%sbackward time jump detected -- slewing clock",
76 warned++ ? "another " : "");
77 } else {
78 delta += fraction;
79 last_time += delta / SLEW_FACTOR;
80 fraction = delta % SLEW_FACTOR;
81 }
82 } else {
83 if (warned) {
84 warned = 0;
85 msg_warn("backward time jump recovered -- back to normality");
86 fraction = 0;
87 }
88 last_time = now;
89 }
90 last_real = now;
91
92 return (last_time);
93 }
94
95 #ifdef TEST
96
97 /*
98 * Proof-of-concept test program. Repeatedly print current system time and
99 * time returned by sane_time(). Meanwhile, try stepping your system clock
100 * back and forth to see what happens.
101 */
102
103 #include <stdlib.h>
104 #include <msg_vstream.h>
105 #include <iostuff.h> /* doze() */
106
main(int argc,char ** argv)107 int main(int argc, char **argv)
108 {
109 int delay = 1000000;
110 time_t now;
111
112 msg_vstream_init(argv[0], VSTREAM_ERR);
113
114 if (argc == 2 && (delay = atol(argv[1]) * 1000) > 0)
115 /* void */ ;
116 else if (argc != 1)
117 msg_fatal("usage: %s [delay in ms (default 1 second)]", argv[0]);
118
119 for (;;) {
120 now = time((time_t *) 0);
121 vstream_printf("real: %s", ctime(&now));
122 now = sane_time();
123 vstream_printf("fake: %s\n", ctime(&now));
124 vstream_fflush(VSTREAM_OUT);
125 doze(delay);
126 }
127 }
128
129 #endif
130