summaryrefslogtreecommitdiff
path: root/gnu/dist/postfix/src/util/sane_time.c
blob: 067e1c19f757e8e90a0714964cdae6896cea4631 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*	$NetBSD: sane_time.c,v 1.1.1.2 2004/05/31 00:25:00 heas Exp $	*/

/*++
/* NAME
/*	sane_time 3
/* SUMMARY
/*	time(2) with backward time jump protection.
/* SYNOPSIS
/*	#include <sane_time.h>
/*
/*	time_t sane_time(void)
/*
/* DESCRIPTION
/*	This module provides time(2) like call for applications
/*	which need monotonically increasing time function rather
/*	than the real exact time. It eliminates the need for various
/*	workarounds all over the application which would handle
/*	potential problems if time suddenly jumps backward.
/*	Instead we choose to deal with this problem inside this
/*	module and let the application focus on its own tasks.
/*
/*	sane_time() returns the current timestamp as obtained from
/*	time(2) call, at least most of the time. In case this routine
/*	detects that time has jumped backward, it keeps returning
/*	whatever timestamp it returned before, until this timestamp
/*	and the time(2) timestamp become synchronized again.
/*	Additionally, the returned timestamp is slowly increased to
/*	prevent the faked clock from freezing for too long.
/* SEE ALSO
/*	time(2) get current time
/* DIAGNOSTICS
/*	Warning message is logged if backward time jump is detected.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Patrik Rak
/*	Modra 6
/*	155 00, Prague, Czech Republic
/*--*/

/* System library. */

#include <sys_defs.h>

/* Utility library. */

#include <msg.h>

/* Application-specific. */

#include "sane_time.h"

/*
 * How many times shall we slow down the real clock when recovering from
 * time jump.
 */
#define SLEW_FACTOR 2

/* sane_time - get current time, protected against time warping */

time_t  sane_time(void)
{
    time_t  now;
    static time_t last_time,
            last_real;
    int     delta;
    static int fraction;
    static int warned;

    now = time((time_t *) 0);

    if ((delta = now - last_time) < 0 && last_time != 0) {
	if ((delta = now - last_real) < 0) {
	    msg_warn("%sbackward time jump detected -- slewing clock",
		     warned++ ? "another " : "");
	} else {
	    delta += fraction;
	    last_time += delta / SLEW_FACTOR;
	    fraction = delta % SLEW_FACTOR;
	}
    } else {
	if (warned) {
	    warned = 0;
	    msg_warn("backward time jump recovered -- back to normality");
	    fraction = 0;
	}
	last_time = now;
    }
    last_real = now;

    return (last_time);
}

#ifdef TEST

 /*
  * Proof-of-concept test program. Repeatedly print current system time and
  * time returned by sane_time(). Meanwhile, try stepping your system clock
  * back and forth to see what happens.
  */

#include <stdlib.h>
#include <msg_vstream.h>
#include <iostuff.h>			/* doze() */

int     main(int argc, char **argv)
{
    int     delay = 1000000;
    time_t  now;

    msg_vstream_init(argv[0], VSTREAM_ERR);

    if (argc == 2 && (delay = atol(argv[1]) * 1000) > 0)
	 /* void */ ;
    else if (argc != 1)
	msg_fatal("usage: %s [delay in ms (default 1 second)]", argv[0]);

    for (;;) {
	now = time((time_t *) 0);
	vstream_printf("real: %s", ctime(&now));
	now = sane_time();
	vstream_printf("fake: %s\n", ctime(&now));
	vstream_fflush(VSTREAM_OUT);
	doze(delay);
    }
}

#endif