From 672924356f9dc2876cc416a8e0cb25c1b6f1c0dd Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 28 Sep 2025 10:09:44 +0900 Subject: [PATCH 1/3] calendar-spec: use SAVE_TIMEZONE Then, we can avoid heavy fork() operation. --- src/shared/calendarspec.c | 45 +++++++-------------------------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c index 3569f15db4..b15f929036 100644 --- a/src/shared/calendarspec.c +++ b/src/shared/calendarspec.c @@ -1426,13 +1426,7 @@ static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, u return 0; } -typedef struct SpecNextResult { - usec_t next; - int return_value; -} SpecNextResult; - int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *ret_next) { - SpecNextResult *shared, tmp; int r; assert(spec); @@ -1440,39 +1434,16 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *ret_n if (isempty(spec->timezone)) return calendar_spec_next_usec_impl(spec, usec, ret_next); - shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); - if (shared == MAP_FAILED) - return negative_errno(); + SAVE_TIMEZONE; - r = safe_fork("(sd-calendar)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, NULL); - if (r < 0) { - (void) munmap(shared, sizeof *shared); + /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */ + const char *colon_tz = strjoina(":", spec->timezone); + + r = RET_NERRNO(setenv("TZ", colon_tz, 1)); + if (r < 0) return r; - } - if (r == 0) { - char *colon_tz; - /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */ - colon_tz = strjoina(":", spec->timezone); + tzset(); - if (setenv("TZ", colon_tz, 1) != 0) { - shared->return_value = negative_errno(); - _exit(EXIT_FAILURE); - } - - tzset(); - - shared->return_value = calendar_spec_next_usec_impl(spec, usec, &shared->next); - - _exit(EXIT_SUCCESS); - } - - tmp = *shared; - if (munmap(shared, sizeof *shared) < 0) - return negative_errno(); - - if (tmp.return_value == 0 && ret_next) - *ret_next = tmp.next; - - return tmp.return_value; + return calendar_spec_next_usec_impl(spec, usec, ret_next); } From 7d6f1d695c5760d6eb403a1829e6b0cc7e9b0ed8 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 28 Sep 2025 10:10:47 +0900 Subject: [PATCH 2/3] timedatectl,test: use SAVE_TIMEZONE at more places --- src/test/test-calendarspec.c | 8 +------- src/timedate/timedatectl.c | 13 ++----------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c index 9abbde622f..0b1cbacd62 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -44,12 +44,9 @@ static void _test_one(int line, const char *input, const char *output) { static void _test_next(int line, const char *input, const char *new_tz, usec_t after, usec_t expect) { _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL; usec_t u; - char *old_tz; int r; - old_tz = getenv("TZ"); - if (old_tz) - old_tz = strdupa_safe(old_tz); + SAVE_TIMEZONE; if (!isempty(new_tz) && !strchr(new_tz, ',')) new_tz = strjoina(":", new_tz); @@ -68,9 +65,6 @@ static void _test_next(int line, const char *input, const char *new_tz, usec_t a assert_se(r >= 0 && u == expect); else assert_se(r == -ENOENT); - - assert_se(set_unset_env("TZ", old_tz, true) == 0); - tzset(); } #define test_next(input, new_tz, after, expect) _test_next(__LINE__, input,new_tz,after,expect) diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c index 0bbc740d46..f3a8046bed 100644 --- a/src/timedate/timedatectl.c +++ b/src/timedate/timedatectl.c @@ -58,7 +58,7 @@ typedef struct StatusInfo { static int print_status_info(const StatusInfo *i) { _cleanup_(table_unrefp) Table *table = NULL; - const char *old_tz = NULL, *tz, *tz_colon; + const char *tz_colon; char a[LINE_MAX]; TableCell *cell; struct tm tm; @@ -79,9 +79,7 @@ static int print_status_info(const StatusInfo *i) { (void) table_set_ellipsize_percent(table, cell, 100); /* Save the old $TZ */ - tz = getenv("TZ"); - if (tz) - old_tz = strdupa_safe(tz); + SAVE_TIMEZONE; /* Set the new $TZ */ tz_colon = strjoina(":", isempty(i->timezone) ? "UTC" : i->timezone); @@ -158,13 +156,6 @@ static int print_status_info(const StatusInfo *i) { if (r < 0) return table_log_add_error(r); - /* Restore the $TZ */ - r = set_unset_env("TZ", old_tz, true); - if (r < 0) - log_warning_errno(r, "Failed to set TZ environment variable, ignoring: %m"); - else - tzset(); - r = table_add_many(table, TABLE_FIELD, "System clock synchronized", TABLE_BOOLEAN, i->ntp_synced, From 8431368668d59f768e8a8ec8194490f7db3f1b2e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 28 Sep 2025 10:19:08 +0900 Subject: [PATCH 3/3] tree-wide: stop assigning colon prefixed timezone to $TZ glibc (and also musl, though we do not officially support it yet) silently ignores colon prefix in $TZ. Let's always not prefix the timezone. tzset(3) states: > A nonempty value of TZ can be one of two formats, either of which can > be preceded by a colon which is ignored. Addresses https://github.com/systemd/systemd/pull/38876#discussion_r2384347594. --- src/basic/time-util.c | 2 +- src/shared/calendarspec.c | 5 +---- src/test/test-time-util.c | 14 ++++---------- src/timedate/timedatectl.c | 4 +--- 4 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/basic/time-util.c b/src/basic/time-util.c index fb54ba276c..e54c829623 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -1022,7 +1022,7 @@ int parse_timestamp(const char *t, usec_t *ret) { if (timezone_is_valid(tz, LOG_DEBUG)) { SAVE_TIMEZONE; - if (setenv("TZ", strjoina(":", tz), /* overwrite = */ true) < 0) + if (setenv("TZ", tz, /* overwrite = */ true) < 0) return negative_errno(); return parse_timestamp_impl(t, max_len, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret); diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c index b15f929036..0e12e55a97 100644 --- a/src/shared/calendarspec.c +++ b/src/shared/calendarspec.c @@ -1436,10 +1436,7 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *ret_n SAVE_TIMEZONE; - /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */ - const char *colon_tz = strjoina(":", spec->timezone); - - r = RET_NERRNO(setenv("TZ", colon_tz, 1)); + r = RET_NERRNO(setenv("TZ", spec->timezone, /* overwrite = */ true)); if (r < 0) return r; diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index 7569f89ca0..7bcf0ae7ee 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -13,13 +13,7 @@ #define TRIAL 100u static void set_timezone(const char *tz) { - if (!tz) - ASSERT_OK_ERRNO(unsetenv("TZ")); - if (isempty(tz)) - ASSERT_OK_ERRNO(setenv("TZ", tz, /* overwrite = */ true)); - else - ASSERT_OK_ERRNO(setenv("TZ", strjoina(":", tz), /* overwrite = */ true)); - + ASSERT_OK(set_unset_env("TZ", tz, /* overwrite = */ true)); tzset(); log_info("TZ=%s, tzname[0]=%s, tzname[1]=%s", strna(getenv("TZ")), strempty(tzname[0]), strempty(tzname[1])); } @@ -421,7 +415,7 @@ static void test_format_timestamp_impl(usec_t x) { * timezone may provide time shifted 1 hour from the original. See * https://github.com/systemd/systemd/issues/28472 and https://github.com/systemd/systemd/pull/35471 */ bool ignore = - streq_ptr(getenv("TZ"), ":Africa/Windhoek") && + streq_ptr(getenv("TZ"), "Africa/Windhoek") && (x_sec > y_sec ? x_sec - y_sec : y_sec - x_sec) == 3600; log_full(ignore ? LOG_WARNING : LOG_ERR, @@ -1113,14 +1107,14 @@ TEST(usec_shift_clock) { TEST(in_utc_timezone) { SAVE_TIMEZONE; - assert_se(setenv("TZ", ":UTC", 1) >= 0); + assert_se(setenv("TZ", "UTC", 1) >= 0); assert_se(in_utc_timezone()); ASSERT_STREQ(tzname[0], "UTC"); ASSERT_STREQ(tzname[1], "UTC"); assert_se(timezone == 0); assert_se(daylight == 0); - assert_se(setenv("TZ", ":Europe/Berlin", 1) >= 0); + assert_se(setenv("TZ", "Europe/Berlin", 1) >= 0); assert_se(!in_utc_timezone()); ASSERT_STREQ(tzname[0], "CET"); ASSERT_STREQ(tzname[1], "CEST"); diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c index f3a8046bed..0e8e705a50 100644 --- a/src/timedate/timedatectl.c +++ b/src/timedate/timedatectl.c @@ -58,7 +58,6 @@ typedef struct StatusInfo { static int print_status_info(const StatusInfo *i) { _cleanup_(table_unrefp) Table *table = NULL; - const char *tz_colon; char a[LINE_MAX]; TableCell *cell; struct tm tm; @@ -82,8 +81,7 @@ static int print_status_info(const StatusInfo *i) { SAVE_TIMEZONE; /* Set the new $TZ */ - tz_colon = strjoina(":", isempty(i->timezone) ? "UTC" : i->timezone); - if (setenv("TZ", tz_colon, true) < 0) + if (setenv("TZ", isempty(i->timezone) ? "UTC" : i->timezone, /* overwrite = */ true) < 0) log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m"); else tzset();