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