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 27114589Sobrien#if 0 28867Sache#ifndef lint 2936625Scharnierstatic const char copyright[] = 3015046Sache"@(#)Copyright (C) 1993-1996 by Andrey A. Chernov, Moscow, Russia.\n\ 31867Sache All rights reserved.\n"; 32867Sache#endif /* not lint */ 33114589Sobrien#endif 34114589Sobrien#include <sys/cdefs.h> 35114589Sobrien__FBSDID("$FreeBSD$"); 36867Sache 37867Sache/* 387398Sache * Andrey A. Chernov <ache@astral.msk.su> Dec 20 1993 39867Sache * 40867Sache * Fix kernel time value if machine run wall CMOS clock 41867Sache * (and /etc/wall_cmos_clock file present) 42867Sache * using zoneinfo rules or direct TZ environment variable set. 43867Sache * Use Joerg Wunsch idea for seconds accurate offset calculation 44867Sache * with Garrett Wollman and Bruce Evans fixes. 45867Sache * 46867Sache */ 47867Sache#include <stdio.h> 484107Sache#include <signal.h> 49867Sache#include <stdlib.h> 50867Sache#include <unistd.h> 514048Sache#include <syslog.h> 52867Sache#include <sys/time.h> 532910Sache#include <sys/param.h> 54253750Savg#include <machine/cpu.h> 552910Sache#include <sys/sysctl.h> 56867Sache 57867Sache#include "pathnames.h" 58867Sache 595232Sache/*#define DEBUG*/ 6015046Sache 6115046Sache#define True (1) 6215046Sache#define False (0) 6315046Sache#define Unknown (-1) 6415046Sache 654048Sache#define REPORT_PERIOD (30*60) 664048Sache 6792536Simpstatic void fake(int); 6892536Simpstatic void usage(void); 6926540Scharnier 7079747Sddstatic void 7192536Simpfake(int unused __unused) 7279747Sdd{ 734107Sache 7479747Sdd /* Do nothing. */ 7579747Sdd} 7679747Sdd 7792536Simpint 7892536Simpmain(int argc, char *argv[]) 79867Sache{ 8035142Sache struct tm local; 81867Sache struct timeval tv, *stv; 82867Sache struct timezone tz, *stz; 8315046Sache int kern_offset, wall_clock, disrtcset; 842910Sache size_t len; 851092Sache /* Avoid time_t here, can be unsigned long or worse */ 8635142Sache long offset, localsec, diff; 871092Sache time_t initial_sec, final_sec; 8815046Sache int ch; 8915046Sache int initial_isdst = -1, final_isdst; 9015046Sache int need_restore = False, sleep_mode = False, looping, 9115046Sache init = Unknown; 924107Sache sigset_t mask, emask; 93867Sache 9424359Simp while ((ch = getopt(argc, argv, "ais")) != -1) 954090Sache switch((char)ch) { 964090Sache case 'i': /* initial call, save offset */ 9715046Sache if (init != Unknown) 9826540Scharnier usage(); 9915046Sache init = True; 1004090Sache break; 1014090Sache case 'a': /* adjustment call, use saved offset */ 10215046Sache if (init != Unknown) 10326540Scharnier usage(); 10415046Sache init = False; 1054090Sache break; 10615046Sache case 's': 10715046Sache sleep_mode = True; 10815046Sache break; 1094090Sache default: 11026540Scharnier usage(); 1114090Sache } 11215046Sache if (init == Unknown) 11326540Scharnier usage(); 11435142Sache 11535142Sache if (access(_PATH_CLOCK, F_OK) != 0) 11635142Sache return 0; 11735142Sache 11815046Sache if (init) 11915046Sache sleep_mode = True; 1208871Srgrimes 1214107Sache sigemptyset(&mask); 1224107Sache sigemptyset(&emask); 1234107Sache sigaddset(&mask, SIGTERM); 1244107Sache 1254090Sache openlog("adjkerntz", LOG_PID|LOG_PERROR, LOG_DAEMON); 1264090Sache 1274107Sache (void) signal(SIGHUP, SIG_IGN); 1284107Sache 129163628Sru if (init && daemon(0, 130163628Sru#ifdef DEBUG 131163628Sru 1 132163628Sru#else 133163628Sru 0 134163628Sru#endif 135163628Sru )) { 1364107Sache syslog(LOG_ERR, "daemon: %m"); 1374107Sache return 1; 1384107Sache } 1394107Sache 1404090Sacheagain: 1414107Sache (void) sigprocmask(SIG_BLOCK, &mask, NULL); 1424107Sache (void) signal(SIGTERM, fake); 1434107Sache 1447398Sache diff = 0; 1457398Sache stv = NULL; 1467398Sache stz = NULL; 14715046Sache looping = False; 1487398Sache 14915052Sache wall_clock = (access(_PATH_CLOCK, F_OK) == 0); 15015057Sache if (init && !sleep_mode) { 15115057Sache init = False; 15215057Sache if (!wall_clock) 15315057Sache return 0; 15415057Sache } 15515052Sache 156128683Sghelmer tzset(); 157128683Sghelmer 1587398Sache len = sizeof(kern_offset); 159158444Sphk if (sysctlbyname("machdep.adjkerntz", &kern_offset, &len, NULL, 0) == -1) { 160158444Sphk syslog(LOG_ERR, "sysctl(\"machdep.adjkerntz\"): %m"); 1617398Sache return 1; 1627398Sache } 1637398Sache 1644090Sache/****** Critical section, do all things as fast as possible ******/ 1654090Sache 1664090Sache /* get local CMOS clock and possible kernel offset */ 1674090Sache if (gettimeofday(&tv, &tz)) { 1684090Sache syslog(LOG_ERR, "gettimeofday: %m"); 1693368Sache return 1; 1703368Sache } 171867Sache 1724090Sache /* get the actual local timezone difference */ 1734090Sache initial_sec = tv.tv_sec; 1747398Sache 1757398Sacherecalculate: 1764090Sache local = *localtime(&initial_sec); 1777398Sache if (diff == 0) 1787398Sache initial_isdst = local.tm_isdst; 17935142Sache local.tm_isdst = initial_isdst; 180867Sache 1814090Sache /* calculate local CMOS diff from GMT */ 182867Sache 1837398Sache localsec = mktime(&local); 18435142Sache if (localsec == -1) { 1854090Sache /* 1864090Sache * XXX user can only control local time, and it is 1874090Sache * unacceptable to fail here for init. 2:30 am in the 1884090Sache * middle of the nonexistent hour means 3:30 am. 1894090Sache */ 19015046Sache if (!sleep_mode) { 19135142Sache syslog(LOG_WARNING, 19235142Sache "Warning: nonexistent local time, try to run later."); 19315046Sache syslog(LOG_WARNING, "Giving up."); 19415046Sache return 1; 19515046Sache } 19635142Sache syslog(LOG_WARNING, 19735142Sache "Warning: nonexistent local time."); 19815046Sache syslog(LOG_WARNING, "Will retry after %d minutes.", 19915046Sache REPORT_PERIOD / 60); 2004107Sache (void) signal(SIGTERM, SIG_DFL); 2014107Sache (void) sigprocmask(SIG_UNBLOCK, &mask, NULL); 2024090Sache (void) sleep(REPORT_PERIOD); 2034090Sache goto again; 2044090Sache } 20535142Sache offset = -local.tm_gmtoff; 2065232Sache#ifdef DEBUG 2075232Sache fprintf(stderr, "Initial offset: %ld secs\n", offset); 2085232Sache#endif 209867Sache 2104090Sache /* correct the kerneltime for this diffs */ 2114090Sache /* subtract kernel offset, if present, old offset too */ 2124090Sache 2134090Sache diff = offset - tz.tz_minuteswest * 60 - kern_offset; 2144090Sache 2154090Sache if (diff != 0) { 2165232Sache#ifdef DEBUG 2175232Sache fprintf(stderr, "Initial diff: %ld secs\n", diff); 2185232Sache#endif 2194090Sache /* Yet one step for final time */ 2204090Sache 2217398Sache final_sec = initial_sec + diff; 2224090Sache 223882Sache /* get the actual local timezone difference */ 2244090Sache local = *localtime(&final_sec); 2257398Sache final_isdst = diff < 0 ? initial_isdst : local.tm_isdst; 2267398Sache if (diff > 0 && initial_isdst != final_isdst) { 2277398Sache if (looping) 2287398Sache goto bad_final; 22915046Sache looping = True; 2307398Sache initial_isdst = final_isdst; 2317398Sache goto recalculate; 2327398Sache } 23335142Sache local.tm_isdst = final_isdst; 234882Sache 2357398Sache localsec = mktime(&local); 23635142Sache if (localsec == -1) { 2377398Sache bad_final: 2381092Sache /* 2394090Sache * XXX as above. The user has even less control, 2404090Sache * but perhaps we never get here. 2411092Sache */ 24215046Sache if (!sleep_mode) { 24335142Sache syslog(LOG_WARNING, 24435142Sache "Warning: nonexistent final local time, try to run later."); 24515046Sache syslog(LOG_WARNING, "Giving up."); 24615046Sache return 1; 24715046Sache } 24835142Sache syslog(LOG_WARNING, 24935142Sache "Warning: nonexistent final local time."); 25015046Sache syslog(LOG_WARNING, "Will retry after %d minutes.", 25115046Sache REPORT_PERIOD / 60); 2524107Sache (void) signal(SIGTERM, SIG_DFL); 2534107Sache (void) sigprocmask(SIG_UNBLOCK, &mask, NULL); 2544090Sache (void) sleep(REPORT_PERIOD); 2554090Sache goto again; 256882Sache } 25735142Sache offset = -local.tm_gmtoff; 2585232Sache#ifdef DEBUG 2595232Sache fprintf(stderr, "Final offset: %ld secs\n", offset); 2605232Sache#endif 261882Sache 262882Sache /* correct the kerneltime for this diffs */ 263882Sache /* subtract kernel offset, if present, old offset too */ 264882Sache 2652910Sache diff = offset - tz.tz_minuteswest * 60 - kern_offset; 266882Sache 267882Sache if (diff != 0) { 2685232Sache#ifdef DEBUG 2695232Sache fprintf(stderr, "Final diff: %ld secs\n", diff); 2705232Sache#endif 27133831Sache /* 27233831Sache * stv is abused as a flag. The important value 27333831Sache * is in `diff'. 27433831Sache */ 2754090Sache stv = &tv; 276882Sache } 2774090Sache } 278867Sache 2794090Sache if (tz.tz_dsttime != 0 || tz.tz_minuteswest != 0) { 2804090Sache tz.tz_dsttime = tz.tz_minuteswest = 0; /* zone info is garbage */ 2814090Sache stz = &tz; 2824090Sache } 28315046Sache if (!wall_clock && stz == NULL) 28415046Sache stv = NULL; 285867Sache 28615046Sache /* if init or UTC clock and offset/date will be changed, */ 28715046Sache /* disable RTC modification for a while. */ 28815046Sache 28915046Sache if ( (init && stv != NULL) 29015046Sache || ((init || !wall_clock) && kern_offset != offset) 29115046Sache ) { 2925076Sache len = sizeof(disrtcset); 293158444Sphk if (sysctlbyname("machdep.disable_rtc_set", &disrtcset, &len, NULL, 0) == -1) { 294158516Spjd syslog(LOG_ERR, "sysctl(get: \"machdep.disable_rtc_set\"): %m"); 2955076Sache return 1; 2965076Sache } 2975076Sache if (disrtcset == 0) { 2985076Sache disrtcset = 1; 29915046Sache need_restore = True; 300158516Spjd if (sysctlbyname("machdep.disable_rtc_set", NULL, NULL, &disrtcset, len) == -1) { 301158516Spjd syslog(LOG_ERR, "sysctl(set: \"machdep.disable_rtc_set\"): %m"); 3024090Sache return 1; 3034090Sache } 3043368Sache } 3054090Sache } 3064039Sache 30733831Sache if ( (init && (stv != NULL || stz != NULL)) 30833831Sache || (stz != NULL && stv == NULL) 3095076Sache ) { 31033831Sache if (stv != NULL) { 31133831Sache /* 31233831Sache * Get the time again, as close as possible to 31333831Sache * adjusting it, to minimise drift. 31433831Sache * XXX we'd better not fail between here and 31533831Sache * restoring disrtcset, since we don't clean up 31633831Sache * anything. 31733831Sache */ 318239991Sed (void)gettimeofday(&tv, NULL); 31933831Sache tv.tv_sec += diff; 32033831Sache stv = &tv; 32133831Sache } 32233831Sache if (settimeofday(stv, stz)) { 32333831Sache syslog(LOG_ERR, "settimeofday: %m"); 32433831Sache return 1; 32533831Sache } 3265076Sache } 3275076Sache 328158444Sphk /* setting machdep.adjkerntz have a side effect: resettodr(), which */ 329158444Sphk /* can be disabled by machdep.disable_rtc_set, so if init or UTC clock */ 33015046Sache /* -- don't write RTC, else write RTC. */ 33115046Sache 3324090Sache if (kern_offset != offset) { 3334090Sache kern_offset = offset; 3344090Sache len = sizeof(kern_offset); 335158444Sphk if (sysctlbyname("machdep.adjkerntz", NULL, NULL, &kern_offset, len) == -1) { 336158516Spjd syslog(LOG_ERR, "sysctl(set: \"machdep.adjkerntz\"): %m"); 3374090Sache return 1; 3383368Sache } 3394090Sache } 340867Sache 34115052Sache len = sizeof(wall_clock); 342158444Sphk if (sysctlbyname("machdep.wall_cmos_clock", NULL, NULL, &wall_clock, len) == -1) { 343158444Sphk syslog(LOG_ERR, "sysctl(set: \"machdep.wall_cmos_clock\"): %m"); 34415052Sache return 1; 34515046Sache } 34615046Sache 3474090Sache if (need_restore) { 34815046Sache need_restore = False; 3494090Sache disrtcset = 0; 3505076Sache len = sizeof(disrtcset); 351158444Sphk if (sysctlbyname("machdep.disable_rtc_set", NULL, NULL, &disrtcset, len) == -1) { 352158444Sphk syslog(LOG_ERR, "sysctl(set: \"machdep.disable_rtc_set\"): %m"); 3534090Sache return 1; 3544039Sache } 3554090Sache } 3564039Sache 357867Sache/****** End of critical section ******/ 358867Sache 35915046Sache if (init && wall_clock) { 36015057Sache sleep_mode = False; 3614107Sache /* wait for signals and acts like -a */ 3624107Sache (void) sigsuspend(&emask); 3634107Sache goto again; 3644107Sache } 3654107Sache 3664090Sache return 0; 367867Sache} 36826540Scharnier 36926540Scharnierstatic void 37092536Simpusage(void) 37126540Scharnier{ 37226540Scharnier fprintf(stderr, "%s\n%s\n%s\n%s\n", 37326540Scharnier "usage: adjkerntz -i", 37426540Scharnier "\t\t(initial call from /etc/rc)", 37526540Scharnier " adjkerntz -a [-s]", 37626540Scharnier "\t\t(adjustment call, -s for sleep/retry mode)"); 37726540Scharnier exit(2); 37826540Scharnier} 379