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