adjkerntz.c revision 882
1867Sache/*
2867Sache * Copyright (C) 1993 by Andrew 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 *
14867Sache * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``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[] =
29867Sache"@(#)Copyright (C) 1993 by Andrew A. Chernov, Moscow, Russia.\n\
30867Sache All rights reserved.\n";
31867Sache#endif /* not lint */
32867Sache
33867Sache/*
34882Sache * Andrew 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>
44867Sache#include <stdlib.h>
45867Sache#include <unistd.h>
46867Sache#include <sys/stat.h>
47867Sache#include <sys/time.h>
48867Sache
49867Sache#include "pathnames.h"
50867Sache
51867Sachechar storage[] = _PATH_OFFSET;
52867Sache
53867Sacheint main(argc, argv)
54867Sache	int argc;
55867Sache	char **argv;
56867Sache{
57867Sache	struct tm local, utc;
58867Sache	struct timeval tv, *stv;
59867Sache	struct timezone tz, *stz;
60867Sache	/* Avoid time_t here, can be unsigned long */
61867Sache	long offset, oldoffset, utcsec, localsec, diff;
62882Sache	time_t final_sec;
63867Sache	int ch, init = -1, verbose = 0;
64867Sache	FILE *f;
65867Sache
66867Sache	while ((ch = getopt(argc, argv, "aiv")) != EOF)
67867Sache		switch((char)ch) {
68867Sache		case 'i':               /* initial call, save offset */
69867Sache			if (init != -1)
70867Sache				goto usage;
71867Sache			init = 1;
72867Sache			break;
73867Sache		case 'a':               /* adjustment call, use saved offset */
74867Sache			if (init != -1)
75867Sache				goto usage;
76867Sache			init = 0;
77867Sache			break;
78867Sache		case 'v':               /* verbose */
79867Sache			verbose = 1;
80867Sache			break;
81867Sache		default:
82867Sache		usage:
83867Sache			fprintf(stderr, "Usage:\n\
84867Sache\tadjkerntz -i [-v]\t(initial call from /etc/rc)\n\
85867Sache\tadjkerntz -a [-v]\t(adjustment call from crontab)\n");
86867Sache			return 2;
87867Sache		}
88867Sache	if (init == -1)
89867Sache		goto usage;
90867Sache
91867Sache	if (access(_PATH_CLOCK, F_OK))
92867Sache		return 0;
93867Sache
94867Sache	/* Restore saved offset */
95867Sache
96867Sache	if (!init) {
97867Sache		if ((f = fopen(storage, "r")) == NULL) {
98867Sache			perror(storage);
99867Sache			return 1;
100867Sache		}
101867Sache		if (fscanf(f, "%ld", &oldoffset) != 1) {
102867Sache			fprintf(stderr, "Incorrect offset in %s\n", storage);
103867Sache			return 1;
104867Sache		}
105867Sache		(void) fclose(f);
106867Sache	}
107867Sache	else
108867Sache		oldoffset = 0;
109867Sache
110867Sache/****** Critical section, do all things as fast as possible ******/
111867Sache
112867Sache	/* get local CMOS clock and possible kernel offset */
113867Sache	if (gettimeofday(&tv, &tz)) {
114867Sache		perror("gettimeofday");
115867Sache		return 1;
116867Sache	}
117867Sache
118867Sache	/* get the actual local timezone difference */
119867Sache	local = *localtime(&tv.tv_sec);
120867Sache	utc = *gmtime(&tv.tv_sec);
121867Sache	utc.tm_isdst = local.tm_isdst; /* Use current timezone for mktime(), */
122867Sache				       /* because it assumed local time */
123867Sache
124867Sache	/* calculate local CMOS diff from GMT */
125867Sache
126867Sache	utcsec = mktime(&utc);
127867Sache	localsec = mktime(&local);
128867Sache	if (utcsec == -1 || localsec == -1) {
129882Sache		fprintf(stderr, "Wrong initial hour to call\n");
130867Sache		return 1;
131867Sache	}
132867Sache	offset = utcsec - localsec;
133867Sache
134867Sache	/* correct the kerneltime for this diffs */
135867Sache	/* subtract kernel offset, if present, old offset too */
136867Sache
137882Sache	diff = offset - tz.tz_minuteswest * 60 - oldoffset;
138882Sache
139867Sache	if (diff != 0) {
140882Sache
141882Sache		/* Yet one step for final time */
142882Sache
143882Sache		final_sec = tv.tv_sec + diff;
144882Sache
145882Sache		/* get the actual local timezone difference */
146882Sache		local = *localtime(&final_sec);
147882Sache		utc = *gmtime(&final_sec);
148882Sache		utc.tm_isdst = local.tm_isdst; /* Use current timezone for mktime(), */
149882Sache					       /* because it assumed local time */
150882Sache
151882Sache		utcsec = mktime(&utc);
152882Sache		localsec = mktime(&local);
153882Sache		if (utcsec == -1 || localsec == -1) {
154882Sache			fprintf(stderr, "Wrong final hour to call\n");
155882Sache			return 1;
156882Sache		}
157882Sache		offset = utcsec - localsec;
158882Sache
159882Sache		/* correct the kerneltime for this diffs */
160882Sache		/* subtract kernel offset, if present, old offset too */
161882Sache
162882Sache		diff = offset - tz.tz_minuteswest * 60 - oldoffset;
163882Sache
164882Sache		if (diff != 0) {
165882Sache			tv.tv_sec += diff;
166882Sache			tv.tv_usec = 0;       /* we are restarting here... */
167882Sache			stv = &tv;
168882Sache		}
169882Sache		else
170882Sache			stv = NULL;
171867Sache	}
172867Sache	else
173867Sache		stv = NULL;
174867Sache
175867Sache	if (tz.tz_dsttime != 0 || tz.tz_minuteswest != 0) {
176867Sache		tz.tz_dsttime = tz.tz_minuteswest = 0;  /* zone info is garbage */
177867Sache		stz = &tz;
178867Sache	}
179867Sache	else
180867Sache		stz = NULL;
181867Sache
182867Sache	if (stz != NULL || stv != NULL) {
183867Sache		if (settimeofday(stv, stz)) {
184867Sache			perror("settimeofday");
185867Sache			return 1;
186867Sache		}
187867Sache	}
188867Sache
189867Sache/****** End of critical section ******/
190867Sache
191867Sache	if (verbose)
192867Sache		printf("Calculated zone offset diffs: %ld seconds\n", diff);
193867Sache
194867Sache	if (offset != oldoffset) {
195867Sache		(void) umask(022);
196867Sache		/* Save offset for next calls from crontab */
197867Sache		if ((f = fopen(storage, "w")) == NULL) {
198867Sache			perror(storage);
199867Sache			return 1;
200867Sache		}
201867Sache		fprintf(f, "%ld\n", offset);
202867Sache		if (fclose(f)) {
203867Sache			perror(storage);
204867Sache			return 1;
205867Sache		}
206867Sache	}
207867Sache
208867Sache	return 0;
209867Sache}
210867Sache
211