]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/basic/time-util.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / basic / time-util.c
index 9ac739b42adbcef607eab9304c592de7982aba51..25a5c116e89827bf2e2b62cbb4ad6ba14f6359d9 100644 (file)
@@ -20,6 +20,7 @@
 #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"
@@ -279,7 +280,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;
         }
@@ -529,64 +530,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;
@@ -924,7 +867,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;
@@ -998,8 +941,7 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
 
         for (;;) {
                 usec_t multiplier = default_unit, k;
-                long long l, z = 0;
-                unsigned n = 0;
+                long long l;
                 char *e;
 
                 p += strspn(p, WHITESPACE);
@@ -1022,37 +964,47 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
                         return -ERANGE;
 
                 if (*e == '.') {
-                        char *b = e + 1;
+                        p = e + 1;
+                        p += strspn(p, DIGITS);
+                } else if (e == p)
+                        return -EINVAL;
+                else
+                        p = e;
 
-                        /* Don't allow "0.-0", "3.+1" or "3. 1" */
-                        if (*b == '-' || *b == '+' || isspace(*b))
-                                return -EINVAL;
+                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;
 
-                        errno = 0;
-                        z = strtoll(b, &e, 10);
-                        if (errno > 0)
-                                return -errno;
-                        if (z < 0)
-                                return -ERANGE;
-                        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;
@@ -1064,58 +1016,84 @@ 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) {
+int parse_sec_fix_0(const char *t, usec_t *ret) {
+        usec_t k;
+        int r;
+
         assert(t);
-        assert(usec);
+        assert(ret);
 
-        t += strspn(t, WHITESPACE);
+        r = parse_sec(t, &k);
+        if (r < 0)
+                return r;
 
-        if (streq(t, "0")) {
-                *usec = USEC_INFINITY;
+        *ret = k == 0 ? USEC_INFINITY : k;
+        return r;
+}
+
+int parse_sec_def_infinity(const char *t, usec_t *ret) {
+        t += strspn(t, WHITESPACE);
+        if (isempty(t)) {
+                *ret = USEC_INFINITY;
                 return 0;
         }
-
-        return parse_sec(t, usec);
+        return parse_sec(t, ret);
 }
 
-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;
@@ -1137,8 +1115,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);
@@ -1150,7 +1128,7 @@ int parse_nsec(const char *t, nsec_t *nsec) {
                         break;
                 }
 
-                if (*p == '-')
+                if (*p == '-') /* Don't allow "-0" */
                         return -ERANGE;
 
                 errno = 0;
@@ -1161,44 +1139,47 @@ int parse_nsec(const char *t, nsec_t *nsec) {
                         return -ERANGE;
 
                 if (*e == '.') {
-                        char *b = e + 1;
+                        p = e + 1;
+                        p += strspn(p, DIGITS);
+                } else if (e == p)
+                        return -EINVAL;
+                else
+                        p = e;
 
-                        if (*b == '-' || *b == '+' || isspace(*b))
-                                return -EINVAL;
+                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;
 
-                        errno = 0;
-                        z = strtoll(b, &e, 10);
-                        if (errno > 0)
-                                return -errno;
-                        if (z < 0)
-                                return -ERANGE;
-                        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;
@@ -1222,10 +1203,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;
 
@@ -1234,13 +1216,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;
@@ -1406,9 +1393,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;