From bdaeafea5dff32ccb479ecf8fe0a3173c995fbf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 1 Jul 2021 10:10:52 +0000 Subject: [PATCH 1/3] time-util: add variant of timezone_is_valid() that returns errno This will be useful for tests to skip missing time zones. --- src/basic/time-util.c | 43 ++++++++++++++++----------------------- src/basic/time-util.h | 5 ++++- src/test/test-time-util.c | 12 +++++++++++ 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/basic/time-util.c b/src/basic/time-util.c index b2c86a0e41..796caac691 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -1381,7 +1381,7 @@ int get_timezones(char ***ret) { return 0; } -bool timezone_is_valid(const char *name, int log_level) { +int verify_timezone(const char *name, int log_level) { bool slash = false; const char *p, *t; _cleanup_close_ int fd = -1; @@ -1389,26 +1389,26 @@ bool timezone_is_valid(const char *name, int log_level) { int r; if (isempty(name)) - return false; + return -EINVAL; /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */ if (streq(name, "UTC")) - return true; + return 0; if (name[0] == '/') - return false; + return -EINVAL; for (p = name; *p; p++) { if (!(*p >= '0' && *p <= '9') && !(*p >= 'a' && *p <= 'z') && !(*p >= 'A' && *p <= 'Z') && !IN_SET(*p, '-', '_', '+', '/')) - return false; + return -EINVAL; if (*p == '/') { if (slash) - return false; + return -EINVAL; slash = true; } else @@ -1416,38 +1416,31 @@ bool timezone_is_valid(const char *name, int log_level) { } if (slash) - return false; + return -EINVAL; if (p - name >= PATH_MAX) - return false; + return -ENAMETOOLONG; t = strjoina("/usr/share/zoneinfo/", name); fd = open(t, O_RDONLY|O_CLOEXEC); - if (fd < 0) { - log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t); - return false; - } + if (fd < 0) + return log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t); r = fd_verify_regular(fd); - if (r < 0) { - log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t); - return false; - } + if (r < 0) + return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t); r = loop_read_exact(fd, buf, 4, false); - if (r < 0) { - log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t); - return false; - } + if (r < 0) + return log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t); /* Magic from tzfile(5) */ - if (memcmp(buf, "TZif", 4) != 0) { - log_full(log_level, "Timezone file '%s' has wrong magic bytes", t); - return false; - } + if (memcmp(buf, "TZif", 4) != 0) + return log_full_errno(log_level, SYNTHETIC_ERRNO(EIO), + "Timezone file '%s' has wrong magic bytes", t); - return true; + return 0; } bool clock_boottime_supported(void) { diff --git a/src/basic/time-util.h b/src/basic/time-util.h index cfde189818..2bd947d6a8 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -134,7 +134,10 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit); int parse_nsec(const char *t, nsec_t *nsec); int get_timezones(char ***l); -bool timezone_is_valid(const char *name, int log_level); +int verify_timezone(const char *name, int log_level); +static inline bool timezone_is_valid(const char *name, int log_level) { + return verify_timezone(name, log_level) >= 0; +} bool clock_boottime_supported(void); bool clock_supported(clockid_t clock); diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index a40cc52314..ee4d20449f 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -243,6 +243,17 @@ static void test_format_timespan(usec_t accuracy) { test_format_timespan_one(USEC_INFINITY, accuracy); } +static void test_verify_timezone(void) { + log_info("/* %s */", __func__); + + assert_se(verify_timezone("Europe/Berlin", LOG_DEBUG) == 0); + assert_se(verify_timezone("Australia/Sydney", LOG_DEBUG) == 0); + assert_se(verify_timezone("Europe/Do not exist", LOG_DEBUG) == -EINVAL); + assert_se(verify_timezone("Europe/DoNotExist", LOG_DEBUG) == -ENOENT); + assert_se(verify_timezone("/DoNotExist", LOG_DEBUG) == -EINVAL); + assert_se(verify_timezone("DoNotExist/", LOG_DEBUG) == -EINVAL); +} + static void test_timezone_is_valid(void) { log_info("/* %s */", __func__); @@ -607,6 +618,7 @@ int main(int argc, char *argv[]) { test_format_timespan(1); test_format_timespan(USEC_PER_MSEC); test_format_timespan(USEC_PER_SEC); + test_verify_timezone(); test_timezone_is_valid(); test_get_timezones(); test_usec_add(); From dc9849b1e568ed0c16e1b5e2c7f22e2f10ae65aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 1 Jul 2021 10:16:56 +0000 Subject: [PATCH 2/3] test-time-util: skip missing timezones Fixes #20089. This is essentially a packaging bug in CentOS: the db lists a timezone which is not present in /usr/share/zoneinfo. Let's skip this gracefully. --- src/test/test-time-util.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index ee4d20449f..0ca9beeb51 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -273,8 +273,9 @@ static void test_get_timezones(void) { assert_se(r == 0); STRV_FOREACH(zone, zones) { - log_info("zone: %s", *zone); - assert_se(timezone_is_valid(*zone, LOG_ERR)); + r = verify_timezone(*zone, LOG_ERR); + log_debug_errno(r, "verify_timezone(\"%s\"): %m", *zone); + assert_se(r >= 0 || r == -ENOENT); } } From 41bc83ed5187f5cb6e7243a36827fc51899652fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 1 Jul 2021 10:17:40 +0000 Subject: [PATCH 3/3] test-time-util: log less verbosely Output can always be kranked up with LOG_LEVEL=debug. But let's make less predictable noise by default. --- src/test/test-time-util.c | 44 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index 0ca9beeb51..6f4675aaf4 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -202,13 +202,13 @@ static void test_format_timespan_one(usec_t x, usec_t accuracy) { const char *t; usec_t y; - log_info(USEC_FMT" (at accuracy "USEC_FMT")", x, accuracy); + log_debug(USEC_FMT" (at accuracy "USEC_FMT")", x, accuracy); assert_se(t = format_timespan(l, sizeof l, x, accuracy)); - log_info(" = <%s>", t); + log_debug(" = <%s>", t); assert_se(parse_sec(t, &y) >= 0); - log_info(" = "USEC_FMT, y); + log_debug(" = "USEC_FMT, y); if (accuracy <= 0) accuracy = 1; @@ -329,11 +329,9 @@ static void test_usec_sub_signed(void) { } static void test_format_timestamp(void) { - unsigned i; - log_info("/* %s */", __func__); - for (i = 0; i < 100; i++) { + for (unsigned i = 0; i < 100; i++) { char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESPAN_MAX)]; usec_t x, y; @@ -341,27 +339,27 @@ static void test_format_timestamp(void) { x = x % (2147483600 * USEC_PER_SEC) + 1; assert_se(format_timestamp(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(parse_timestamp(buf, &y) >= 0); assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC); assert_se(format_timestamp_style(buf, sizeof(buf), x, TIMESTAMP_UTC)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(parse_timestamp(buf, &y) >= 0); assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC); assert_se(format_timestamp_style(buf, sizeof(buf), x, TIMESTAMP_US)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(parse_timestamp(buf, &y) >= 0); assert_se(x == y); assert_se(format_timestamp_style(buf, sizeof(buf), x, TIMESTAMP_US_UTC)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(parse_timestamp(buf, &y) >= 0); assert_se(x == y); assert_se(format_timestamp_relative(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(parse_timestamp(buf, &y) >= 0); /* The two calls above will run with a slightly different local time. Make sure we are in the same @@ -385,64 +383,64 @@ static void test_format_timestamp_relative(void) { /* Years and months */ x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 1*USEC_PER_MONTH); assert_se(format_timestamp_relative(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(streq(buf, "1 year 1 month ago")); x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 2*USEC_PER_MONTH); assert_se(format_timestamp_relative(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(streq(buf, "1 year 2 months ago")); x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 1*USEC_PER_MONTH); assert_se(format_timestamp_relative(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(streq(buf, "2 years 1 month ago")); x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 2*USEC_PER_MONTH); assert_se(format_timestamp_relative(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(streq(buf, "2 years 2 months ago")); /* Months and days */ x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 1*USEC_PER_DAY); assert_se(format_timestamp_relative(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(streq(buf, "1 month 1 day ago")); x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 2*USEC_PER_DAY); assert_se(format_timestamp_relative(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(streq(buf, "1 month 2 days ago")); x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 1*USEC_PER_DAY); assert_se(format_timestamp_relative(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(streq(buf, "2 months 1 day ago")); x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 2*USEC_PER_DAY); assert_se(format_timestamp_relative(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(streq(buf, "2 months 2 days ago")); /* Weeks and days */ x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 1*USEC_PER_DAY); assert_se(format_timestamp_relative(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(streq(buf, "1 week 1 day ago")); x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 2*USEC_PER_DAY); assert_se(format_timestamp_relative(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(streq(buf, "1 week 2 days ago")); x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 1*USEC_PER_DAY); assert_se(format_timestamp_relative(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(streq(buf, "2 weeks 1 day ago")); x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 2*USEC_PER_DAY); assert_se(format_timestamp_relative(buf, sizeof(buf), x)); - log_info("%s", buf); + log_debug("%s", buf); assert_se(streq(buf, "2 weeks 2 days ago")); }