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