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