summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2024-03-07 16:18:32 -0500
committerTavian Barnes <tavianator@tavianator.com>2024-03-07 16:26:25 -0500
commit43cd776d7dc8ac573262f8459edeb1c1f5f3cd09 (patch)
tree4aae15ba3afb55506e43faf3dd36076f504fb793
parent416ca3b557055efa5746a4d40d927391c59a9292 (diff)
downloadbfs-43cd776d7dc8ac573262f8459edeb1c1f5f3cd09.tar.xz
xtime: Call tzset() from main() instead of lazily
POSIX specifies[1] that If a thread accesses tzname, daylight, or timezone directly while another thread is in a call to tzset(), or to any function that is required or allowed to set timezone information as if by calling tzset(), the behavior is undefined. So calling it lazily from arbitrary threads is risky. [1]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/tzset.html
-rw-r--r--src/eval.c2
-rw-r--r--src/main.c4
-rw-r--r--src/parse.c12
-rw-r--r--src/printf.c4
-rw-r--r--src/xtime.c34
-rw-r--r--src/xtime.h24
-rw-r--r--tests/bfstd.c5
-rw-r--r--tests/main.c14
-rw-r--r--tests/xtime.c10
-rw-r--r--tests/xtouch.c2
10 files changed, 32 insertions, 79 deletions
diff --git a/src/eval.c b/src/eval.c
index 8b71833..1711001 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -730,7 +730,7 @@ bool eval_fls(const struct bfs_expr *expr, struct bfs_eval *state) {
time_t six_months_ago = now - 6 * 30 * 24 * 60 * 60;
time_t tomorrow = now + 24 * 60 * 60;
struct tm tm;
- if (xlocaltime(&time, &tm) != 0) {
+ if (!localtime_r(&time, &tm)) {
goto error;
}
char time_str[256];
diff --git a/src/main.c b/src/main.c
index 1ddbc54..16a2576 100644
--- a/src/main.c
+++ b/src/main.c
@@ -55,6 +55,7 @@
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
+#include <time.h>
#include <unistd.h>
/**
@@ -121,6 +122,9 @@ int main(int argc, char *argv[]) {
locale_err = errno;
}
+ // Apply the environment's timezone
+ tzset();
+
// Parse the command line
struct bfs_ctx *ctx = bfs_parse_cmdline(argc, argv);
if (!ctx) {
diff --git a/src/parse.c b/src/parse.c
index 5d0f333..b26a50f 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -1086,8 +1086,8 @@ static struct bfs_expr *parse_const(struct bfs_parser *parser, int value, int ar
*/
static struct bfs_expr *parse_daystart(struct bfs_parser *parser, int arg1, int arg2) {
struct tm tm;
- if (xlocaltime(&parser->now.tv_sec, &tm) != 0) {
- parse_perror(parser, "xlocaltime()");
+ if (!localtime_r(&parser->now.tv_sec, &tm)) {
+ parse_perror(parser, "localtime_r()");
return NULL;
}
@@ -1708,8 +1708,8 @@ static int parse_reftime(const struct bfs_parser *parser, struct bfs_expr *expr)
fprintf(stderr, "Supported timestamp formats are ISO 8601-like, e.g.\n\n");
struct tm tm;
- if (xlocaltime(&parser->now.tv_sec, &tm) != 0) {
- parse_perror(parser, "xlocaltime()");
+ if (!localtime_r(&parser->now.tv_sec, &tm)) {
+ parse_perror(parser, "localtime_r()");
return -1;
}
@@ -1728,8 +1728,8 @@ static int parse_reftime(const struct bfs_parser *parser, struct bfs_expr *expr)
fprintf(stderr, " - %04d-%02d-%02dT%02d:%02d:%02d%+03d:%02d\n",
year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tz_hour, tz_min);
- if (xgmtime(&parser->now.tv_sec, &tm) != 0) {
- parse_perror(parser, "xgmtime()");
+ if (!gmtime_r(&parser->now.tv_sec, &tm)) {
+ parse_perror(parser, "gmtime_r()");
return -1;
}
diff --git a/src/printf.c b/src/printf.c
index 1fe8cc3..34ed606 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -123,7 +123,7 @@ static int bfs_printf_ctime(CFILE *cfile, const struct bfs_fmt *fmt, const struc
}
struct tm tm;
- if (xlocaltime(&ts->tv_sec, &tm) != 0) {
+ if (!localtime_r(&ts->tv_sec, &tm)) {
return -1;
}
@@ -153,7 +153,7 @@ static int bfs_printf_strftime(CFILE *cfile, const struct bfs_fmt *fmt, const st
}
struct tm tm;
- if (xlocaltime(&ts->tv_sec, &tm) != 0) {
+ if (!localtime_r(&ts->tv_sec, &tm)) {
return -1;
}
diff --git a/src/xtime.c b/src/xtime.c
index 4309289..8100f6c 100644
--- a/src/xtime.c
+++ b/src/xtime.c
@@ -11,38 +11,6 @@
#include <time.h>
#include <unistd.h>
-/** Call tzset() if necessary. */
-static void xtzset(void) {
- static atomic bool is_set = false;
-
- if (!load(&is_set, relaxed)) {
- tzset();
- store(&is_set, true, relaxed);
- }
-}
-
-int xlocaltime(const time_t *timep, struct tm *result) {
- // Should be called before localtime_r() according to POSIX.1-2004
- xtzset();
-
- if (localtime_r(timep, result)) {
- return 0;
- } else {
- return -1;
- }
-}
-
-int xgmtime(const time_t *timep, struct tm *result) {
- // Should be called before gmtime_r() according to POSIX.1-2004
- xtzset();
-
- if (gmtime_r(timep, result)) {
- return 0;
- } else {
- return -1;
- }
-}
-
int xmktime(struct tm *tm, time_t *timep) {
*timep = mktime(tm);
@@ -50,7 +18,7 @@ int xmktime(struct tm *tm, time_t *timep) {
int error = errno;
struct tm tmp;
- if (xlocaltime(timep, &tmp) != 0) {
+ if (!localtime_r(timep, &tmp)) {
return -1;
}
diff --git a/src/xtime.h b/src/xtime.h
index 75d1f4e..fb60ae4 100644
--- a/src/xtime.h
+++ b/src/xtime.h
@@ -11,30 +11,6 @@
#include <time.h>
/**
- * localtime_r() wrapper that calls tzset() first.
- *
- * @param[in] timep
- * The time_t to convert.
- * @param[out] result
- * Buffer to hold the result.
- * @return
- * 0 on success, -1 on failure.
- */
-int xlocaltime(const time_t *timep, struct tm *result);
-
-/**
- * gmtime_r() wrapper that calls tzset() first.
- *
- * @param[in] timep
- * The time_t to convert.
- * @param[out] result
- * Buffer to hold the result.
- * @return
- * 0 on success, -1 on failure.
- */
-int xgmtime(const time_t *timep, struct tm *result);
-
-/**
* mktime() wrapper that reports errors more reliably.
*
* @param[in,out] tm
diff --git a/tests/bfstd.c b/tests/bfstd.c
index 4d0ec23..0ded5de 100644
--- a/tests/bfstd.c
+++ b/tests/bfstd.c
@@ -43,11 +43,6 @@ static bool check_wordesc(const char *str, const char *exp, enum wesc_flags flag
bool check_bfstd(void) {
bool ret = true;
- // Try to set a UTF-8 locale
- if (!setlocale(LC_ALL, "C.UTF-8")) {
- setlocale(LC_ALL, "");
- }
-
// From man 3p basename
ret &= check_base_dir("usr", ".", "usr");
ret &= check_base_dir("usr/", ".", "usr");
diff --git a/tests/main.c b/tests/main.c
index e69fe35..38438b2 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -11,9 +11,11 @@
#include "../src/config.h"
#include "../src/diag.h"
#include <errno.h>
+#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
/**
* Test context.
@@ -90,6 +92,18 @@ static void run_test(struct test_ctx *ctx, const char *test, test_fn *fn) {
}
int main(int argc, char *argv[]) {
+ // Try to set a UTF-8 locale
+ if (!setlocale(LC_ALL, "C.UTF-8")) {
+ setlocale(LC_ALL, "");
+ }
+
+ // Run tests in UTC
+ if (setenv("TZ", "UTC0", true) != 0) {
+ perror("setenv()");
+ return EXIT_FAILURE;
+ }
+ tzset();
+
struct test_ctx ctx;
if (test_init(&ctx, argc, argv) != 0) {
goto done;
diff --git a/tests/xtime.c b/tests/xtime.c
index f853428..2609c1c 100644
--- a/tests/xtime.c
+++ b/tests/xtime.c
@@ -81,8 +81,8 @@ static bool check_xgetdate(void) {
/** Check one xmktime() result. */
static bool check_one_xmktime(time_t expected) {
struct tm tm;
- if (xlocaltime(&expected, &tm) != 0) {
- bfs_diag("xlocaltime(%jd): %s", (intmax_t)expected, xstrerror(errno));
+ if (!localtime_r(&expected, &tm)) {
+ bfs_diag("localtime_r(%jd): %s", (intmax_t)expected, xstrerror(errno));
return false;
}
@@ -145,12 +145,6 @@ static bool check_xtimegm(void) {
}
bool check_xtime(void) {
- if (setenv("TZ", "UTC0", true) != 0) {
- perror("setenv()");
- return false;
- }
- tzset();
-
bool ret = true;
ret &= check_xgetdate();
ret &= check_xmktime();
diff --git a/tests/xtouch.c b/tests/xtouch.c
index 6099128..8c5c5f3 100644
--- a/tests/xtouch.c
+++ b/tests/xtouch.c
@@ -157,6 +157,8 @@ done:
}
int main(int argc, char *argv[]) {
+ tzset();
+
mode_t mask = umask(0);
struct args args = {