mailing list of musl libc
 help / color / mirror / code / Atom feed
* detect timezone changes by monitoring /etc/localtime (like glibc)
@ 2017-06-09  7:15 Patrick Oppenlander
  2017-06-09 12:15 ` Rich Felker
  0 siblings, 1 reply; 10+ messages in thread
From: Patrick Oppenlander @ 2017-06-09  7:15 UTC (permalink / raw)
  To: musl

During some recent testing I came across a bug when adjusting timezones on an
embedded system by changing /etc/localtime. The cause ended up being a
behavioural difference between glibc and musl.

A program compiled against musl will not detect changes to /etc/localtime,
while a program compiled against glibc will.

Intuitively it seems like correct behaviour to me to detect and act upon system
timezone changes, however I am not familiar with any pertinent specifications
for this.

Under glibc if you set TZ=:/etc/localtime changes will not be detected and no
extra syscalls are invoked. This is equivalent to the current musl behaviour.

Given the above I have attached a proposed patch for review. This is
compile-tested only as I ran out of time and would like reviewer comments
before going any further. Is a change in this direction acceptable?

One possible (unlikely?) problem could be if TZ changes in such a way as to
match the cached stat values.

		Patrick

===8<===

diff --git a/src/time/__tz.c b/src/time/__tz.c
index ffe8d402..305616d5 100644
--- a/src/time/__tz.c
+++ b/src/time/__tz.c
@@ -3,6 +3,7 @@
  #include <limits.h>
  #include <stdlib.h>
  #include <string.h>
+#include <sys/stat.h>
  #include "libc.h"
  
  long  __timezone = 0;
@@ -17,15 +18,24 @@ static char std_name[TZNAME_MAX+1];
  static char dst_name[TZNAME_MAX+1];
  const char __gmt[] = "GMT";
  
+static const char etc_lt[] = "/etc/localtime";
+
  static int dst_off;
  static int r0[5], r1[5];
  
  static const unsigned char *zi, *trans, *index, *types, *abbrevs, *abbrevs_end;
  static size_t map_size;
  
-static char old_tz_buf[32];
-static char *old_tz = old_tz_buf;
-static size_t old_tz_size = sizeof old_tz_buf;
+static union {
+	char tz_buf[32];
+	struct {
+		ino_t ino;
+		dev_t dev;
+		time_t mtime;
+	};
+} old;
+static char *old_tz = old.tz_buf;
+static size_t old_tz_size = sizeof old.tz_buf;
  
  static volatile int lock[2];
  
@@ -123,27 +133,37 @@ static void do_tzset()
  	size_t i;
  	static const char search[] =
  		"/usr/share/zoneinfo/\0/share/zoneinfo/\0/etc/zoneinfo/\0";
+	struct stat st;
  
  	s = getenv("TZ");
-	if (!s) s = "/etc/localtime";
+	if (!s) s = stat(etc_lt, &st) == 0 ? etc_lt : __gmt;
  	if (!*s) s = __gmt;
  
-	if (old_tz && !strcmp(s, old_tz)) return;
+	if (s == etc_lt && st.st_ino == old.ino && st.st_dev == old.dev &&
+	    st.st_mtime == old.mtime) return;
+	if (s != etc_lt && old_tz && !strcmp(s, old_tz)) return;
  
  	if (zi) __munmap((void *)zi, map_size);
  
-	/* Cache the old value of TZ to check if it has changed. Avoid
-	 * free so as not to pull it into static programs. Growth
-	 * strategy makes it so free would have minimal benefit anyway. */
-	i = strlen(s);
-	if (i > PATH_MAX+1) s = __gmt, i = 3;
-	if (i >= old_tz_size) {
-		old_tz_size *= 2;
-		if (i >= old_tz_size) old_tz_size = i+1;
-		if (old_tz_size > PATH_MAX+2) old_tz_size = PATH_MAX+2;
-		old_tz = malloc(old_tz_size);
+	/* Cache the old value of TZ or stat of /etc/localtime to check
+	 * if it has changed. Avoid free so as not to pull it into static
+	 * programs. Growth strategy makes it so free would have minimal
+	 * benefit anyway. */
+	if (s == etc_lt) {
+		old.ino = st.st_ino;
+		old.dev = st.st_dev;
+		old.mtime = st.st_mtime;
+	} else {
+		i = strlen(s);
+		if (i > PATH_MAX+1) s = __gmt, i = 3;
+		if (i >= old_tz_size) {
+			old_tz_size *= 2;
+			if (i >= old_tz_size) old_tz_size = i+1;
+			if (old_tz_size > PATH_MAX+2) old_tz_size = PATH_MAX+2;
+			old_tz = malloc(old_tz_size);
+		}
+		if (old_tz) memcpy(old_tz, s, i+1);
  	}
-	if (old_tz) memcpy(old_tz, s, i+1);
  
  	/* Non-suid can use an absolute tzfile pathname or a relative
  	 * pathame beginning with "."; in secure mode, only the
@@ -151,7 +171,7 @@ static void do_tzset()
  	if (*s == ':' || ((p=strchr(s, '/')) && !memchr(s, ',', p-s))) {
  		if (*s == ':') s++;
  		if (*s == '/' || *s == '.') {
-			if (!libc.secure || !strcmp(s, "/etc/localtime"))
+			if (!libc.secure || !strcmp(s, etc_lt))
  				map = __map_file(s, &map_size);
  		} else {
  			size_t l = strlen(s);


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2017-06-12 23:58 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-09  7:15 detect timezone changes by monitoring /etc/localtime (like glibc) Patrick Oppenlander
2017-06-09 12:15 ` Rich Felker
2017-06-10  3:36   ` A. Wilcox
2017-06-10  9:57     ` u-uy74
2017-06-10 12:23       ` Rich Felker
2017-06-10 13:23         ` u-uy74
2017-06-12 23:08   ` Patrick Oppenlander
2017-06-12 23:19     ` Rich Felker
2017-06-12 23:49       ` Patrick Oppenlander
2017-06-12 23:58         ` Rich Felker

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).