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