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