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:
		@@ -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 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)
 | 
			
		||||
{
 | 
			
		||||
	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_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);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user