1/* $NetBSD$ */ 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, 67 last_real; 68 int delta; 69 static int fraction; 70 static int warned; 71 72 now = time((time_t *) 0); 73 74 if ((delta = now - last_time) < 0 && last_time != 0) { 75 if ((delta = now - last_real) < 0) { 76 msg_warn("%sbackward time jump detected -- slewing clock", 77 warned++ ? "another " : ""); 78 } else { 79 delta += fraction; 80 last_time += delta / SLEW_FACTOR; 81 fraction = delta % SLEW_FACTOR; 82 } 83 } else { 84 if (warned) { 85 warned = 0; 86 msg_warn("backward time jump recovered -- back to normality"); 87 fraction = 0; 88 } 89 last_time = now; 90 } 91 last_real = now; 92 93 return (last_time); 94} 95 96#ifdef TEST 97 98 /* 99 * Proof-of-concept test program. Repeatedly print current system time and 100 * time returned by sane_time(). Meanwhile, try stepping your system clock 101 * back and forth to see what happens. 102 */ 103 104#include <stdlib.h> 105#include <msg_vstream.h> 106#include <iostuff.h> /* doze() */ 107 108int main(int argc, char **argv) 109{ 110 int delay = 1000000; 111 time_t now; 112 113 msg_vstream_init(argv[0], VSTREAM_ERR); 114 115 if (argc == 2 && (delay = atol(argv[1]) * 1000) > 0) 116 /* void */ ; 117 else if (argc != 1) 118 msg_fatal("usage: %s [delay in ms (default 1 second)]", argv[0]); 119 120 for (;;) { 121 now = time((time_t *) 0); 122 vstream_printf("real: %s", ctime(&now)); 123 now = sane_time(); 124 vstream_printf("fake: %s\n", ctime(&now)); 125 vstream_fflush(VSTREAM_OUT); 126 doze(delay); 127 } 128} 129 130#endif 131