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