adjkerntz.c revision 36625
1867Sache/* 235142Sache * Copyright (C) 1993-1998 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 2836625Scharnierstatic const char copyright[] = 2915046Sache"@(#)Copyright (C) 1993-1996 by Andrey A. Chernov, Moscow, Russia.\n\ 30867Sache All rights reserved.\n"; 31867Sache#endif /* not lint */ 32867Sache 3336625Scharnier#ifndef lint 3436625Scharnierstatic const char rcsid[] = 3536625Scharnier "$Id$"; 3636625Scharnier#endif /* not lint */ 3736625Scharnier 38867Sache/* 397398Sache * Andrey A. Chernov <ache@astral.msk.su> Dec 20 1993 40867Sache * 41867Sache * Fix kernel time value if machine run wall CMOS clock 42867Sache * (and /etc/wall_cmos_clock file present) 43867Sache * using zoneinfo rules or direct TZ environment variable set. 44867Sache * Use Joerg Wunsch idea for seconds accurate offset calculation 45867Sache * with Garrett Wollman and Bruce Evans fixes. 46867Sache * 47867Sache */ 48867Sache#include <stdio.h> 494107Sache#include <signal.h> 50867Sache#include <stdlib.h> 51867Sache#include <unistd.h> 524048Sache#include <syslog.h> 53867Sache#include <sys/time.h> 542910Sache#include <sys/param.h> 552910Sache#include <machine/cpu.h> 562910Sache#include <sys/sysctl.h> 57867Sache 58867Sache#include "pathnames.h" 59867Sache 605232Sache/*#define DEBUG*/ 6115046Sache 6215046Sache#define True (1) 6315046Sache#define False (0) 6415046Sache#define Unknown (-1) 6515046Sache 664048Sache#define REPORT_PERIOD (30*60) 674048Sache 6826540Scharnierstatic void usage __P((void)); 6926540Scharnier 704107Sachevoid fake() {} 714107Sache 72867Sacheint main(argc, argv) 73867Sache int argc; 74867Sache char **argv; 75867Sache{ 7635142Sache struct tm local; 77867Sache struct timeval tv, *stv; 78867Sache struct timezone tz, *stz; 7915046Sache int kern_offset, wall_clock, disrtcset; 802910Sache size_t len; 812910Sache int mib[2]; 821092Sache /* Avoid time_t here, can be unsigned long or worse */ 8335142Sache long offset, localsec, diff; 841092Sache time_t initial_sec, final_sec; 8515046Sache int ch; 8615046Sache int initial_isdst = -1, final_isdst; 8715046Sache int need_restore = False, sleep_mode = False, looping, 8815046Sache init = Unknown; 894107Sache sigset_t mask, emask; 90867Sache 9124359Simp while ((ch = getopt(argc, argv, "ais")) != -1) 924090Sache switch((char)ch) { 934090Sache case 'i': /* initial call, save offset */ 9415046Sache if (init != Unknown) 9526540Scharnier usage(); 9615046Sache init = True; 974090Sache break; 984090Sache case 'a': /* adjustment call, use saved offset */ 9915046Sache if (init != Unknown) 10026540Scharnier usage(); 10115046Sache init = False; 1024090Sache break; 10315046Sache case 's': 10415046Sache sleep_mode = True; 10515046Sache break; 1064090Sache default: 10726540Scharnier usage(); 1084090Sache } 10915046Sache if (init == Unknown) 11026540Scharnier usage(); 11135142Sache 11235142Sache if (access(_PATH_CLOCK, F_OK) != 0) 11335142Sache return 0; 11435142Sache 11515046Sache if (init) 11615046Sache sleep_mode = True; 1178871Srgrimes 1184107Sache sigemptyset(&mask); 1194107Sache sigemptyset(&emask); 1204107Sache sigaddset(&mask, SIGTERM); 1214107Sache 1224090Sache openlog("adjkerntz", LOG_PID|LOG_PERROR, LOG_DAEMON); 1234090Sache 1244107Sache (void) signal(SIGHUP, SIG_IGN); 1254107Sache 1264107Sache if (init && daemon(0, 1)) { 1274107Sache syslog(LOG_ERR, "daemon: %m"); 1284107Sache return 1; 1294107Sache } 1304107Sache 1314090Sacheagain: 1324107Sache (void) sigprocmask(SIG_BLOCK, &mask, NULL); 1334107Sache (void) signal(SIGTERM, fake); 1344107Sache 1357398Sache diff = 0; 1367398Sache stv = NULL; 1377398Sache stz = NULL; 13815046Sache looping = False; 1397398Sache 14015052Sache wall_clock = (access(_PATH_CLOCK, F_OK) == 0); 14115057Sache if (init && !sleep_mode) { 14215057Sache init = False; 14315057Sache if (!wall_clock) 14415057Sache return 0; 14515057Sache } 14615052Sache 1477398Sache mib[0] = CTL_MACHDEP; 1487398Sache mib[1] = CPU_ADJKERNTZ; 1497398Sache len = sizeof(kern_offset); 1507398Sache if (sysctl(mib, 2, &kern_offset, &len, NULL, 0) == -1) { 1517398Sache syslog(LOG_ERR, "sysctl(get_offset): %m"); 1527398Sache return 1; 1537398Sache } 1547398Sache 1554090Sache/****** Critical section, do all things as fast as possible ******/ 1564090Sache 1574090Sache /* get local CMOS clock and possible kernel offset */ 1584090Sache if (gettimeofday(&tv, &tz)) { 1594090Sache syslog(LOG_ERR, "gettimeofday: %m"); 1603368Sache return 1; 1613368Sache } 162867Sache 1634090Sache /* get the actual local timezone difference */ 1644090Sache initial_sec = tv.tv_sec; 1657398Sache 1667398Sacherecalculate: 1674090Sache local = *localtime(&initial_sec); 1687398Sache if (diff == 0) 1697398Sache initial_isdst = local.tm_isdst; 17035142Sache local.tm_isdst = initial_isdst; 171867Sache 1724090Sache /* calculate local CMOS diff from GMT */ 173867Sache 1747398Sache localsec = mktime(&local); 17535142Sache if (localsec == -1) { 1764090Sache /* 1774090Sache * XXX user can only control local time, and it is 1784090Sache * unacceptable to fail here for init. 2:30 am in the 1794090Sache * middle of the nonexistent hour means 3:30 am. 1804090Sache */ 18115046Sache if (!sleep_mode) { 18235142Sache syslog(LOG_WARNING, 18335142Sache "Warning: nonexistent local time, try to run later."); 18415046Sache syslog(LOG_WARNING, "Giving up."); 18515046Sache return 1; 18615046Sache } 18735142Sache syslog(LOG_WARNING, 18835142Sache "Warning: nonexistent local time."); 18915046Sache syslog(LOG_WARNING, "Will retry after %d minutes.", 19015046Sache REPORT_PERIOD / 60); 1914107Sache (void) signal(SIGTERM, SIG_DFL); 1924107Sache (void) sigprocmask(SIG_UNBLOCK, &mask, NULL); 1934090Sache (void) sleep(REPORT_PERIOD); 1944090Sache goto again; 1954090Sache } 19635142Sache offset = -local.tm_gmtoff; 1975232Sache#ifdef DEBUG 1985232Sache fprintf(stderr, "Initial offset: %ld secs\n", offset); 1995232Sache#endif 200867Sache 2014090Sache /* correct the kerneltime for this diffs */ 2024090Sache /* subtract kernel offset, if present, old offset too */ 2034090Sache 2044090Sache diff = offset - tz.tz_minuteswest * 60 - kern_offset; 2054090Sache 2064090Sache if (diff != 0) { 2075232Sache#ifdef DEBUG 2085232Sache fprintf(stderr, "Initial diff: %ld secs\n", diff); 2095232Sache#endif 2104090Sache /* Yet one step for final time */ 2114090Sache 2127398Sache final_sec = initial_sec + diff; 2134090Sache 214882Sache /* get the actual local timezone difference */ 2154090Sache local = *localtime(&final_sec); 2167398Sache final_isdst = diff < 0 ? initial_isdst : local.tm_isdst; 2177398Sache if (diff > 0 && initial_isdst != final_isdst) { 2187398Sache if (looping) 2197398Sache goto bad_final; 22015046Sache looping = True; 2217398Sache initial_isdst = final_isdst; 2227398Sache goto recalculate; 2237398Sache } 22435142Sache local.tm_isdst = final_isdst; 225882Sache 2267398Sache localsec = mktime(&local); 22735142Sache if (localsec == -1) { 2287398Sache bad_final: 2291092Sache /* 2304090Sache * XXX as above. The user has even less control, 2314090Sache * but perhaps we never get here. 2321092Sache */ 23315046Sache if (!sleep_mode) { 23435142Sache syslog(LOG_WARNING, 23535142Sache "Warning: nonexistent final local time, try to run later."); 23615046Sache syslog(LOG_WARNING, "Giving up."); 23715046Sache return 1; 23815046Sache } 23935142Sache syslog(LOG_WARNING, 24035142Sache "Warning: nonexistent final local time."); 24115046Sache syslog(LOG_WARNING, "Will retry after %d minutes.", 24215046Sache REPORT_PERIOD / 60); 2434107Sache (void) signal(SIGTERM, SIG_DFL); 2444107Sache (void) sigprocmask(SIG_UNBLOCK, &mask, NULL); 2454090Sache (void) sleep(REPORT_PERIOD); 2464090Sache goto again; 247882Sache } 24835142Sache offset = -local.tm_gmtoff; 2495232Sache#ifdef DEBUG 2505232Sache fprintf(stderr, "Final offset: %ld secs\n", offset); 2515232Sache#endif 252882Sache 253882Sache /* correct the kerneltime for this diffs */ 254882Sache /* subtract kernel offset, if present, old offset too */ 255882Sache 2562910Sache diff = offset - tz.tz_minuteswest * 60 - kern_offset; 257882Sache 258882Sache if (diff != 0) { 2595232Sache#ifdef DEBUG 2605232Sache fprintf(stderr, "Final diff: %ld secs\n", diff); 2615232Sache#endif 26233831Sache /* 26333831Sache * stv is abused as a flag. The important value 26433831Sache * is in `diff'. 26533831Sache */ 2664090Sache stv = &tv; 267882Sache } 2684090Sache } 269867Sache 2704090Sache if (tz.tz_dsttime != 0 || tz.tz_minuteswest != 0) { 2714090Sache tz.tz_dsttime = tz.tz_minuteswest = 0; /* zone info is garbage */ 2724090Sache stz = &tz; 2734090Sache } 27415046Sache if (!wall_clock && stz == NULL) 27515046Sache stv = NULL; 276867Sache 27715046Sache /* if init or UTC clock and offset/date will be changed, */ 27815046Sache /* disable RTC modification for a while. */ 27915046Sache 28015046Sache if ( (init && stv != NULL) 28115046Sache || ((init || !wall_clock) && kern_offset != offset) 28215046Sache ) { 2835076Sache mib[0] = CTL_MACHDEP; 2845076Sache mib[1] = CPU_DISRTCSET; 2855076Sache len = sizeof(disrtcset); 2865076Sache if (sysctl(mib, 2, &disrtcset, &len, NULL, 0) == -1) { 2875076Sache syslog(LOG_ERR, "sysctl(get_disrtcset): %m"); 2885076Sache return 1; 2895076Sache } 2905076Sache if (disrtcset == 0) { 2915076Sache disrtcset = 1; 29215046Sache need_restore = True; 2935076Sache if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) { 2945076Sache syslog(LOG_ERR, "sysctl(set_disrtcset): %m"); 2954090Sache return 1; 2964090Sache } 2973368Sache } 2984090Sache } 2994039Sache 30033831Sache if ( (init && (stv != NULL || stz != NULL)) 30133831Sache || (stz != NULL && stv == NULL) 3025076Sache ) { 30333831Sache if (stv != NULL) { 30433831Sache /* 30533831Sache * Get the time again, as close as possible to 30633831Sache * adjusting it, to minimise drift. 30733831Sache * XXX we'd better not fail between here and 30833831Sache * restoring disrtcset, since we don't clean up 30933831Sache * anything. 31033831Sache */ 31133831Sache if (gettimeofday(&tv, (struct timezone *)NULL)) { 31233831Sache syslog(LOG_ERR, "gettimeofday: %m"); 31333831Sache return 1; 31433831Sache } 31533831Sache tv.tv_sec += diff; 31633831Sache stv = &tv; 31733831Sache } 31833831Sache if (settimeofday(stv, stz)) { 31933831Sache syslog(LOG_ERR, "settimeofday: %m"); 32033831Sache return 1; 32133831Sache } 3225076Sache } 3235076Sache 32415046Sache /* setting CPU_ADJKERNTZ have a side effect: resettodr(), which */ 32515046Sache /* can be disabled by CPU_DISRTCSET, so if init or UTC clock */ 32615046Sache /* -- don't write RTC, else write RTC. */ 32715046Sache 3284090Sache if (kern_offset != offset) { 3294090Sache kern_offset = offset; 3304090Sache mib[0] = CTL_MACHDEP; 3314090Sache mib[1] = CPU_ADJKERNTZ; 3324090Sache len = sizeof(kern_offset); 3334090Sache if (sysctl(mib, 2, NULL, NULL, &kern_offset, len) == -1) { 3344090Sache syslog(LOG_ERR, "sysctl(update_offset): %m"); 3354090Sache return 1; 3363368Sache } 3374090Sache } 338867Sache 33915052Sache mib[0] = CTL_MACHDEP; 34015052Sache mib[1] = CPU_WALLCLOCK; 34115052Sache len = sizeof(wall_clock); 34215052Sache if (sysctl(mib, 2, NULL, NULL, &wall_clock, len) == -1) { 34315052Sache syslog(LOG_ERR, "sysctl(put_wallclock): %m"); 34415052Sache return 1; 34515046Sache } 34615046Sache 3474090Sache if (need_restore) { 34815046Sache need_restore = False; 3495078Sache mib[0] = CTL_MACHDEP; 3505078Sache mib[1] = CPU_DISRTCSET; 3514090Sache disrtcset = 0; 3525076Sache len = sizeof(disrtcset); 3534090Sache if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) { 3544090Sache syslog(LOG_ERR, "sysctl(restore_disrtcset): %m"); 3554090Sache return 1; 3564039Sache } 3574090Sache } 3584039Sache 359867Sache/****** End of critical section ******/ 360867Sache 36115046Sache if (init && wall_clock) { 36215057Sache sleep_mode = False; 3634107Sache /* wait for signals and acts like -a */ 3644107Sache (void) sigsuspend(&emask); 3654107Sache goto again; 3664107Sache } 3674107Sache 3684090Sache return 0; 369867Sache} 37026540Scharnier 37126540Scharnierstatic void 37226540Scharnierusage() 37326540Scharnier{ 37426540Scharnier fprintf(stderr, "%s\n%s\n%s\n%s\n", 37526540Scharnier "usage: adjkerntz -i", 37626540Scharnier "\t\t(initial call from /etc/rc)", 37726540Scharnier " adjkerntz -a [-s]", 37826540Scharnier "\t\t(adjustment call, -s for sleep/retry mode)"); 37926540Scharnier exit(2); 38026540Scharnier} 381