diff options
Diffstat (limited to 'src/basic/time-util.c')
-rw-r--r-- | src/basic/time-util.c | 307 |
1 files changed, 146 insertions, 161 deletions
diff --git a/src/basic/time-util.c b/src/basic/time-util.c index fe201c398d..557c75debc 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include <ctype.h> #include <errno.h> #include <limits.h> #include <stdlib.h> @@ -19,9 +20,11 @@ #include "io-util.h" #include "log.h" #include "macro.h" +#include "missing_timerfd.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" +#include "serialize.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" @@ -278,7 +281,7 @@ static char *format_timestamp_internal( /* Let's not format times with years > 9999 */ if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) { - assert(l >= strlen("--- XXXX-XX-XX XX:XX:XX") + 1); + assert(l >= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1); strcpy(buf, "--- XXXX-XX-XX XX:XX:XX"); return buf; } @@ -528,64 +531,6 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { return buf; } -void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { - - assert(f); - assert(name); - assert(t); - - if (!dual_timestamp_is_set(t)) - return; - - fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n", - name, - t->realtime, - t->monotonic); -} - -int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { - uint64_t a, b; - int r, pos; - - assert(value); - assert(t); - - pos = strspn(value, WHITESPACE); - if (value[pos] == '-') - return -EINVAL; - pos += strspn(value + pos, DIGITS); - pos += strspn(value + pos, WHITESPACE); - if (value[pos] == '-') - return -EINVAL; - - r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos); - if (r != 2) { - log_debug("Failed to parse dual timestamp value \"%s\".", value); - return -EINVAL; - } - - if (value[pos] != '\0') - /* trailing garbage */ - return -EINVAL; - - t->realtime = a; - t->monotonic = b; - - return 0; -} - -int timestamp_deserialize(const char *value, usec_t *timestamp) { - int r; - - assert(value); - - r = safe_atou64(value, timestamp); - if (r < 0) - return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value); - - return r; -} - static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { static const struct { const char *name; @@ -904,7 +849,7 @@ int parse_timestamp(const char *t, usec_t *usec) { * Otherwise just cut it off. */ with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]); - /* Cut off the timezone if we dont need it. */ + /* Cut off the timezone if we don't need it. */ if (with_tz) t = strndupa(t, last_space - t); @@ -923,7 +868,7 @@ int parse_timestamp(const char *t, usec_t *usec) { return tmp.return_value; } -static char* extract_multiplier(char *p, usec_t *multiplier) { +static const char* extract_multiplier(const char *p, usec_t *multiplier) { static const struct { const char *suffix; usec_t usec; @@ -996,10 +941,9 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) { } for (;;) { - long long l, z = 0; - char *e; - unsigned n = 0; usec_t multiplier = default_unit, k; + long long l; + char *e; p += strspn(p, WHITESPACE); @@ -1010,6 +954,9 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) { break; } + if (*p == '-') /* Don't allow "-0" */ + return -ERANGE; + errno = 0; l = strtoll(p, &e, 10); if (errno > 0) @@ -1018,35 +965,47 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) { return -ERANGE; if (*e == '.') { - char *b = e + 1; - - errno = 0; - z = strtoll(b, &e, 10); - if (errno > 0) - return -errno; + p = e + 1; + p += strspn(p, DIGITS); + } else if (e == p) + return -EINVAL; + else + p = e; - if (z < 0) - return -ERANGE; + s = extract_multiplier(p + strspn(p, WHITESPACE), &multiplier); + if (s == p && *s != '\0') + /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/ + return -EINVAL; - if (e == b) - return -EINVAL; + p = s; - n = e - b; + if ((usec_t) l >= USEC_INFINITY / multiplier) + return -ERANGE; - } else if (e == p) - return -EINVAL; + k = (usec_t) l * multiplier; + if (k >= USEC_INFINITY - r) + return -ERANGE; - e += strspn(e, WHITESPACE); - p = extract_multiplier(e, &multiplier); + r += k; something = true; - k = (usec_t) z * multiplier; + if (*e == '.') { + usec_t m = multiplier / 10; + const char *b; + + for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) { + k = (usec_t) (*b - '0') * m; + if (k >= USEC_INFINITY - r) + return -ERANGE; - for (; n > 0; n--) - k /= 10; + r += k; + } - r += (usec_t) l * multiplier + k; + /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/ + if (b == e + 1) + return -EINVAL; + } } *usec = r; @@ -1058,58 +1017,75 @@ int parse_sec(const char *t, usec_t *usec) { return parse_time(t, usec, USEC_PER_SEC); } -int parse_sec_fix_0(const char *t, usec_t *usec) { - assert(t); - assert(usec); +int parse_sec_fix_0(const char *t, usec_t *ret) { + usec_t k; + int r; - t += strspn(t, WHITESPACE); + assert(t); + assert(ret); - if (streq(t, "0")) { - *usec = USEC_INFINITY; - return 0; - } + r = parse_sec(t, &k); + if (r < 0) + return r; - return parse_sec(t, usec); + *ret = k == 0 ? USEC_INFINITY : k; + return r; } -int parse_nsec(const char *t, nsec_t *nsec) { +static const char* extract_nsec_multiplier(const char *p, nsec_t *multiplier) { static const struct { const char *suffix; nsec_t nsec; } table[] = { - { "seconds", NSEC_PER_SEC }, - { "second", NSEC_PER_SEC }, - { "sec", NSEC_PER_SEC }, - { "s", NSEC_PER_SEC }, + { "seconds", NSEC_PER_SEC }, + { "second", NSEC_PER_SEC }, + { "sec", NSEC_PER_SEC }, + { "s", NSEC_PER_SEC }, { "minutes", NSEC_PER_MINUTE }, - { "minute", NSEC_PER_MINUTE }, - { "min", NSEC_PER_MINUTE }, - { "months", NSEC_PER_MONTH }, - { "month", NSEC_PER_MONTH }, - { "msec", NSEC_PER_MSEC }, - { "ms", NSEC_PER_MSEC }, - { "m", NSEC_PER_MINUTE }, - { "hours", NSEC_PER_HOUR }, - { "hour", NSEC_PER_HOUR }, - { "hr", NSEC_PER_HOUR }, - { "h", NSEC_PER_HOUR }, - { "days", NSEC_PER_DAY }, - { "day", NSEC_PER_DAY }, - { "d", NSEC_PER_DAY }, - { "weeks", NSEC_PER_WEEK }, - { "week", NSEC_PER_WEEK }, - { "w", NSEC_PER_WEEK }, - { "years", NSEC_PER_YEAR }, - { "year", NSEC_PER_YEAR }, - { "y", NSEC_PER_YEAR }, - { "usec", NSEC_PER_USEC }, - { "us", NSEC_PER_USEC }, - { "µs", NSEC_PER_USEC }, - { "nsec", 1ULL }, - { "ns", 1ULL }, - { "", 1ULL }, /* default is nsec */ + { "minute", NSEC_PER_MINUTE }, + { "min", NSEC_PER_MINUTE }, + { "months", NSEC_PER_MONTH }, + { "month", NSEC_PER_MONTH }, + { "M", NSEC_PER_MONTH }, + { "msec", NSEC_PER_MSEC }, + { "ms", NSEC_PER_MSEC }, + { "m", NSEC_PER_MINUTE }, + { "hours", NSEC_PER_HOUR }, + { "hour", NSEC_PER_HOUR }, + { "hr", NSEC_PER_HOUR }, + { "h", NSEC_PER_HOUR }, + { "days", NSEC_PER_DAY }, + { "day", NSEC_PER_DAY }, + { "d", NSEC_PER_DAY }, + { "weeks", NSEC_PER_WEEK }, + { "week", NSEC_PER_WEEK }, + { "w", NSEC_PER_WEEK }, + { "years", NSEC_PER_YEAR }, + { "year", NSEC_PER_YEAR }, + { "y", NSEC_PER_YEAR }, + { "usec", NSEC_PER_USEC }, + { "us", NSEC_PER_USEC }, + { "µs", NSEC_PER_USEC }, + { "nsec", 1ULL }, + { "ns", 1ULL }, + { "", 1ULL }, /* default is nsec */ }; + size_t i; + + for (i = 0; i < ELEMENTSOF(table); i++) { + char *e; + e = startswith(p, table[i].suffix); + if (e) { + *multiplier = table[i].nsec; + return e; + } + } + + return p; +} + +int parse_nsec(const char *t, nsec_t *nsec) { const char *p, *s; nsec_t r = 0; bool something = false; @@ -1131,8 +1107,8 @@ int parse_nsec(const char *t, nsec_t *nsec) { } for (;;) { - long long l, z = 0; - size_t n = 0, i; + nsec_t multiplier = 1, k; + long long l; char *e; p += strspn(p, WHITESPACE); @@ -1144,53 +1120,58 @@ int parse_nsec(const char *t, nsec_t *nsec) { break; } + if (*p == '-') /* Don't allow "-0" */ + return -ERANGE; + errno = 0; l = strtoll(p, &e, 10); - if (errno > 0) return -errno; - if (l < 0) return -ERANGE; if (*e == '.') { - char *b = e + 1; - - errno = 0; - z = strtoll(b, &e, 10); - if (errno > 0) - return -errno; + p = e + 1; + p += strspn(p, DIGITS); + } else if (e == p) + return -EINVAL; + else + p = e; - if (z < 0) - return -ERANGE; + s = extract_nsec_multiplier(p + strspn(p, WHITESPACE), &multiplier); + if (s == p && *s != '\0') + /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/ + return -EINVAL; - if (e == b) - return -EINVAL; + p = s; - n = e - b; + if ((nsec_t) l >= NSEC_INFINITY / multiplier) + return -ERANGE; - } else if (e == p) - return -EINVAL; + k = (nsec_t) l * multiplier; + if (k >= NSEC_INFINITY - r) + return -ERANGE; - e += strspn(e, WHITESPACE); + r += k; - for (i = 0; i < ELEMENTSOF(table); i++) - if (startswith(e, table[i].suffix)) { - nsec_t k = (nsec_t) z * table[i].nsec; + something = true; - for (; n > 0; n--) - k /= 10; + if (*e == '.') { + nsec_t m = multiplier / 10; + const char *b; - r += (nsec_t) l * table[i].nsec + k; - p = e + strlen(table[i].suffix); + for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) { + k = (nsec_t) (*b - '0') * m; + if (k >= NSEC_INFINITY - r) + return -ERANGE; - something = true; - break; + r += k; } - if (i >= ELEMENTSOF(table)) - return -EINVAL; - + /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/ + if (b == e + 1) + return -EINVAL; + } } *nsec = r; @@ -1214,10 +1195,11 @@ int get_timezones(char ***ret) { _cleanup_fclose_ FILE *f = NULL; _cleanup_strv_free_ char **zones = NULL; size_t n_zones = 0, n_allocated = 0; + int r; assert(ret); - zones = strv_new("UTC", NULL); + zones = strv_new("UTC"); if (!zones) return -ENOMEM; @@ -1226,13 +1208,18 @@ int get_timezones(char ***ret) { f = fopen("/usr/share/zoneinfo/zone.tab", "re"); if (f) { - char l[LINE_MAX]; - - FOREACH_LINE(l, f, return -errno) { + for (;;) { + _cleanup_free_ char *line = NULL; char *p, *w; size_t k; - p = strstrip(l); + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) + break; + + p = strstrip(line); if (isempty(p) || *p == '#') continue; @@ -1398,9 +1385,7 @@ int get_timezone(char **tz) { if (r < 0) return r; /* returns EINVAL if not a symlink */ - e = path_startswith(t, "/usr/share/zoneinfo/"); - if (!e) - e = path_startswith(t, "../usr/share/zoneinfo/"); + e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/"); if (!e) return -EINVAL; |