aboutsummaryrefslogtreecommitdiff
path: root/util-linux/hwclock.c
diff options
context:
space:
mode:
Diffstat (limited to 'util-linux/hwclock.c')
-rw-r--r--util-linux/hwclock.c193
1 files changed, 119 insertions, 74 deletions
diff --git a/util-linux/hwclock.c b/util-linux/hwclock.c
index e85bca2b2..723b09589 100644
--- a/util-linux/hwclock.c
+++ b/util-linux/hwclock.c
@@ -36,6 +36,19 @@
36#include <sys/utsname.h> 36#include <sys/utsname.h>
37#include "rtc_.h" 37#include "rtc_.h"
38 38
39
40//musl has no __MUSL__ or similar define to check for,
41//but its <sys/types.h> has these lines:
42// #define __NEED_fsblkcnt_t
43// #define __NEED_fsfilcnt_t
44#if defined(__linux__) && defined(__NEED_fsblkcnt_t) && defined(__NEED_fsfilcnt_t)
45# define LIBC_IS_MUSL 1
46# include <sys/syscall.h>
47#else
48# define LIBC_IS_MUSL 0
49#endif
50
51
39/* diff code is disabled: it's not sys/hw clock diff, it's some useless 52/* diff code is disabled: it's not sys/hw clock diff, it's some useless
40 * "time between hwclock was started and we saw CMOS tick" quantity. 53 * "time between hwclock was started and we saw CMOS tick" quantity.
41 * It's useless since hwclock is started at a random moment, 54 * It's useless since hwclock is started at a random moment,
@@ -66,7 +79,7 @@ static time_t read_rtc(const char **pp_rtcname, struct timeval *sys_tv, int utc)
66 int before = tm_time.tm_sec; 79 int before = tm_time.tm_sec;
67 while (1) { 80 while (1) {
68 rtc_read_tm(&tm_time, fd); 81 rtc_read_tm(&tm_time, fd);
69 gettimeofday(sys_tv, NULL); 82 xgettimeofday(sys_tv);
70 if (before != (int)tm_time.tm_sec) 83 if (before != (int)tm_time.tm_sec)
71 break; 84 break;
72 } 85 }
@@ -116,26 +129,72 @@ static void show_clock(const char **pp_rtcname, int utc)
116#endif 129#endif
117} 130}
118 131
119static void to_sys_clock(const char **pp_rtcname, int utc) 132static void set_kernel_tz(const struct timezone *tz)
120{ 133{
121 struct timeval tv; 134#if LIBC_IS_MUSL
122 struct timezone tz; 135 /* musl libc does not pass tz argument to syscall
123 136 * because "it's deprecated by POSIX, therefore it's fine
124 tz.tz_minuteswest = timezone / 60; 137 * if we gratuitously break stuff" :(
125 /* ^^^ used to also subtract 60*daylight, but it's wrong:
126 * daylight!=0 means "this timezone has some DST
127 * during the year", not "DST is in effect now".
128 */ 138 */
129 tz.tz_dsttime = 0; 139#if !defined(SYS_settimeofday) && defined(SYS_settimeofday_time32)
130 140# define SYS_settimeofday SYS_settimeofday_time32
131 /* glibc v2.31+ returns an error if both args are non-NULL */ 141#endif
132 if (settimeofday(NULL, &tz)) 142 int ret = syscall(SYS_settimeofday, NULL, tz);
143#else
144 int ret = settimeofday(NULL, tz);
145#endif
146 if (ret)
133 bb_simple_perror_msg_and_die("settimeofday"); 147 bb_simple_perror_msg_and_die("settimeofday");
148}
149
150/*
151 * At system boot, kernel may set system time from RTC,
152 * but it knows nothing about timezones. If RTC is in local time,
153 * then system time is wrong - it is offset by timezone.
154 * --systz option corrects system time if RTC is in local time,
155 * and (always) sets in-kernel timezone.
156 * (Unlike --hctosys, it does not read the RTC).
157 *
158 * util-linux's code has this comment:
159 * RTC | settimeofday calls
160 * ------|-------------------------------------------------
161 * Local | 1st) warps system time*, sets PCIL* and kernel tz
162 * UTC | 1st) locks warp_clock 2nd) sets kernel tz
163 * * only on first call after boot
164 * (PCIL is "persistent_clock_is_local" kernel internal flag,
165 * it makes kernel save RTC in local time, not UTC.)
166 */
167static void set_kernel_timezone_and_clock(int utc, const struct timeval *hctosys)
168{
169 time_t cur;
170 struct tm *broken;
171 struct timezone tz = { 0 };
172
173 /* if --utc, prevent kernel's warp_clock() with a dummy call */
174 if (utc)
175 set_kernel_tz(&tz);
176
177 /* Set kernel's timezone offset based on userspace one */
178//It's tempting to call tzset() and use libc global "timezone" variable
179//...but it does NOT include DST shift (IOW: it's WRONG, usually by one hour,
180//if DST is in effect!) Thus this ridiculous dance:
181 cur = time(NULL);
182 broken = localtime(&cur);
183 tz.tz_minuteswest = -broken->tm_gmtoff / 60;
184 /*tz.tz_dsttime = 0; already is */
185 set_kernel_tz(&tz); /* MIGHT warp_clock() if 1st call since boot */
186
187 if (hctosys) /* it's --hctosys: set time too */
188 xsettimeofday(hctosys);
189}
190
191static void to_sys_clock(const char **pp_rtcname, int utc)
192{
193 struct timeval tv;
134 194
135 tv.tv_sec = read_rtc(pp_rtcname, NULL, utc); 195 tv.tv_sec = read_rtc(pp_rtcname, NULL, utc);
136 tv.tv_usec = 0; 196 tv.tv_usec = 0;
137 if (settimeofday(&tv, NULL)) 197 return set_kernel_timezone_and_clock(utc, &tv);
138 bb_simple_perror_msg_and_die("settimeofday");
139} 198}
140 199
141static void from_sys_clock(const char **pp_rtcname, int utc) 200static void from_sys_clock(const char **pp_rtcname, int utc)
@@ -146,7 +205,7 @@ static void from_sys_clock(const char **pp_rtcname, int utc)
146 int rtc; 205 int rtc;
147 206
148 rtc = rtc_xopen(pp_rtcname, O_WRONLY); 207 rtc = rtc_xopen(pp_rtcname, O_WRONLY);
149 gettimeofday(&tv, NULL); 208 xgettimeofday(&tv);
150 /* Prepare tm_time */ 209 /* Prepare tm_time */
151 if (sizeof(time_t) == sizeof(tv.tv_sec)) { 210 if (sizeof(time_t) == sizeof(tv.tv_sec)) {
152 if (utc) 211 if (utc)
@@ -194,7 +253,7 @@ static void from_sys_clock(const char **pp_rtcname, int utc)
194 unsigned rem_usec; 253 unsigned rem_usec;
195 time_t t; 254 time_t t;
196 255
197 gettimeofday(&tv, NULL); 256 xgettimeofday(&tv);
198 257
199 t = tv.tv_sec; 258 t = tv.tv_sec;
200 rem_usec = 1000000 - tv.tv_usec; 259 rem_usec = 1000000 - tv.tv_usec;
@@ -215,7 +274,7 @@ static void from_sys_clock(const char **pp_rtcname, int utc)
215 } 274 }
216 275
217 /* gmtime/localtime took some time, re-get cur time */ 276 /* gmtime/localtime took some time, re-get cur time */
218 gettimeofday(&tv, NULL); 277 xgettimeofday(&tv);
219 278
220 if (tv.tv_sec < t /* we are still in old second */ 279 if (tv.tv_sec < t /* we are still in old second */
221 || (tv.tv_sec == t && tv.tv_usec < adj) /* not too far into next second */ 280 || (tv.tv_sec == t && tv.tv_usec < adj) /* not too far into next second */
@@ -261,64 +320,53 @@ static void from_sys_clock(const char **pp_rtcname, int utc)
261 close(rtc); 320 close(rtc);
262} 321}
263 322
264/* 323// hwclock from util-linux 2.36.1
265 * At system boot, kernel may set system time from RTC, 324// hwclock [function] [option...]
266 * but it knows nothing about timezones. If RTC is in local time, 325//Functions:
267 * then system time is wrong - it is offset by timezone. 326// -r, --show display the RTC time
268 * This option corrects system time if RTC is in local time, 327// --get display drift corrected RTC time
269 * and (always) sets in-kernel timezone. 328// --set set the RTC according to --date
270 * 329// -s, --hctosys set the system time from the RTC
271 * This is an alternate option to --hctosys that does not read the 330// -w, --systohc set the RTC from the system time
272 * hardware clock. 331// --systz send timescale configurations to the kernel
273 */ 332// -a, --adjust adjust the RTC to account for systematic drift
274static void set_system_clock_timezone(int utc) 333// --predict predict the drifted RTC time according to --date
275{ 334//Options:
276 struct timeval tv; 335// -u, --utc the RTC timescale is UTC
277 struct tm *broken; 336// -l, --localtime the RTC timescale is Local
278 struct timezone tz; 337// -f, --rtc <file> use an alternate file to /dev/rtc0
279 338// --directisa use the ISA bus instead of /dev/rtc0 access
280 gettimeofday(&tv, NULL); 339// --date <time> date/time input for --set and --predict
281 broken = localtime(&tv.tv_sec); 340// --delay <sec> delay used when set new RTC time
282 tz.tz_minuteswest = timezone / 60; 341// --update-drift update the RTC drift factor
283 if (broken->tm_isdst > 0) 342// --noadjfile do not use /etc/adjtime
284 tz.tz_minuteswest -= 60; 343// --adjfile <file> use an alternate file to /etc/adjtime
285 tz.tz_dsttime = 0; 344// --test dry run; implies --verbose
286 gettimeofday(&tv, NULL); 345// -v, --verbose display more details
287 if (!utc)
288 tv.tv_sec += tz.tz_minuteswest * 60;
289
290 /* glibc v2.31+ returns an error if both args are non-NULL */
291 if (settimeofday(NULL, &tz))
292 bb_simple_perror_msg_and_die("settimeofday");
293 if (settimeofday(&tv, NULL))
294 bb_simple_perror_msg_and_die("settimeofday");
295}
296 346
297//usage:#define hwclock_trivial_usage 347//usage:#define hwclock_trivial_usage
298//usage: IF_LONG_OPTS( 348//usage: IF_LONG_OPTS(
299//usage: "[-r|--show] [-s|--hctosys] [-w|--systohc] [--systz]" 349//usage: "[-swul] [--systz] [-f DEV]"
300//usage: " [--localtime] [-u|--utc]"
301//usage: " [-f|--rtc FILE]"
302//usage: ) 350//usage: )
303//usage: IF_NOT_LONG_OPTS( 351//usage: IF_NOT_LONG_OPTS(
304//usage: "[-r] [-s] [-w] [-t] [-l] [-u] [-f FILE]" 352//usage: "[-swult] [-f DEV]"
305//usage: ) 353//usage: )
306//usage:#define hwclock_full_usage "\n\n" 354//usage:#define hwclock_full_usage "\n\n"
307//usage: "Query and set hardware clock (RTC)\n" 355//usage: "Show or set hardware clock (RTC)\n"
308//usage: "\n -r Show hardware clock time" 356///////: "\n -r Show RTC time"
309//usage: "\n -s Set system time from hardware clock" 357///////-r is default, don't bother showing it in help
310//usage: "\n -w Set hardware clock from system time" 358//usage: "\n -s Set system time from RTC"
359//usage: "\n -w Set RTC from system time"
311//usage: IF_LONG_OPTS( 360//usage: IF_LONG_OPTS(
312//usage: "\n --systz Set in-kernel timezone, correct system time" 361//usage: "\n --systz Set in-kernel timezone, correct system time"
362//usage: "\n if RTC is kept in local time"
313//usage: ) 363//usage: )
314//usage: "\n if hardware clock is in local time" 364//usage: "\n -f DEV Use specified device (e.g. /dev/rtc2)"
315//usage: "\n -u Assume hardware clock is kept in UTC" 365//usage: "\n -u Assume RTC is kept in UTC"
316//usage: IF_LONG_OPTS( 366//usage: "\n -l Assume RTC is kept in local time"
317//usage: "\n --localtime Assume hardware clock is kept in local time" 367//usage: "\n (if neither is given, read from "ADJTIME_PATH")"
318//usage: )
319//usage: "\n -f FILE Use specified device (e.g. /dev/rtc2)"
320 368
321//TODO: get rid of incompatible -t and -l aliases to --systz and --localtime 369//TODO: get rid of incompatible -t alias to --systz?
322 370
323#define HWCLOCK_OPT_LOCALTIME 0x01 371#define HWCLOCK_OPT_LOCALTIME 0x01
324#define HWCLOCK_OPT_UTC 0x02 372#define HWCLOCK_OPT_UTC 0x02
@@ -334,10 +382,9 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv)
334 const char *rtcname = NULL; 382 const char *rtcname = NULL;
335 unsigned opt; 383 unsigned opt;
336 int utc; 384 int utc;
337
338#if ENABLE_LONG_OPTS 385#if ENABLE_LONG_OPTS
339 static const char hwclock_longopts[] ALIGN1 = 386 static const char hwclock_longopts[] ALIGN1 =
340 "localtime\0" No_argument "l" /* short opt is non-standard */ 387 "localtime\0" No_argument "l"
341 "utc\0" No_argument "u" 388 "utc\0" No_argument "u"
342 "show\0" No_argument "r" 389 "show\0" No_argument "r"
343 "hctosys\0" No_argument "s" 390 "hctosys\0" No_argument "s"
@@ -346,17 +393,15 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv)
346 "rtc\0" Required_argument "f" 393 "rtc\0" Required_argument "f"
347 ; 394 ;
348#endif 395#endif
349
350 /* Initialize "timezone" (libc global variable) */
351 tzset();
352
353 opt = getopt32long(argv, 396 opt = getopt32long(argv,
354 "^lurswtf:" "\0" "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l", 397 "^""lurswtf:v" /* -v is accepted and ignored */
398 "\0"
399 "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l",
355 hwclock_longopts, 400 hwclock_longopts,
356 &rtcname 401 &rtcname
357 ); 402 );
358 403
359 /* If -u or -l wasn't given check if we are using utc */ 404 /* If -u or -l wasn't given, check if we are using utc */
360 if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME)) 405 if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
361 utc = (opt & HWCLOCK_OPT_UTC); 406 utc = (opt & HWCLOCK_OPT_UTC);
362 else 407 else
@@ -367,7 +412,7 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv)
367 else if (opt & HWCLOCK_OPT_SYSTOHC) 412 else if (opt & HWCLOCK_OPT_SYSTOHC)
368 from_sys_clock(&rtcname, utc); 413 from_sys_clock(&rtcname, utc);
369 else if (opt & HWCLOCK_OPT_SYSTZ) 414 else if (opt & HWCLOCK_OPT_SYSTZ)
370 set_system_clock_timezone(utc); 415 set_kernel_timezone_and_clock(utc, NULL);
371 else 416 else
372 /* default HWCLOCK_OPT_SHOW */ 417 /* default HWCLOCK_OPT_SHOW */
373 show_clock(&rtcname, utc); 418 show_clock(&rtcname, utc);