diff options
author | Lennart Poettering <lennart@poettering.net> | 2024-08-28 14:10:01 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2024-09-05 17:40:25 +0200 |
commit | 6f5cf41570776f489967d1a7de18260b2bc9acf9 (patch) | |
tree | 0c94612f28294ccf45e6f737c439d0f921c88cc2 /src/basic | |
parent | hwclock-util: the struct tm parameter is not a pure return parameter, it's al... (diff) | |
download | systemd-6f5cf41570776f489967d1a7de18260b2bc9acf9.tar.xz systemd-6f5cf41570776f489967d1a7de18260b2bc9acf9.zip |
time-util: rework localtime_or_gmtime() into localtime_or_gmtime_usec()
We typically want to deal in usec_t, hence let's change the prototype
accordingly, and do proper range checks. Also, make sure are not
confused by negative times.
Do something similar for mktime_or_timegm().
This is a more comprehensive alternative to #34065
Replaces: #34065
Diffstat (limited to 'src/basic')
-rw-r--r-- | src/basic/log.c | 8 | ||||
-rw-r--r-- | src/basic/os-util.c | 21 | ||||
-rw-r--r-- | src/basic/time-util.c | 70 | ||||
-rw-r--r-- | src/basic/time-util.h | 4 |
4 files changed, 70 insertions, 33 deletions
diff --git a/src/basic/log.c b/src/basic/log.c index c3e61ab5d4..b99f37385c 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -536,8 +536,8 @@ static int write_to_syslog( char header_priority[2 + DECIMAL_STR_MAX(int) + 1], header_time[64], header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1]; - time_t t; struct tm tm; + int r; if (syslog_fd < 0) return 0; @@ -547,9 +547,9 @@ static int write_to_syslog( xsprintf(header_priority, "<%i>", level); - t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC); - if (!localtime_r(&t, &tm)) - return -EINVAL; + r = localtime_or_gmtime_usec(now(CLOCK_REALTIME), /* utc= */ false, &tm); + if (r < 0) + return r; if (strftime(header_time, sizeof(header_time), "%h %e %T ", &tm) <= 0) return -EINVAL; diff --git a/src/basic/os-util.c b/src/basic/os-util.c index 9b31a0d325..4eec2f6014 100644 --- a/src/basic/os-util.c +++ b/src/basic/os-util.c @@ -450,24 +450,29 @@ int os_release_support_ended(const char *support_end, bool quiet, usec_t *ret_eo support_end = _support_end_alloc; } - if (isempty(support_end)) /* An empty string is a explicit way to say "no EOL exists" */ + if (isempty(support_end)) { /* An empty string is a explicit way to say "no EOL exists" */ + if (ret_eol) + *ret_eol = USEC_INFINITY; + return false; /* no end date defined */ + } struct tm tm = {}; const char *k = strptime(support_end, "%Y-%m-%d", &tm); if (!k || *k) return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL), - "Failed to parse SUPPORT_END= in os-release file, ignoring: %m"); + "Failed to parse SUPPORT_END= from os-release file, ignoring: %s", support_end); - time_t eol = timegm(&tm); - if (eol == (time_t) -1) - return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL), - "Failed to convert SUPPORT_END= in os-release file, ignoring: %m"); + usec_t eol; + r = mktime_or_timegm_usec(&tm, /* utc= */ true, &eol); + if (r < 0) + return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING, r, + "Failed to convert SUPPORT_END= time from os-release file, ignoring: %m"); if (ret_eol) - *ret_eol = eol * USEC_PER_SEC; + *ret_eol = eol; - return DIV_ROUND_UP(now(CLOCK_REALTIME), USEC_PER_SEC) > (usec_t) eol; + return now(CLOCK_REALTIME) > eol; } const char* os_release_pretty_name(const char *pretty_name, const char *name) { diff --git a/src/basic/time-util.c b/src/basic/time-util.c index ed6fdc3e49..ac2b078225 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -332,7 +332,6 @@ char* format_timestamp_style( struct tm tm; bool utc, us; - time_t sec; size_t n; assert(buf); @@ -375,9 +374,7 @@ char* format_timestamp_style( return strcpy(buf, xxx[style]); } - sec = (time_t) (t / USEC_PER_SEC); /* Round down */ - - if (!localtime_or_gmtime_r(&sec, &tm, utc)) + if (localtime_or_gmtime_usec(t, utc, &tm) < 0) return NULL; /* Start with the week day */ @@ -665,7 +662,6 @@ static int parse_timestamp_impl( unsigned fractional = 0; const char *k; struct tm tm, copy; - time_t sec; /* Allowed syntaxes: * @@ -778,10 +774,9 @@ static int parse_timestamp_impl( } } - sec = (time_t) (usec / USEC_PER_SEC); - - if (!localtime_or_gmtime_r(&sec, &tm, utc)) - return -EINVAL; + r = localtime_or_gmtime_usec(usec, utc, &tm); + if (r < 0) + return r; tm.tm_isdst = isdst; @@ -939,11 +934,11 @@ from_tm: } else minus = gmtoff * USEC_PER_SEC; - sec = mktime_or_timegm(&tm, utc); - if (sec < 0) - return -EINVAL; + r = mktime_or_timegm_usec(&tm, utc, &usec); + if (r < 0) + return r; - usec = usec_add(sec * USEC_PER_SEC, fractional); + usec = usec_add(usec, fractional); finish: usec = usec_add(usec, plus); @@ -1625,17 +1620,54 @@ int get_timezone(char **ret) { return strdup_to(ret, e); } -time_t mktime_or_timegm(struct tm *tm, bool utc) { +int mktime_or_timegm_usec( + struct tm *tm, /* input + normalized output */ + bool utc, + usec_t *ret) { + + time_t t; + assert(tm); - return utc ? timegm(tm) : mktime(tm); + if (tm->tm_year < 69) /* early check for negative (i.e. before 1970) time_t (Note that in some timezones the epoch is in the year 1969!)*/ + return -ERANGE; + if ((usec_t) tm->tm_year > CONST_MIN(USEC_INFINITY / USEC_PER_YEAR, (usec_t) TIME_T_MAX / (365U * 24U * 60U * 60U)) - 1900) /* early check for possible overrun of usec_t or time_t */ + return -ERANGE; + + /* timegm()/mktime() is a bit weird to use, since it returns -1 in two cases: on error as well as a + * valid time indicating one second before the UNIX epoch. Let's treat both cases the same here, and + * return -ERANGE for anything negative, since usec_t is unsigned, and we can thus not express + * negative times anyway. */ + + t = utc ? timegm(tm) : mktime(tm); + if (t < 0) /* Refuse negative times and errors */ + return -ERANGE; + if ((usec_t) t >= USEC_INFINITY / USEC_PER_SEC) /* Never return USEC_INFINITY by accident (or overflow) */ + return -ERANGE; + + if (ret) + *ret = (usec_t) t * USEC_PER_SEC; + return 0; } -struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) { - assert(t); - assert(tm); +int localtime_or_gmtime_usec( + usec_t t, + bool utc, + struct tm *ret) { - return utc ? gmtime_r(t, tm) : localtime_r(t, tm); + t /= USEC_PER_SEC; /* Round down */ + if (t > (usec_t) TIME_T_MAX) + return -ERANGE; + time_t sec = (time_t) t; + + struct tm buf = {}; + if (!(utc ? gmtime_r(&sec, &buf) : localtime_r(&sec, &buf))) + return -EINVAL; + + if (ret) + *ret = buf; + + return 0; } static uint32_t sysconf_clock_ticks_cached(void) { diff --git a/src/basic/time-util.h b/src/basic/time-util.h index f273770233..7d5a1b7b78 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -177,8 +177,8 @@ usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to); int get_timezone(char **ret); -time_t mktime_or_timegm(struct tm *tm, bool utc); -struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc); +int mktime_or_timegm_usec(struct tm *tm, bool utc, usec_t *ret); +int localtime_or_gmtime_usec(usec_t t, bool utc, struct tm *ret); uint32_t usec_to_jiffies(usec_t usec); usec_t jiffies_to_usec(uint32_t jiffies); |