summaryrefslogtreecommitdiff
path: root/src/basic/time-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic/time-util.c')
-rw-r--r--src/basic/time-util.c307
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;