adjkerntz.c revision 4039
1226584Sdim/* 2226584Sdim * Copyright (C) 1993 by Andrew A. Chernov, Moscow, Russia. 3226584Sdim * All rights reserved. 4226584Sdim * 5226584Sdim * Redistribution and use in source and binary forms, with or without 6226584Sdim * modification, are permitted provided that the following conditions 7226584Sdim * are met: 8226584Sdim * 1. Redistributions of source code must retain the above copyright 9226584Sdim * notice, this list of conditions and the following disclaimer. 10226584Sdim * 2. Redistributions in binary form must reproduce the above copyright 11226584Sdim * notice, this list of conditions and the following disclaimer in the 12226584Sdim * documentation and/or other materials provided with the distribution. 13226584Sdim * 14226584Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 15226584Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16226584Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17226584Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18226584Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19226584Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20226584Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21226584Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22249423Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23226584Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24263508Sdim * SUCH DAMAGE. 25249423Sdim */ 26226584Sdim 27226584Sdim#ifndef lint 28226584Sdimchar copyright[] = 29226584Sdim"@(#)Copyright (C) 1993 by Andrew A. Chernov, Moscow, Russia.\n\ 30226584Sdim All rights reserved.\n"; 31226584Sdim#endif /* not lint */ 32226584Sdim 33226584Sdim/* 34226584Sdim * Andrew A. Chernov <ache@astral.msk.su> Dec 20 1993 35226584Sdim * 36226584Sdim * Fix kernel time value if machine run wall CMOS clock 37226584Sdim * (and /etc/wall_cmos_clock file present) 38226584Sdim * using zoneinfo rules or direct TZ environment variable set. 39226584Sdim * Use Joerg Wunsch idea for seconds accurate offset calculation 40226584Sdim * with Garrett Wollman and Bruce Evans fixes. 41226584Sdim * 42226584Sdim */ 43226584Sdim#include <stdio.h> 44226584Sdim#include <stdlib.h> 45263508Sdim#include <unistd.h> 46263508Sdim#include <sys/stat.h> 47226584Sdim#include <sys/time.h> 48226584Sdim#include <sys/param.h> 49263508Sdim#include <machine/cpu.h> 50234353Sdim#include <sys/sysctl.h> 51226584Sdim 52226584Sdim#include "pathnames.h" 53226584Sdim 54263508Sdimint main(argc, argv) 55263508Sdim int argc; 56263508Sdim char **argv; 57226584Sdim{ 58234353Sdim struct tm local, utc; 59263508Sdim struct timeval tv, *stv; 60263508Sdim struct timezone tz, *stz; 61226584Sdim int kern_offset; 62263508Sdim size_t len; 63263508Sdim int mib[2]; 64263508Sdim /* Avoid time_t here, can be unsigned long or worse */ 65263508Sdim long offset, utcsec, localsec, diff; 66263508Sdim time_t initial_sec, final_sec; 67263508Sdim int ch, init = -1, verbose = 0; 68263508Sdim int disrtcset, need_restore = 0; 69263508Sdim 70226584Sdim while ((ch = getopt(argc, argv, "aiv")) != EOF) 71226584Sdim switch((char)ch) { 72226584Sdim case 'i': /* initial call, save offset */ 73226584Sdim if (init != -1) 74226584Sdim goto usage; 75226584Sdim init = 1; 76226584Sdim break; 77226584Sdim case 'a': /* adjustment call, use saved offset */ 78226584Sdim if (init != -1) 79226584Sdim goto usage; 80226584Sdim init = 0; 81226584Sdim break; 82226584Sdim case 'v': /* verbose */ 83226584Sdim verbose = 1; 84263508Sdim break; 85226584Sdim default: 86226584Sdim usage: 87226584Sdim fprintf(stderr, "Usage:\n\ 88234353Sdim\tadjkerntz -i [-v]\t(initial call from /etc/rc)\n\ 89234353Sdim\tadjkerntz -a [-v]\t(adjustment call from crontab)\n"); 90226584Sdim return 2; 91226584Sdim } 92226584Sdim if (init == -1) 93226584Sdim goto usage; 94226584Sdim 95226584Sdim if (access(_PATH_CLOCK, F_OK)) 96226584Sdim return 0; 97226584Sdim 98226584Sdim /* Restore saved offset */ 99226584Sdim 100234353Sdim mib[0] = CTL_MACHDEP; 101226584Sdim mib[1] = CPU_ADJKERNTZ; 102234353Sdim len = sizeof(kern_offset); 103234353Sdim if (sysctl(mib, 2, &kern_offset, &len, NULL, 0) == -1) { 104226584Sdim perror("sysctl(get_offset)"); 105226584Sdim return 1; 106241430Sdim } 107263508Sdim 108241430Sdim/****** Critical section, do all things as fast as possible ******/ 109241430Sdim 110226584Sdim /* get local CMOS clock and possible kernel offset */ 111263508Sdim if (gettimeofday(&tv, &tz)) { 112263508Sdim perror("gettimeofday"); 113226584Sdim return 1; 114226584Sdim } 115226584Sdim 116226584Sdim /* get the actual local timezone difference */ 117226584Sdim initial_sec = tv.tv_sec; 118234353Sdim local = *localtime(&initial_sec); 119234353Sdim utc = *gmtime(&initial_sec); 120226584Sdim utc.tm_isdst = local.tm_isdst; /* Use current timezone for mktime(), */ 121226584Sdim /* because it assumed local time */ 122239462Sdim 123226584Sdim /* calculate local CMOS diff from GMT */ 124226584Sdim 125226584Sdim utcsec = mktime(&utc); 126226584Sdim localsec = mktime(&local); 127226584Sdim if (utcsec == -1 || localsec == -1) { 128226584Sdim /* 129226584Sdim * XXX user can only control local time, and it is 130226584Sdim * unacceptable to fail here for -i. 2:30 am in the 131226584Sdim * middle of the nonexistent hour means 3:30 am. 132226584Sdim */ 133226584Sdim fprintf(stderr, 134226584Sdim "Nonexistent local time - try again in an hour\n"); 135226584Sdim return 1; 136226584Sdim } 137226584Sdim offset = utcsec - localsec; 138234353Sdim 139226584Sdim /* correct the kerneltime for this diffs */ 140226584Sdim /* subtract kernel offset, if present, old offset too */ 141226584Sdim 142226584Sdim diff = offset - tz.tz_minuteswest * 60 - kern_offset; 143263508Sdim 144263508Sdim if (diff != 0) { 145263508Sdim 146263508Sdim /* Yet one step for final time */ 147263508Sdim 148263508Sdim final_sec = tv.tv_sec + diff; 149263508Sdim 150263508Sdim /* get the actual local timezone difference */ 151226584Sdim local = *localtime(&final_sec); 152226584Sdim utc = *gmtime(&final_sec); 153226584Sdim utc.tm_isdst = local.tm_isdst; /* Use current timezone for mktime(), */ 154226584Sdim /* because it assumed local time */ 155226584Sdim 156226584Sdim utcsec = mktime(&utc); 157226584Sdim localsec = mktime(&local); 158226584Sdim if (utcsec == -1 || localsec == -1) { 159226584Sdim /* 160226584Sdim * XXX as above. The user has even less control, 161226584Sdim * but perhaps we never get here. 162226584Sdim */ 163226584Sdim fprintf(stderr, 164226584Sdim "Nonexistent (final) local time - try again in an hour\n"); 165226584Sdim return 1; 166226584Sdim } 167226584Sdim offset = utcsec - localsec; 168226584Sdim 169226584Sdim /* correct the kerneltime for this diffs */ 170226584Sdim /* subtract kernel offset, if present, old offset too */ 171226584Sdim 172226584Sdim diff = offset - tz.tz_minuteswest * 60 - kern_offset; 173226584Sdim 174234353Sdim if (diff != 0) { 175234353Sdim tv.tv_sec += diff; 176226584Sdim tv.tv_usec = 0; /* we are restarting here... */ 177226584Sdim stv = &tv; 178226584Sdim } 179226584Sdim else 180226584Sdim stv = NULL; 181226584Sdim } 182226584Sdim else 183226584Sdim stv = NULL; 184226584Sdim 185226584Sdim if (tz.tz_dsttime != 0 || tz.tz_minuteswest != 0) { 186226584Sdim tz.tz_dsttime = tz.tz_minuteswest = 0; /* zone info is garbage */ 187226584Sdim stz = &tz; 188226584Sdim } 189226584Sdim else 190226584Sdim stz = NULL; 191226584Sdim 192226584Sdim if (stz != NULL || stv != NULL) { 193226584Sdim if (init && stv != NULL) { 194226584Sdim mib[0] = CTL_MACHDEP; 195226584Sdim mib[1] = CPU_DISRTCSET; 196226584Sdim len = sizeof(disrtcset); 197226584Sdim if (sysctl(mib, 2, &disrtcset, &len, NULL, 0) == -1) { 198226584Sdim perror("sysctl(get_disrtcset)"); 199226584Sdim return 1; 200226584Sdim } 201226584Sdim if (disrtcset == 0) { 202226584Sdim disrtcset = 1; 203226584Sdim need_restore = 1; 204226584Sdim if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) { 205226584Sdim perror("sysctl(set_disrtcset)"); 206226584Sdim return 1; 207226584Sdim } 208226584Sdim } 209226584Sdim } 210226584Sdim if ((init || stv == NULL) && settimeofday(stv, stz)) { 211226584Sdim perror("settimeofday"); 212226584Sdim return 1; 213226584Sdim } 214226584Sdim } 215226584Sdim 216226584Sdim if (kern_offset != offset) { 217226584Sdim kern_offset = offset; 218226584Sdim mib[0] = CTL_MACHDEP; 219226584Sdim mib[1] = CPU_ADJKERNTZ; 220226584Sdim len = sizeof(kern_offset); 221226584Sdim if (sysctl(mib, 2, NULL, NULL, &kern_offset, len) == -1) { 222226584Sdim perror("sysctl(update_offset)"); 223226584Sdim return 1; 224226584Sdim } 225226584Sdim } 226226584Sdim 227226584Sdim if (need_restore) { 228226584Sdim disrtcset = 0; 229226584Sdim if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) { 230263508Sdim perror("sysctl(restore_disrtcset)"); 231263508Sdim return 1; 232263508Sdim } 233263508Sdim } 234263508Sdim 235263508Sdim/****** End of critical section ******/ 236263508Sdim 237263508Sdim if (verbose) 238226584Sdim printf("Calculated zone offset difference: %ld seconds\n", 239263508Sdim diff); 240263508Sdim 241263508Sdim return 0; 242226584Sdim} 243226584Sdim 244226584Sdim