[PATCH]-19.07 busybox: backport hwclock fixes

Rosen Penev rosenp at gmail.com
Sat Apr 10 00:43:31 BST 2021


Currently on devices with an RTC, sysfixtime uses busybox' hwclock to
set the RTC time. Unfortunately, g/settimeofday on musl completely
ignore the second parameter which forces users to use the syscall
directly.

Backport two patches to fix hwclock's support for timezones.

Signed-off-by: Rosen Penev <rosenp at gmail.com>
---
 Note: this is already fixed in master and 21.02.
 package/utils/busybox/Makefile                |   2 +-
 ...ock-Fix-settimeofday-for-glibc-v2.31.patch |  53 +++++
 ...fix-musl-breakage-of-settimeofday-tz.patch | 195 ++++++++++++++++++
 3 files changed, 249 insertions(+), 1 deletion(-)
 create mode 100644 package/utils/busybox/patches/010-hwclock-Fix-settimeofday-for-glibc-v2.31.patch
 create mode 100644 package/utils/busybox/patches/020-hwclock-fix-musl-breakage-of-settimeofday-tz.patch

diff --git a/package/utils/busybox/Makefile b/package/utils/busybox/Makefile
index 23dd5498a1..a12401ff30 100644
--- a/package/utils/busybox/Makefile
+++ b/package/utils/busybox/Makefile
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=busybox
 PKG_VERSION:=1.30.1
-PKG_RELEASE:=6
+PKG_RELEASE:=7
 PKG_FLAGS:=essential
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
diff --git a/package/utils/busybox/patches/010-hwclock-Fix-settimeofday-for-glibc-v2.31.patch b/package/utils/busybox/patches/010-hwclock-Fix-settimeofday-for-glibc-v2.31.patch
new file mode 100644
index 0000000000..c6dd897b78
--- /dev/null
+++ b/package/utils/busybox/patches/010-hwclock-Fix-settimeofday-for-glibc-v2.31.patch
@@ -0,0 +1,53 @@
+From 38b8f0a91a786f42531b1e27746060377fbdcaba Mon Sep 17 00:00:00 2001
+From: Eddie James <eajames at linux.ibm.com>
+Date: Mon, 10 Aug 2020 09:59:02 -0500
+Subject: [PATCH 1/2] hwclock: Fix settimeofday for glibc v2.31+
+
+The glibc implementation changed for settimeofday, resulting in "invalid
+argument" error when attempting to set both timezone and time with a single
+call. Fix this by calling settimeofday twice
+
+Signed-off-by: Eddie James <eajames at linux.ibm.com>
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ util-linux/hwclock.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+--- a/util-linux/hwclock.c
++++ b/util-linux/hwclock.c
+@@ -122,16 +122,20 @@ static void to_sys_clock(const char **pp
+ 	struct timeval tv;
+ 	struct timezone tz;
+ 
+-	tz.tz_minuteswest = timezone/60;
++	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, &tz))
++	if (settimeofday(&tv, NULL))
+ 		bb_perror_msg_and_die("settimeofday");
+ }
+ 
+@@ -283,7 +287,11 @@ static void set_system_clock_timezone(in
+ 	gettimeofday(&tv, NULL);
+ 	if (!utc)
+ 		tv.tv_sec += tz.tz_minuteswest * 60;
+-	if (settimeofday(&tv, &tz))
++
++	/* glibc v2.31+ returns an error if both args are non-NULL */
++	if (settimeofday(NULL, &tz))
++		bb_perror_msg_and_die("settimeofday");
++	if (settimeofday(&tv, NULL))
+ 		bb_perror_msg_and_die("settimeofday");
+ }
+ 
diff --git a/package/utils/busybox/patches/020-hwclock-fix-musl-breakage-of-settimeofday-tz.patch b/package/utils/busybox/patches/020-hwclock-fix-musl-breakage-of-settimeofday-tz.patch
new file mode 100644
index 0000000000..88ab9f7b18
--- /dev/null
+++ b/package/utils/busybox/patches/020-hwclock-fix-musl-breakage-of-settimeofday-tz.patch
@@ -0,0 +1,195 @@
+From 54644059ef984727168c124858448ec66f4ecb5f Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Wed, 16 Dec 2020 13:49:10 +0100
+Subject: [PATCH 2/2] 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 at googlemail.com>
+---
+ util-linux/hwclock.c | 128 +++++++++++++++++++++++++------------------
+ 1 file changed, 75 insertions(+), 53 deletions(-)
+
+--- a/util-linux/hwclock.c
++++ b/util-linux/hwclock.c
+@@ -37,6 +37,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,
+@@ -117,26 +130,73 @@ static void show_clock(const char **pp_r
+ #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_perror_msg_and_die("settimeofday");
++	return set_kernel_timezone_and_clock(utc, &tv);
+ }
+ 
+ static void from_sys_clock(const char **pp_rtcname, int utc)
+@@ -262,39 +322,6 @@ static void from_sys_clock(const char **
+ 		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_perror_msg_and_die("settimeofday");
+-	if (settimeofday(&tv, NULL))
+-		bb_perror_msg_and_die("settimeofday");
+-}
+-
+ //usage:#define hwclock_trivial_usage
+ //usage:	IF_LONG_OPTS(
+ //usage:       "[-r|--show] [-s|--hctosys] [-w|--systohc] [--systz]"
+@@ -335,7 +362,6 @@ int hwclock_main(int argc UNUSED_PARAM,
+ 	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 */
+@@ -347,10 +373,6 @@ int hwclock_main(int argc UNUSED_PARAM,
+ 		"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,
+@@ -368,7 +390,7 @@ int hwclock_main(int argc UNUSED_PARAM,
+ 	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);
-- 
2.30.2




More information about the openwrt-devel mailing list