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>
This commit is contained in:
parent
a97a795dcb
commit
9e262f13c2
@ -36,6 +36,19 @@
|
|||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
#include "rtc_.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
|
/* 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.
|
* "time between hwclock was started and we saw CMOS tick" quantity.
|
||||||
* It's useless since hwclock is started at a random moment,
|
* 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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_kernel_tz(const struct timezone *tz)
|
||||||
|
{
|
||||||
|
#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" :(
|
||||||
|
*/
|
||||||
|
#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)
|
static void to_sys_clock(const char **pp_rtcname, int utc)
|
||||||
{
|
{
|
||||||
struct timeval tv;
|
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".
|
|
||||||
*/
|
|
||||||
tz.tz_dsttime = 0;
|
|
||||||
|
|
||||||
/* glibc v2.31+ returns an error if both args are non-NULL */
|
|
||||||
if (settimeofday(NULL, &tz))
|
|
||||||
bb_simple_perror_msg_and_die("settimeofday");
|
|
||||||
|
|
||||||
tv.tv_sec = read_rtc(pp_rtcname, NULL, utc);
|
tv.tv_sec = read_rtc(pp_rtcname, NULL, utc);
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
if (settimeofday(&tv, NULL))
|
return set_kernel_timezone_and_clock(utc, &tv);
|
||||||
bb_simple_perror_msg_and_die("settimeofday");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void from_sys_clock(const char **pp_rtcname, int utc)
|
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);
|
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:#define hwclock_trivial_usage
|
||||||
//usage: IF_LONG_OPTS(
|
//usage: IF_LONG_OPTS(
|
||||||
//usage: "[-swu] [--systz] [--localtime] [-f FILE]"
|
//usage: "[-swu] [--systz] [--localtime] [-f FILE]"
|
||||||
@ -333,7 +360,6 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
const char *rtcname = NULL;
|
const char *rtcname = NULL;
|
||||||
unsigned opt;
|
unsigned opt;
|
||||||
int utc;
|
int utc;
|
||||||
|
|
||||||
#if ENABLE_LONG_OPTS
|
#if ENABLE_LONG_OPTS
|
||||||
static const char hwclock_longopts[] ALIGN1 =
|
static const char hwclock_longopts[] ALIGN1 =
|
||||||
"localtime\0" No_argument "l" /* short opt is non-standard */
|
"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"
|
"rtc\0" Required_argument "f"
|
||||||
;
|
;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Initialize "timezone" (libc global variable) */
|
|
||||||
tzset();
|
|
||||||
|
|
||||||
opt = getopt32long(argv,
|
opt = getopt32long(argv,
|
||||||
"^lurswtf:" "\0" "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l",
|
"^lurswtf:" "\0" "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l",
|
||||||
hwclock_longopts,
|
hwclock_longopts,
|
||||||
@ -366,7 +388,7 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
else if (opt & HWCLOCK_OPT_SYSTOHC)
|
else if (opt & HWCLOCK_OPT_SYSTOHC)
|
||||||
from_sys_clock(&rtcname, utc);
|
from_sys_clock(&rtcname, utc);
|
||||||
else if (opt & HWCLOCK_OPT_SYSTZ)
|
else if (opt & HWCLOCK_OPT_SYSTZ)
|
||||||
set_system_clock_timezone(utc);
|
set_kernel_timezone_and_clock(utc, NULL);
|
||||||
else
|
else
|
||||||
/* default HWCLOCK_OPT_SHOW */
|
/* default HWCLOCK_OPT_SHOW */
|
||||||
show_clock(&rtcname, utc);
|
show_clock(&rtcname, utc);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user