Index: contrib/tzcode/stdtime/localtime.c =================================================================== --- contrib/tzcode/stdtime/localtime.c +++ contrib/tzcode/stdtime/localtime.c @@ -354,6 +354,45 @@ } } +#ifdef DETECT_TZ_CHANGES +/* + * Determine if there's a change in the timezone since the last time we checked. + * Returns: -1 on error + * 0 if the timezone has not changed + * 1 if the timezone has changed + */ +static int +change_in_tz(const char *name) +{ + static char old_name[PATH_MAX]; + static struct stat old_sb; + struct stat sb; + int error; + + error = stat(name, &sb); + if (error != 0) + return -1; + + if (strcmp(name, old_name) != 0) { + strlcpy(old_name, name, sizeof(old_name)); + old_sb = sb; + return 1; + } + + if (sb.st_dev != old_sb.st_dev || + sb.st_ino != old_sb.st_ino || + sb.st_ctime != old_sb.st_ctime || + sb.st_mtime != old_sb.st_mtime) { + old_sb = sb; + return 1; + } + + return 0; +} +#else /* !DETECT_TZ_CHANGES */ +#define change_in_tz(X) 0 +#endif /* !DETECT_TZ_CHANGES */ + static int differ_by_repeat(const time_t t1, const time_t t0) { @@ -379,6 +418,7 @@ int stored; int nread; int res; + int ret; union { struct tzhead tzhead; char buf[2 * sizeof(struct tzhead) + @@ -427,6 +467,22 @@ (void) strcat(fullname, name); name = fullname; } + if (doextend == TRUE) { + /* + * Detect if the timezone file has changed. Check + * 'doextend' to ignore TZDEFRULES; the change_in_tz() + * function can only keep state for a single file. + */ + ret = change_in_tz(name); + if (ret <= 0) { + /* + * Returns -1 if there was an error, + * and 0 if the timezone had not changed. + */ + free(fullname); + return ret; + } + } if ((fid = _open(name, OPEN_MODE)) == -1) { free(fullname); return -1; @@ -1209,12 +1265,43 @@ (void) tzparse(gmt, sp, TRUE); } +#ifdef DETECT_TZ_CHANGES +static int +recheck_tzdata() +{ + static time_t last_checked; + struct timespec now; + time_t current_time; + int error; + + /* + * We want to recheck the timezone file every 61 sec. + */ + error = clock_gettime(CLOCK_MONOTONIC, &now); + if (error <= 0) { + /* XXX: Can we somehow report this? */ + return 0; + } + + current_time = now.tv_sec; + if ((current_time - last_checked > 61) || + (last_checked > current_time)) { + last_checked = current_time; + return 1; + } + + return 0; +} +#else /* !DETECT_TZ_CHANGES */ +#define recheck_tzdata() 0 +#endif /* !DETECT_TZ_CHANGES */ + static void tzsetwall_basic(int rdlocked) { if (!rdlocked) _RWLOCK_RDLOCK(&lcl_rwlock); - if (lcl_is_set < 0) { + if (lcl_is_set < 0 && recheck_tzdata() == 0) { if (!rdlocked) _RWLOCK_UNLOCK(&lcl_rwlock); return; Index: lib/libc/stdtime/Makefile.inc =================================================================== --- lib/libc/stdtime/Makefile.inc +++ lib/libc/stdtime/Makefile.inc @@ -12,6 +12,10 @@ CFLAGS.localtime.c= -fwrapv +.if ${MK_DETECT_TZ_CHANGES} != "no" +CFLAGS+= -DDETECT_TZ_CHANGES +.endif + MAN+= ctime.3 strftime.3 strptime.3 time2posix.3 MAN+= tzfile.5 Index: share/mk/src.opts.mk =================================================================== --- share/mk/src.opts.mk +++ share/mk/src.opts.mk @@ -197,6 +197,7 @@ BHYVE_SNAPSHOT \ CLANG_EXTRAS \ CLANG_FORMAT \ + DETECT_TZ_CHANGES \ DTRACE_TESTS \ EXPERIMENTAL \ HESIOD \ Index: tools/build/options/WITH_DETECT_TZ_CHANGES =================================================================== --- /dev/null +++ tools/build/options/WITH_DETECT_TZ_CHANGES @@ -0,0 +1,2 @@ +.\" $FreeBSD$ +Make the time handling code detect changes to the timezone files.