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); |
