hwclock: fix musl breakage of settimeofday(tz)
No binary (build failed)
—
(log yok)
| @@ -36,6 +36,19 @@ | |
| #include <sys/utsname.h> | |
| #include "rtc_.h" | |
| + | |
| + | //musl has no __MUSL__ or similar define to check for, |
| + | //but its <sys/types.h> has these lines: |
| + | // #define __NEED_fsblkcnt_t |
| + | // #define __NEED_fsfilcnt_t |
| + | #if defined(__linux__) && defined(__NEED_fsblkcnt_t) && defined(__NEED_fsfilcnt_t) |
| + | # define LIBC_IS_MUSL 1 |
| + | # include <sys/syscall.h> |
| + | #else |
| + | # define LIBC_IS_MUSL 0 |
| + | #endif |
| + | |
| + | |
| /* diff code is disabled: it's not sys/hw clock diff, it's some useless | |
| * "time between hwclock was started and we saw CMOS tick" quantity. | |
| * 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) | |
| #endif | |
| } | |
| - | static void to_sys_clock(const char **pp_rtcname, int utc) |
| + | static void set_kernel_tz(const struct timezone *tz) |
| { | |
| - | struct timeval tv; |
| - | struct timezone tz; |
| - | |
| - | tz.tz_minuteswest = timezone / 60; |
| - | /* ^^^ used to also subtract 60*daylight, but it's wrong: |
| - | * daylight!=0 means "this timezone has some DST |
| - | * during the year", not "DST is in effect now". |
| + | #if LIBC_IS_MUSL |
| + | /* musl libc does not pass tz argument to syscall |
| + | * because "it's deprecated by POSIX, therefore it's fine |
| + | * if we gratuitously break stuff" :( |
| */ | |
| - | tz.tz_dsttime = 0; |
| - | |
| - | /* glibc v2.31+ returns an error if both args are non-NULL */ |
| - | if (settimeofday(NULL, &tz)) |
| + | #if !defined(SYS_settimeofday) && defined(SYS_settimeofday_time32) |
| + | # define SYS_settimeofday SYS_settimeofday_time32 |
| + | #endif |
| + | int ret = syscall(SYS_settimeofday, NULL, tz); |
| + | #else |
| + | int ret = settimeofday(NULL, tz); |
| + | #endif |
| + | if (ret) |
| bb_simple_perror_msg_and_die("settimeofday"); | |
| + | } |
| + | |
| + | /* |
| + | * At system boot, kernel may set system time from RTC, |
| + | * but it knows nothing about timezones. If RTC is in local time, |
| + | * then system time is wrong - it is offset by timezone. |
| + | * --systz option corrects system time if RTC is in local time, |
| + | * and (always) sets in-kernel timezone. |
| + | * |
| + | * This is an alternate option to --hctosys that does not read the |
| + | * hardware clock. |
| + | * |
| + | * util-linux's code has this comment: |
| + | * RTC | settimeofday calls |
| + | * ------|------------------------------------------------- |
| + | * Local | 1) warps system time*, sets PCIL* and kernel tz |
| + | * UTC | 1st) locks warp_clock 2nd) sets kernel tz |
| + | * * only on first call after boot |
| + | * (PCIL is "persistent_clock_is_local" kernel internal flag, |
| + | * it makes kernel save RTC in local time, not UTC.) |
| + | */ |
| + | static void set_kernel_timezone_and_clock(int utc, const struct timeval *hctosys) |
| + | { |
| + | time_t cur; |
| + | struct tm *broken; |
| + | struct timezone tz = { 0 }; |
| + | |
| + | /* if --utc, prevent kernel's warp_clock() with a dummy call */ |
| + | if (utc) |
| + | set_kernel_tz(&tz); |
| + | |
| + | /* Set kernel's timezone offset based on userspace one */ |
| + | cur = time(NULL); |
| + | broken = localtime(&cur); |
| + | tz.tz_minuteswest = -broken->tm_gmtoff / 60; |
| + | /*tz.tz_dsttime = 0; already is */ |
| + | set_kernel_tz(&tz); /* MIGHT warp_clock() if 1st call since boot */ |
| + | |
| + | if (hctosys) { /* it's --hctosys: set time too */ |
| + | if (settimeofday(hctosys, NULL)) |
| + | bb_simple_perror_msg_and_die("settimeofday"); |
| + | } |
| + | } |
| + | |
| + | static void to_sys_clock(const char **pp_rtcname, int utc) |
| + | { |
| + | struct timeval tv; |
| tv.tv_sec = read_rtc(pp_rtcname, NULL, utc); | |
| tv.tv_usec = 0; | |
| - | if (settimeofday(&tv, NULL)) |
| - | bb_simple_perror_msg_and_die("settimeofday"); |
| + | return set_kernel_timezone_and_clock(utc, &tv); |
| } | |
| 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) | |
| close(rtc); | |
| } | |
| - | /* |
| - | * At system boot, kernel may set system time from RTC, |
| - | * but it knows nothing about timezones. If RTC is in local time, |
| - | * then system time is wrong - it is offset by timezone. |
| - | * This option corrects system time if RTC is in local time, |
| - | * and (always) sets in-kernel timezone. |
| - | * |
| - | * This is an alternate option to --hctosys that does not read the |
| - | * hardware clock. |
| - | */ |
| - | static void set_system_clock_timezone(int utc) |
| - | { |
| - | struct timeval tv; |
| - | struct tm *broken; |
| - | struct timezone tz; |
| - | |
| - | gettimeofday(&tv, NULL); |
| - | broken = localtime(&tv.tv_sec); |
| - | tz.tz_minuteswest = timezone / 60; |
| - | if (broken->tm_isdst > 0) |
| - | tz.tz_minuteswest -= 60; |
| - | tz.tz_dsttime = 0; |
| - | gettimeofday(&tv, NULL); |
| - | if (!utc) |
| - | tv.tv_sec += tz.tz_minuteswest * 60; |
| - | |
| - | /* glibc v2.31+ returns an error if both args are non-NULL */ |
| - | if (settimeofday(NULL, &tz)) |
| - | bb_simple_perror_msg_and_die("settimeofday"); |
| - | if (settimeofday(&tv, NULL)) |
| - | bb_simple_perror_msg_and_die("settimeofday"); |
| - | } |
| - | |
| //usage:#define hwclock_trivial_usage | |
| //usage: IF_LONG_OPTS( | |
| //usage: "[-swu] [--systz] [--localtime] [-f FILE]" | |
| @@ -333,7 +360,6 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv) | |
| const char *rtcname = NULL; | |
| unsigned opt; | |
| int utc; | |
| - | |
| #if ENABLE_LONG_OPTS | |
| static const char hwclock_longopts[] ALIGN1 = | |
| "localtime\0" No_argument "l" /* short opt is non-standard */ | |
| @@ -345,10 +371,6 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv) | |
| "rtc\0" Required_argument "f" | |
| ; | |
| #endif | |
| - | |
| - | /* Initialize "timezone" (libc global variable) */ |
| - | tzset(); |
| - | |
| opt = getopt32long(argv, | |
| "^lurswtf:" "\0" "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l", | |
| hwclock_longopts, | |
| @@ -366,7 +388,7 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv) | |
| else if (opt & HWCLOCK_OPT_SYSTOHC) | |
| from_sys_clock(&rtcname, utc); | |
| else if (opt & HWCLOCK_OPT_SYSTZ) | |
| - | set_system_clock_timezone(utc); |
| + | set_kernel_timezone_and_clock(utc, NULL); |
| else | |
| /* default HWCLOCK_OPT_SHOW */ | |
| show_clock(&rtcname, utc); | |