xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/sane_time.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
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