adjkerntz.c revision 15057
1867Sache/* 215046Sache * Copyright (C) 1993-1996 by Andrey A. Chernov, Moscow, Russia. 3867Sache * All rights reserved. 4867Sache * 5867Sache * Redistribution and use in source and binary forms, with or without 6867Sache * modification, are permitted provided that the following conditions 7867Sache * are met: 8867Sache * 1. Redistributions of source code must retain the above copyright 9867Sache * notice, this list of conditions and the following disclaimer. 10867Sache * 2. Redistributions in binary form must reproduce the above copyright 11867Sache * notice, this list of conditions and the following disclaimer in the 12867Sache * documentation and/or other materials provided with the distribution. 13867Sache * 141092Sache * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 15867Sache * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16867Sache * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17867Sache * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18867Sache * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19867Sache * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20867Sache * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21867Sache * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22867Sache * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23867Sache * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24867Sache * SUCH DAMAGE. 25867Sache */ 26867Sache 27867Sache#ifndef lint 28867Sachechar copyright[] = 2915046Sache"@(#)Copyright (C) 1993-1996 by Andrey A. Chernov, Moscow, Russia.\n\ 30867Sache All rights reserved.\n"; 31867Sache#endif /* not lint */ 32867Sache 33867Sache/* 347398Sache * Andrey A. Chernov <ache@astral.msk.su> Dec 20 1993 35867Sache * 36867Sache * Fix kernel time value if machine run wall CMOS clock 37867Sache * (and /etc/wall_cmos_clock file present) 38867Sache * using zoneinfo rules or direct TZ environment variable set. 39867Sache * Use Joerg Wunsch idea for seconds accurate offset calculation 40867Sache * with Garrett Wollman and Bruce Evans fixes. 41867Sache * 42867Sache */ 43867Sache#include <stdio.h> 444107Sache#include <signal.h> 45867Sache#include <stdlib.h> 46867Sache#include <unistd.h> 474048Sache#include <syslog.h> 48867Sache#include <sys/stat.h> 49867Sache#include <sys/time.h> 502910Sache#include <sys/param.h> 512910Sache#include <machine/cpu.h> 522910Sache#include <sys/sysctl.h> 53867Sache 54867Sache#include "pathnames.h" 55867Sache 565232Sache/*#define DEBUG*/ 5715046Sache 5815046Sache#define True (1) 5915046Sache#define False (0) 6015046Sache#define Unknown (-1) 6115046Sache 624048Sache#define REPORT_PERIOD (30*60) 634048Sache 644107Sachevoid fake() {} 654107Sache 66867Sacheint main(argc, argv) 67867Sache int argc; 68867Sache char **argv; 69867Sache{ 70867Sache struct tm local, utc; 71867Sache struct timeval tv, *stv; 72867Sache struct timezone tz, *stz; 7315046Sache int kern_offset, wall_clock, disrtcset; 742910Sache size_t len; 752910Sache int mib[2]; 761092Sache /* Avoid time_t here, can be unsigned long or worse */ 772910Sache long offset, utcsec, localsec, diff; 781092Sache time_t initial_sec, final_sec; 7915046Sache int ch; 8015046Sache int initial_isdst = -1, final_isdst; 8115046Sache int need_restore = False, sleep_mode = False, looping, 8215046Sache init = Unknown; 834107Sache sigset_t mask, emask; 84867Sache 8515046Sache while ((ch = getopt(argc, argv, "ais")) != EOF) 864090Sache switch((char)ch) { 874090Sache case 'i': /* initial call, save offset */ 8815046Sache if (init != Unknown) 894090Sache goto usage; 9015046Sache init = True; 914090Sache break; 924090Sache case 'a': /* adjustment call, use saved offset */ 9315046Sache if (init != Unknown) 944090Sache goto usage; 9515046Sache init = False; 964090Sache break; 9715046Sache case 's': 9815046Sache sleep_mode = True; 9915046Sache break; 1004090Sache default: 1014090Sache usage: 1024090Sache fprintf(stderr, "Usage:\n\ 10315046Sache\tadjkerntz -i\t\t(initial call from /etc/rc)\n\ 10415046Sache\tadjkerntz -a [-s]\t(adjustment call, -s for sleep/retry mode)\n"); 1054090Sache return 2; 1064090Sache } 10715046Sache if (init == Unknown) 1084090Sache goto usage; 10915046Sache if (init) 11015046Sache sleep_mode = True; 1118871Srgrimes 1124107Sache sigemptyset(&mask); 1134107Sache sigemptyset(&emask); 1144107Sache sigaddset(&mask, SIGTERM); 1154107Sache 1164090Sache openlog("adjkerntz", LOG_PID|LOG_PERROR, LOG_DAEMON); 1174090Sache 1184107Sache (void) signal(SIGHUP, SIG_IGN); 1194107Sache 1204107Sache if (init && daemon(0, 1)) { 1214107Sache syslog(LOG_ERR, "daemon: %m"); 1224107Sache return 1; 1234107Sache } 1244107Sache 1254090Sacheagain: 1264107Sache (void) sigprocmask(SIG_BLOCK, &mask, NULL); 1274107Sache (void) signal(SIGTERM, fake); 1284107Sache 1297398Sache diff = 0; 1307398Sache stv = NULL; 1317398Sache stz = NULL; 13215046Sache looping = False; 1337398Sache 13415052Sache wall_clock = (access(_PATH_CLOCK, F_OK) == 0); 13515057Sache if (init && !sleep_mode) { 13615057Sache init = False; 13715057Sache if (!wall_clock) 13815057Sache return 0; 13915057Sache } 14015052Sache 1417398Sache mib[0] = CTL_MACHDEP; 1427398Sache mib[1] = CPU_ADJKERNTZ; 1437398Sache len = sizeof(kern_offset); 1447398Sache if (sysctl(mib, 2, &kern_offset, &len, NULL, 0) == -1) { 1457398Sache syslog(LOG_ERR, "sysctl(get_offset): %m"); 1467398Sache return 1; 1477398Sache } 1487398Sache 1494090Sache/****** Critical section, do all things as fast as possible ******/ 1504090Sache 1514090Sache /* get local CMOS clock and possible kernel offset */ 1524090Sache if (gettimeofday(&tv, &tz)) { 1534090Sache syslog(LOG_ERR, "gettimeofday: %m"); 1543368Sache return 1; 1553368Sache } 156867Sache 1574090Sache /* get the actual local timezone difference */ 1584090Sache initial_sec = tv.tv_sec; 1597398Sache 1607398Sacherecalculate: 1614090Sache local = *localtime(&initial_sec); 1627398Sache if (diff == 0) 1637398Sache initial_isdst = local.tm_isdst; 1644090Sache utc = *gmtime(&initial_sec); 1657398Sache local.tm_isdst = utc.tm_isdst = initial_isdst; 166867Sache 1674090Sache /* calculate local CMOS diff from GMT */ 168867Sache 1697398Sache utcsec = mktime(&utc); 1707398Sache localsec = mktime(&local); 1714090Sache if (utcsec == -1 || localsec == -1) { 1724090Sache /* 1734090Sache * XXX user can only control local time, and it is 1744090Sache * unacceptable to fail here for init. 2:30 am in the 1754090Sache * middle of the nonexistent hour means 3:30 am. 1764090Sache */ 1774090Sache syslog(LOG_WARNING, 17815046Sache "Warning: nonexistent %s time.", 17915046Sache utcsec == -1 && localsec == -1 ? "UTC time and local" : 18015046Sache utcsec == -1 ? "UTC" : "local"); 18115046Sache if (!sleep_mode) { 18215046Sache syslog(LOG_WARNING, "Giving up."); 18315046Sache return 1; 18415046Sache } 18515046Sache syslog(LOG_WARNING, "Will retry after %d minutes.", 18615046Sache REPORT_PERIOD / 60); 1874107Sache (void) signal(SIGTERM, SIG_DFL); 1884107Sache (void) sigprocmask(SIG_UNBLOCK, &mask, NULL); 1894090Sache (void) sleep(REPORT_PERIOD); 1904090Sache goto again; 1914090Sache } 1924090Sache offset = utcsec - localsec; 1935232Sache#ifdef DEBUG 1945232Sache fprintf(stderr, "Initial offset: %ld secs\n", offset); 1955232Sache#endif 196867Sache 1974090Sache /* correct the kerneltime for this diffs */ 1984090Sache /* subtract kernel offset, if present, old offset too */ 1994090Sache 2004090Sache diff = offset - tz.tz_minuteswest * 60 - kern_offset; 2014090Sache 2024090Sache if (diff != 0) { 2035232Sache#ifdef DEBUG 2045232Sache fprintf(stderr, "Initial diff: %ld secs\n", diff); 2055232Sache#endif 2064090Sache /* Yet one step for final time */ 2074090Sache 2087398Sache final_sec = initial_sec + diff; 2094090Sache 210882Sache /* get the actual local timezone difference */ 2114090Sache local = *localtime(&final_sec); 2127398Sache final_isdst = diff < 0 ? initial_isdst : local.tm_isdst; 2137398Sache if (diff > 0 && initial_isdst != final_isdst) { 2147398Sache if (looping) 2157398Sache goto bad_final; 21615046Sache looping = True; 2177398Sache initial_isdst = final_isdst; 2187398Sache goto recalculate; 2197398Sache } 2204090Sache utc = *gmtime(&final_sec); 2217398Sache local.tm_isdst = utc.tm_isdst = final_isdst; 222882Sache 2237398Sache utcsec = mktime(&utc); 2247398Sache localsec = mktime(&local); 225882Sache if (utcsec == -1 || localsec == -1) { 2267398Sache bad_final: 2271092Sache /* 2284090Sache * XXX as above. The user has even less control, 2294090Sache * but perhaps we never get here. 2301092Sache */ 2314090Sache syslog(LOG_WARNING, 23215046Sache "Warning: nonexistent final %s time.", 23315046Sache utcsec == -1 && localsec == -1 ? "UTC time and local" : 23415046Sache utcsec == -1 ? "UTC" : "local"); 23515046Sache if (!sleep_mode) { 23615046Sache syslog(LOG_WARNING, "Giving up."); 23715046Sache return 1; 23815046Sache } 23915046Sache syslog(LOG_WARNING, "Will retry after %d minutes.", 24015046Sache REPORT_PERIOD / 60); 2414107Sache (void) signal(SIGTERM, SIG_DFL); 2424107Sache (void) sigprocmask(SIG_UNBLOCK, &mask, NULL); 2434090Sache (void) sleep(REPORT_PERIOD); 2444090Sache goto again; 245882Sache } 246882Sache offset = utcsec - localsec; 2475232Sache#ifdef DEBUG 2485232Sache fprintf(stderr, "Final offset: %ld secs\n", offset); 2495232Sache#endif 250882Sache 251882Sache /* correct the kerneltime for this diffs */ 252882Sache /* subtract kernel offset, if present, old offset too */ 253882Sache 2542910Sache diff = offset - tz.tz_minuteswest * 60 - kern_offset; 255882Sache 256882Sache if (diff != 0) { 2575232Sache#ifdef DEBUG 2585232Sache fprintf(stderr, "Final diff: %ld secs\n", diff); 2595232Sache#endif 2604090Sache tv.tv_sec += diff; 2614090Sache tv.tv_usec = 0; /* we are restarting here... */ 2624090Sache stv = &tv; 263882Sache } 2644090Sache } 265867Sache 2664090Sache if (tz.tz_dsttime != 0 || tz.tz_minuteswest != 0) { 2674090Sache tz.tz_dsttime = tz.tz_minuteswest = 0; /* zone info is garbage */ 2684090Sache stz = &tz; 2694090Sache } 27015046Sache if (!wall_clock && stz == NULL) 27115046Sache stv = NULL; 272867Sache 27315046Sache /* if init or UTC clock and offset/date will be changed, */ 27415046Sache /* disable RTC modification for a while. */ 27515046Sache 27615046Sache if ( (init && stv != NULL) 27715046Sache || ((init || !wall_clock) && kern_offset != offset) 27815046Sache ) { 2795076Sache mib[0] = CTL_MACHDEP; 2805076Sache mib[1] = CPU_DISRTCSET; 2815076Sache len = sizeof(disrtcset); 2825076Sache if (sysctl(mib, 2, &disrtcset, &len, NULL, 0) == -1) { 2835076Sache syslog(LOG_ERR, "sysctl(get_disrtcset): %m"); 2845076Sache return 1; 2855076Sache } 2865076Sache if (disrtcset == 0) { 2875076Sache disrtcset = 1; 28815046Sache need_restore = True; 2895076Sache if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) { 2905076Sache syslog(LOG_ERR, "sysctl(set_disrtcset): %m"); 2914090Sache return 1; 2924090Sache } 2933368Sache } 2944090Sache } 2954039Sache 29615046Sache if ( ( (init && (stv != NULL || stz != NULL)) 29715046Sache || (stz != NULL && stv == NULL) 29815046Sache ) 2995076Sache && settimeofday(stv, stz) 3005076Sache ) { 3015076Sache syslog(LOG_ERR, "settimeofday: %m"); 3025076Sache return 1; 3035076Sache } 3045076Sache 30515046Sache /* setting CPU_ADJKERNTZ have a side effect: resettodr(), which */ 30615046Sache /* can be disabled by CPU_DISRTCSET, so if init or UTC clock */ 30715046Sache /* -- don't write RTC, else write RTC. */ 30815046Sache 3094090Sache if (kern_offset != offset) { 3104090Sache kern_offset = offset; 3114090Sache mib[0] = CTL_MACHDEP; 3124090Sache mib[1] = CPU_ADJKERNTZ; 3134090Sache len = sizeof(kern_offset); 3144090Sache if (sysctl(mib, 2, NULL, NULL, &kern_offset, len) == -1) { 3154090Sache syslog(LOG_ERR, "sysctl(update_offset): %m"); 3164090Sache return 1; 3173368Sache } 3184090Sache } 319867Sache 32015052Sache mib[0] = CTL_MACHDEP; 32115052Sache mib[1] = CPU_WALLCLOCK; 32215052Sache len = sizeof(wall_clock); 32315052Sache if (sysctl(mib, 2, NULL, NULL, &wall_clock, len) == -1) { 32415052Sache syslog(LOG_ERR, "sysctl(put_wallclock): %m"); 32515052Sache return 1; 32615046Sache } 32715046Sache 3284090Sache if (need_restore) { 32915046Sache need_restore = False; 3305078Sache mib[0] = CTL_MACHDEP; 3315078Sache mib[1] = CPU_DISRTCSET; 3324090Sache disrtcset = 0; 3335076Sache len = sizeof(disrtcset); 3344090Sache if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) { 3354090Sache syslog(LOG_ERR, "sysctl(restore_disrtcset): %m"); 3364090Sache return 1; 3374039Sache } 3384090Sache } 3394039Sache 340867Sache/****** End of critical section ******/ 341867Sache 34215046Sache if (init && wall_clock) { 34315057Sache sleep_mode = False; 3444107Sache /* wait for signals and acts like -a */ 3454107Sache (void) sigsuspend(&emask); 3464107Sache goto again; 3474107Sache } 3484107Sache 3494090Sache return 0; 350867Sache} 351