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
63time_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
107int     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