diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2020-12-16 13:49:10 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2020-12-16 14:00:47 +0100 |
commit | 9e262f13c2e53490d69d3112ffd718c27de04d1f (patch) | |
tree | 0022d9f686beb5e4d7bfa7aa2a30513443ca7856 | |
parent | a97a795dcb41943943b8ecfe039e23673365af55 (diff) | |
download | busybox-w32-9e262f13c2e53490d69d3112ffd718c27de04d1f.tar.gz busybox-w32-9e262f13c2e53490d69d3112ffd718c27de04d1f.tar.bz2 busybox-w32-9e262f13c2e53490d69d3112ffd718c27de04d1f.zip |
hwclock: fix musl breakage of settimeofday(tz)
function old new delta
set_kernel_timezone_and_clock - 119 +119
set_kernel_tz - 28 +28
hwclock_main 480 301 -179
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 0/1 up/down: 147/-179) Total: -32 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | util-linux/hwclock.c | 128 |
1 files changed, 75 insertions, 53 deletions
diff --git a/util-linux/hwclock.c b/util-linux/hwclock.c index 791525fc2..44cb4794e 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, |
@@ -116,26 +129,73 @@ static void show_clock(const char **pp_rtcname, int utc) | |||
116 | #endif | 129 | #endif |
117 | } | 130 | } |
118 | 131 | ||
119 | static void to_sys_clock(const char **pp_rtcname, int utc) | 132 | static 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 | * | ||
157 | * This is an alternate option to --hctosys that does not read the | ||
158 | * hardware clock. | ||
159 | * | ||
160 | * util-linux's code has this comment: | ||
161 | * RTC | settimeofday calls | ||
162 | * ------|------------------------------------------------- | ||
163 | * Local | 1) warps system time*, sets PCIL* and kernel tz | ||
164 | * UTC | 1st) locks warp_clock 2nd) sets kernel tz | ||
165 | * * only on first call after boot | ||
166 | * (PCIL is "persistent_clock_is_local" kernel internal flag, | ||
167 | * it makes kernel save RTC in local time, not UTC.) | ||
168 | */ | ||
169 | static void set_kernel_timezone_and_clock(int utc, const struct timeval *hctosys) | ||
170 | { | ||
171 | time_t cur; | ||
172 | struct tm *broken; | ||
173 | struct timezone tz = { 0 }; | ||
174 | |||
175 | /* if --utc, prevent kernel's warp_clock() with a dummy call */ | ||
176 | if (utc) | ||
177 | set_kernel_tz(&tz); | ||
178 | |||
179 | /* Set kernel's timezone offset based on userspace one */ | ||
180 | cur = time(NULL); | ||
181 | broken = localtime(&cur); | ||
182 | tz.tz_minuteswest = -broken->tm_gmtoff / 60; | ||
183 | /*tz.tz_dsttime = 0; already is */ | ||
184 | set_kernel_tz(&tz); /* MIGHT warp_clock() if 1st call since boot */ | ||
185 | |||
186 | if (hctosys) { /* it's --hctosys: set time too */ | ||
187 | if (settimeofday(hctosys, NULL)) | ||
188 | bb_simple_perror_msg_and_die("settimeofday"); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | static void to_sys_clock(const char **pp_rtcname, int utc) | ||
193 | { | ||
194 | struct timeval tv; | ||
134 | 195 | ||
135 | tv.tv_sec = read_rtc(pp_rtcname, NULL, utc); | 196 | tv.tv_sec = read_rtc(pp_rtcname, NULL, utc); |
136 | tv.tv_usec = 0; | 197 | tv.tv_usec = 0; |
137 | if (settimeofday(&tv, NULL)) | 198 | return set_kernel_timezone_and_clock(utc, &tv); |
138 | bb_simple_perror_msg_and_die("settimeofday"); | ||
139 | } | 199 | } |
140 | 200 | ||
141 | static void from_sys_clock(const char **pp_rtcname, int utc) | 201 | static void from_sys_clock(const char **pp_rtcname, int utc) |
@@ -261,39 +321,6 @@ static void from_sys_clock(const char **pp_rtcname, int utc) | |||
261 | close(rtc); | 321 | close(rtc); |
262 | } | 322 | } |
263 | 323 | ||
264 | /* | ||
265 | * At system boot, kernel may set system time from RTC, | ||
266 | * but it knows nothing about timezones. If RTC is in local time, | ||
267 | * then system time is wrong - it is offset by timezone. | ||
268 | * This option corrects system time if RTC is in local time, | ||
269 | * and (always) sets in-kernel timezone. | ||
270 | * | ||
271 | * This is an alternate option to --hctosys that does not read the | ||
272 | * hardware clock. | ||
273 | */ | ||
274 | static void set_system_clock_timezone(int utc) | ||
275 | { | ||
276 | struct timeval tv; | ||
277 | struct tm *broken; | ||
278 | struct timezone tz; | ||
279 | |||
280 | gettimeofday(&tv, NULL); | ||
281 | broken = localtime(&tv.tv_sec); | ||
282 | tz.tz_minuteswest = timezone / 60; | ||
283 | if (broken->tm_isdst > 0) | ||
284 | tz.tz_minuteswest -= 60; | ||
285 | tz.tz_dsttime = 0; | ||
286 | gettimeofday(&tv, NULL); | ||
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 | |||
297 | //usage:#define hwclock_trivial_usage | 324 | //usage:#define hwclock_trivial_usage |
298 | //usage: IF_LONG_OPTS( | 325 | //usage: IF_LONG_OPTS( |
299 | //usage: "[-swu] [--systz] [--localtime] [-f FILE]" | 326 | //usage: "[-swu] [--systz] [--localtime] [-f FILE]" |
@@ -333,7 +360,6 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv) | |||
333 | const char *rtcname = NULL; | 360 | const char *rtcname = NULL; |
334 | unsigned opt; | 361 | unsigned opt; |
335 | int utc; | 362 | int utc; |
336 | |||
337 | #if ENABLE_LONG_OPTS | 363 | #if ENABLE_LONG_OPTS |
338 | static const char hwclock_longopts[] ALIGN1 = | 364 | static const char hwclock_longopts[] ALIGN1 = |
339 | "localtime\0" No_argument "l" /* short opt is non-standard */ | 365 | "localtime\0" No_argument "l" /* short opt is non-standard */ |
@@ -345,10 +371,6 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv) | |||
345 | "rtc\0" Required_argument "f" | 371 | "rtc\0" Required_argument "f" |
346 | ; | 372 | ; |
347 | #endif | 373 | #endif |
348 | |||
349 | /* Initialize "timezone" (libc global variable) */ | ||
350 | tzset(); | ||
351 | |||
352 | opt = getopt32long(argv, | 374 | opt = getopt32long(argv, |
353 | "^lurswtf:" "\0" "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l", | 375 | "^lurswtf:" "\0" "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l", |
354 | hwclock_longopts, | 376 | hwclock_longopts, |
@@ -366,7 +388,7 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv) | |||
366 | else if (opt & HWCLOCK_OPT_SYSTOHC) | 388 | else if (opt & HWCLOCK_OPT_SYSTOHC) |
367 | from_sys_clock(&rtcname, utc); | 389 | from_sys_clock(&rtcname, utc); |
368 | else if (opt & HWCLOCK_OPT_SYSTZ) | 390 | else if (opt & HWCLOCK_OPT_SYSTZ) |
369 | set_system_clock_timezone(utc); | 391 | set_kernel_timezone_and_clock(utc, NULL); |
370 | else | 392 | else |
371 | /* default HWCLOCK_OPT_SHOW */ | 393 | /* default HWCLOCK_OPT_SHOW */ |
372 | show_clock(&rtcname, utc); | 394 | show_clock(&rtcname, utc); |