]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/adjkerntz/adjkerntz.c
This commit was generated by cvs2svn to compensate for changes in r158795,
[FreeBSD/FreeBSD.git] / sbin / adjkerntz / adjkerntz.c
1 /*
2  * Copyright (C) 1993-1998 by Andrey A. Chernov, Moscow, Russia.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #if 0
28 #ifndef lint
29 static const char copyright[] =
30 "@(#)Copyright (C) 1993-1996 by Andrey A. Chernov, Moscow, Russia.\n\
31  All rights reserved.\n";
32 #endif /* not lint */
33 #endif
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 /*
38  * Andrey A. Chernov   <ache@astral.msk.su>    Dec 20 1993
39  *
40  * Fix kernel time value if machine run wall CMOS clock
41  * (and /etc/wall_cmos_clock file present)
42  * using zoneinfo rules or direct TZ environment variable set.
43  * Use Joerg Wunsch idea for seconds accurate offset calculation
44  * with Garrett Wollman and Bruce Evans fixes.
45  *
46  */
47 #include <stdio.h>
48 #include <signal.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <syslog.h>
52 #include <sys/time.h>
53 #include <sys/param.h>
54 #include <machine/cpu.h>
55 #include <sys/sysctl.h>
56
57 #include "pathnames.h"
58
59 /*#define DEBUG*/
60
61 #define True (1)
62 #define False (0)
63 #define Unknown (-1)
64
65 #define REPORT_PERIOD (30*60)
66
67 static void fake(int);
68 static void usage(void);
69
70 static void
71 fake(int unused __unused)
72 {
73
74         /* Do nothing. */
75 }
76
77 int
78 main(int argc, char *argv[])
79 {
80         struct tm local;
81         struct timeval tv, *stv;
82         struct timezone tz, *stz;
83         int kern_offset, wall_clock, disrtcset;
84         size_t len;
85         /* Avoid time_t here, can be unsigned long or worse */
86         long offset, localsec, diff;
87         time_t initial_sec, final_sec;
88         int ch;
89         int initial_isdst = -1, final_isdst;
90         int need_restore = False, sleep_mode = False, looping,
91             init = Unknown;
92         sigset_t mask, emask;
93
94         while ((ch = getopt(argc, argv, "ais")) != -1)
95                 switch((char)ch) {
96                 case 'i':               /* initial call, save offset */
97                         if (init != Unknown)
98                                 usage();
99                         init = True;
100                         break;
101                 case 'a':               /* adjustment call, use saved offset */
102                         if (init != Unknown)
103                                 usage();
104                         init = False;
105                         break;
106                 case 's':
107                         sleep_mode = True;
108                         break;
109                 default:
110                         usage();
111                 }
112         if (init == Unknown)
113                 usage();
114
115         if (access(_PATH_CLOCK, F_OK) != 0)
116                 return 0;
117
118         if (init)
119                 sleep_mode = True;
120
121         sigemptyset(&mask);
122         sigemptyset(&emask);
123         sigaddset(&mask, SIGTERM);
124
125         openlog("adjkerntz", LOG_PID|LOG_PERROR, LOG_DAEMON);
126
127         (void) signal(SIGHUP, SIG_IGN);
128
129         if (init && daemon(0, 1)) {
130                 syslog(LOG_ERR, "daemon: %m");
131                 return 1;
132         }
133
134 again:
135         (void) sigprocmask(SIG_BLOCK, &mask, NULL);
136         (void) signal(SIGTERM, fake);
137
138         diff = 0;
139         stv = NULL;
140         stz = NULL;
141         looping = False;
142
143         wall_clock = (access(_PATH_CLOCK, F_OK) == 0);
144         if (init && !sleep_mode) {
145                 init = False;
146                 if (!wall_clock)
147                         return 0;
148         }
149
150         tzset();
151
152         len = sizeof(kern_offset);
153         if (sysctlbyname("machdep.adjkerntz", &kern_offset, &len, NULL, 0) == -1) {
154                 syslog(LOG_ERR, "sysctl(\"machdep.adjkerntz\"): %m");
155                 return 1;
156         }
157
158 /****** Critical section, do all things as fast as possible ******/
159
160         /* get local CMOS clock and possible kernel offset */
161         if (gettimeofday(&tv, &tz)) {
162                 syslog(LOG_ERR, "gettimeofday: %m");
163                 return 1;
164         }
165
166         /* get the actual local timezone difference */
167         initial_sec = tv.tv_sec;
168
169 recalculate:
170         local = *localtime(&initial_sec);
171         if (diff == 0)
172                 initial_isdst = local.tm_isdst;
173         local.tm_isdst = initial_isdst;
174
175         /* calculate local CMOS diff from GMT */
176
177         localsec = mktime(&local);
178         if (localsec == -1) {
179                 /*
180                  * XXX user can only control local time, and it is
181                  * unacceptable to fail here for init.  2:30 am in the
182                  * middle of the nonexistent hour means 3:30 am.
183                  */
184                 if (!sleep_mode) {
185                         syslog(LOG_WARNING,
186                         "Warning: nonexistent local time, try to run later.");
187                         syslog(LOG_WARNING, "Giving up.");
188                         return 1;
189                 }
190                 syslog(LOG_WARNING,
191                         "Warning: nonexistent local time.");
192                 syslog(LOG_WARNING, "Will retry after %d minutes.",
193                         REPORT_PERIOD / 60);
194                 (void) signal(SIGTERM, SIG_DFL);
195                 (void) sigprocmask(SIG_UNBLOCK, &mask, NULL);
196                 (void) sleep(REPORT_PERIOD);
197                 goto again;
198         }
199         offset = -local.tm_gmtoff;
200 #ifdef DEBUG
201         fprintf(stderr, "Initial offset: %ld secs\n", offset);
202 #endif
203
204         /* correct the kerneltime for this diffs */
205         /* subtract kernel offset, if present, old offset too */
206
207         diff = offset - tz.tz_minuteswest * 60 - kern_offset;
208
209         if (diff != 0) {
210 #ifdef DEBUG
211                 fprintf(stderr, "Initial diff: %ld secs\n", diff);
212 #endif
213                 /* Yet one step for final time */
214
215                 final_sec = initial_sec + diff;
216
217                 /* get the actual local timezone difference */
218                 local = *localtime(&final_sec);
219                 final_isdst = diff < 0 ? initial_isdst : local.tm_isdst;
220                 if (diff > 0 && initial_isdst != final_isdst) {
221                         if (looping)
222                                 goto bad_final;
223                         looping = True;
224                         initial_isdst = final_isdst;
225                         goto recalculate;
226                 }
227                 local.tm_isdst =  final_isdst;
228
229                 localsec = mktime(&local);
230                 if (localsec == -1) {
231                 bad_final:
232                         /*
233                          * XXX as above.  The user has even less control,
234                          * but perhaps we never get here.
235                          */
236                         if (!sleep_mode) {
237                                 syslog(LOG_WARNING,
238                                         "Warning: nonexistent final local time, try to run later.");
239                                 syslog(LOG_WARNING, "Giving up.");
240                                 return 1;
241                         }
242                         syslog(LOG_WARNING,
243                                 "Warning: nonexistent final local time.");
244                         syslog(LOG_WARNING, "Will retry after %d minutes.",
245                                 REPORT_PERIOD / 60);
246                         (void) signal(SIGTERM, SIG_DFL);
247                         (void) sigprocmask(SIG_UNBLOCK, &mask, NULL);
248                         (void) sleep(REPORT_PERIOD);
249                         goto again;
250                 }
251                 offset = -local.tm_gmtoff;
252 #ifdef DEBUG
253                 fprintf(stderr, "Final offset: %ld secs\n", offset);
254 #endif
255
256                 /* correct the kerneltime for this diffs */
257                 /* subtract kernel offset, if present, old offset too */
258
259                 diff = offset - tz.tz_minuteswest * 60 - kern_offset;
260
261                 if (diff != 0) {
262 #ifdef DEBUG
263                         fprintf(stderr, "Final diff: %ld secs\n", diff);
264 #endif
265                         /*
266                          * stv is abused as a flag.  The important value
267                          * is in `diff'.
268                          */
269                         stv = &tv;
270                 }
271         }
272
273         if (tz.tz_dsttime != 0 || tz.tz_minuteswest != 0) {
274                 tz.tz_dsttime = tz.tz_minuteswest = 0;  /* zone info is garbage */
275                 stz = &tz;
276         }
277         if (!wall_clock && stz == NULL)
278                 stv = NULL;
279
280         /* if init or UTC clock and offset/date will be changed, */
281         /* disable RTC modification for a while.                      */
282
283         if (   (init && stv != NULL)
284             || ((init || !wall_clock) && kern_offset != offset)
285            ) {
286                 len = sizeof(disrtcset);
287                 if (sysctlbyname("machdep.disable_rtc_set", &disrtcset, &len, NULL, 0) == -1) {
288                         syslog(LOG_ERR, "sysctl(get: \"machdep.disable_rtc_set\"): %m");
289                         return 1;
290                 }
291                 if (disrtcset == 0) {
292                         disrtcset = 1;
293                         need_restore = True;
294                         if (sysctlbyname("machdep.disable_rtc_set", NULL, NULL, &disrtcset, len) == -1) {
295                                 syslog(LOG_ERR, "sysctl(set: \"machdep.disable_rtc_set\"): %m");
296                                 return 1;
297                         }
298                 }
299         }
300
301         if (   (init && (stv != NULL || stz != NULL))
302             || (stz != NULL && stv == NULL)
303            ) {
304                 if (stv != NULL) {
305                         /*
306                          * Get the time again, as close as possible to
307                          * adjusting it, to minimise drift.
308                          * XXX we'd better not fail between here and
309                          * restoring disrtcset, since we don't clean up
310                          * anything.
311                          */
312                         if (gettimeofday(&tv, (struct timezone *)NULL)) {
313                                 syslog(LOG_ERR, "gettimeofday: %m");
314                                 return 1;
315                         }
316                         tv.tv_sec += diff;
317                         stv = &tv;
318                 }
319                 if (settimeofday(stv, stz)) {
320                         syslog(LOG_ERR, "settimeofday: %m");
321                         return 1;
322                 }
323         }
324
325         /* setting machdep.adjkerntz have a side effect: resettodr(), which */
326         /* can be disabled by machdep.disable_rtc_set, so if init or UTC clock    */
327         /* -- don't write RTC, else write RTC.                          */
328
329         if (kern_offset != offset) {
330                 kern_offset = offset;
331                 len = sizeof(kern_offset);
332                 if (sysctlbyname("machdep.adjkerntz", NULL, NULL, &kern_offset, len) == -1) {
333                         syslog(LOG_ERR, "sysctl(set: \"machdep.adjkerntz\"): %m");
334                         return 1;
335                 }
336         }
337
338         len = sizeof(wall_clock);
339         if (sysctlbyname("machdep.wall_cmos_clock",  NULL, NULL, &wall_clock, len) == -1) {
340                 syslog(LOG_ERR, "sysctl(set: \"machdep.wall_cmos_clock\"): %m");
341                 return 1;
342         }
343
344         if (need_restore) {
345                 need_restore = False;
346                 disrtcset = 0;
347                 len = sizeof(disrtcset);
348                 if (sysctlbyname("machdep.disable_rtc_set", NULL, NULL, &disrtcset, len) == -1) {
349                         syslog(LOG_ERR, "sysctl(set: \"machdep.disable_rtc_set\"): %m");
350                         return 1;
351                 }
352         }
353
354 /****** End of critical section ******/
355
356         if (init && wall_clock) {
357                 sleep_mode = False;
358                 /* wait for signals and acts like -a */
359                 (void) sigsuspend(&emask);
360                 goto again;
361         }
362
363         return 0;
364 }
365
366 static void
367 usage(void)
368 {
369         fprintf(stderr, "%s\n%s\n%s\n%s\n",
370                 "usage: adjkerntz -i",
371                 "\t\t(initial call from /etc/rc)",
372                 "       adjkerntz -a [-s]",
373                 "\t\t(adjustment call, -s for sleep/retry mode)");
374         exit(2);
375 }