about summary refs log tree commit diff
path: root/src/libnm-systemd-shared/src/basic/time-util.c
diff options
context:
space:
mode:
authorJeremy Bicha <jeremy.bicha@canonical.com>2022-02-03 09:24:19 -0500
committerJeremy Bicha <jeremy.bicha@canonical.com>2022-02-03 12:01:26 -0500
commit240934659cd8326a14324c73d4f0f88eecfddcab (patch)
treee07cde48aa0a143bebf10ca7103cc0e69febad25 /src/libnm-systemd-shared/src/basic/time-util.c
parentfbe61ddadf4a89800a8e3651ae91099259ddf2ad (diff)
parent1628eda029ba4dede8d4d45572c6b60367882436 (diff)
Merge tag 'debian/1.34.0-2' into ubuntu/master
network-manager Debian release 1.34.0-2
Diffstat (limited to 'src/libnm-systemd-shared/src/basic/time-util.c')
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.c281
1 files changed, 171 insertions, 110 deletions
diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c
index 4b7042f7..21e7b24a 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.c
+++ b/src/libnm-systemd-shared/src/basic/time-util.c
@@ -432,48 +432,66 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
                 s = "left";
         }
 
-        if (d >= USEC_PER_YEAR)
-                snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
-                         d / USEC_PER_YEAR,
-                         (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
-        else if (d >= USEC_PER_MONTH)
-                snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
-                         d / USEC_PER_MONTH,
-                         (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
-        else if (d >= USEC_PER_WEEK)
-                snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
-                         d / USEC_PER_WEEK,
-                         (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
-        else if (d >= 2*USEC_PER_DAY)
-                snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
+        if (d >= USEC_PER_YEAR) {
+                usec_t years = d / USEC_PER_YEAR;
+                usec_t months = (d % USEC_PER_YEAR) / USEC_PER_MONTH;
+
+                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+                                years,
+                                years == 1 ? "year" : "years",
+                                months,
+                                months == 1 ? "month" : "months",
+                                s);
+        } else if (d >= USEC_PER_MONTH) {
+                usec_t months = d / USEC_PER_MONTH;
+                usec_t days = (d % USEC_PER_MONTH) / USEC_PER_DAY;
+
+                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+                                months,
+                                months == 1 ? "month" : "months",
+                                days,
+                                days == 1 ? "day" : "days",
+                                s);
+        } else if (d >= USEC_PER_WEEK) {
+                usec_t weeks = d / USEC_PER_WEEK;
+                usec_t days = (d % USEC_PER_WEEK) / USEC_PER_DAY;
+
+                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+                                weeks,
+                                weeks == 1 ? "week" : "weeks",
+                                days,
+                                days == 1 ? "day" : "days",
+                                s);
+        } else if (d >= 2*USEC_PER_DAY)
+                (void) snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
         else if (d >= 25*USEC_PER_HOUR)
-                snprintf(buf, l, "1 day " USEC_FMT "h %s",
-                         (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
+                (void) snprintf(buf, l, "1 day " USEC_FMT "h %s",
+                                (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
         else if (d >= 6*USEC_PER_HOUR)
-                snprintf(buf, l, USEC_FMT "h %s",
-                         d / USEC_PER_HOUR, s);
+                (void) snprintf(buf, l, USEC_FMT "h %s",
+                                d / USEC_PER_HOUR, s);
         else if (d >= USEC_PER_HOUR)
-                snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
-                         d / USEC_PER_HOUR,
-                         (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
+                (void) snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
+                                d / USEC_PER_HOUR,
+                                (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
         else if (d >= 5*USEC_PER_MINUTE)
-                snprintf(buf, l, USEC_FMT "min %s",
-                         d / USEC_PER_MINUTE, s);
+                (void) snprintf(buf, l, USEC_FMT "min %s",
+                                d / USEC_PER_MINUTE, s);
         else if (d >= USEC_PER_MINUTE)
-                snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
-                         d / USEC_PER_MINUTE,
-                         (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
+                (void) snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
+                                d / USEC_PER_MINUTE,
+                                (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
         else if (d >= USEC_PER_SEC)
-                snprintf(buf, l, USEC_FMT "s %s",
-                         d / USEC_PER_SEC, s);
+                (void) snprintf(buf, l, USEC_FMT "s %s",
+                                d / USEC_PER_SEC, s);
         else if (d >= USEC_PER_MSEC)
-                snprintf(buf, l, USEC_FMT "ms %s",
-                         d / USEC_PER_MSEC, s);
+                (void) snprintf(buf, l, USEC_FMT "ms %s",
+                                d / USEC_PER_MSEC, s);
         else if (d > 0)
-                snprintf(buf, l, USEC_FMT"us %s",
-                         d, s);
+                (void) snprintf(buf, l, USEC_FMT"us %s",
+                                d, s);
         else
-                snprintf(buf, l, "now");
+                (void) snprintf(buf, l, "now");
 
         buf[l-1] = 0;
         return buf;
@@ -539,14 +557,12 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
 
                 /* Let's see if we should shows this in dot notation */
                 if (t < USEC_PER_MINUTE && b > 0) {
-                        usec_t cc;
-                        signed char j;
+                        signed char j = 0;
 
-                        j = 0;
-                        for (cc = table[i].usec; cc > 1; cc /= 10)
+                        for (usec_t cc = table[i].usec; cc > 1; cc /= 10)
                                 j++;
 
-                        for (cc = accuracy; cc > 1; cc /= 10) {
+                        for (usec_t cc = accuracy; cc > 1; cc /= 10) {
                                 b /= 10;
                                 j--;
                         }
@@ -1035,7 +1051,7 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
 
                 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'*/
+                        /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
                         return -EINVAL;
 
                 p = s;
@@ -1063,7 +1079,7 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
                                 r += k;
                         }
 
-                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
+                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
                         if (b == e + 1)
                                 return -EINVAL;
                 }
@@ -1210,7 +1226,7 @@ int parse_nsec(const char *t, nsec_t *nsec) {
 
                 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'*/
+                        /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
                         return -EINVAL;
 
                 p = s;
@@ -1238,7 +1254,7 @@ int parse_nsec(const char *t, nsec_t *nsec) {
                                 r += k;
                         }
 
-                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
+                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
                         if (b == e + 1)
                                 return -EINVAL;
                 }
@@ -1249,75 +1265,127 @@ int parse_nsec(const char *t, nsec_t *nsec) {
         return 0;
 }
 
-int get_timezones(char ***ret) {
+static int get_timezones_from_zone1970_tab(char ***ret) {
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_strv_free_ char **zones = NULL;
-        size_t n_zones = 0;
         int r;
 
         assert(ret);
 
-        zones = strv_new("UTC");
-        if (!zones)
-                return -ENOMEM;
+        f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
+        if (!f)
+                return -errno;
 
-        n_zones = 1;
+        for (;;) {
+                _cleanup_free_ char *line = NULL, *cc = NULL, *co = NULL, *tz = NULL;
 
-        f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
-        if (f) {
-                for (;;) {
-                        _cleanup_free_ char *line = NULL, *w = NULL;
-                        char *p;
-                        size_t k;
+                r = read_line(f, LONG_LINE_MAX, &line);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
 
-                        r = read_line(f, LONG_LINE_MAX, &line);
-                        if (r < 0)
-                                return r;
-                        if (r == 0)
-                                break;
+                const char *p = line;
+
+                /* Line format is:
+                 * 'country codes' 'coordinates' 'timezone' 'comments' */
+                r = extract_many_words(&p, NULL, 0, &cc, &co, &tz, NULL);
+                if (r < 0)
+                        continue;
 
-                        p = strstrip(line);
+                /* Lines that start with # are comments. */
+                if (*cc == '#')
+                        continue;
+
+                r = strv_extend(&zones, tz);
+                if (r < 0)
+                        return r;
+        }
 
-                        if (isempty(p) || *p == '#')
-                                continue;
+        *ret = TAKE_PTR(zones);
+        return 0;
+}
 
-                        /* Skip over country code */
-                        p += strcspn(p, WHITESPACE);
-                        p += strspn(p, WHITESPACE);
+static int get_timezones_from_tzdata_zi(char ***ret) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_strv_free_ char **zones = NULL;
+        int r;
 
-                        /* Skip over coordinates */
-                        p += strcspn(p, WHITESPACE);
-                        p += strspn(p, WHITESPACE);
+        f = fopen("/usr/share/zoneinfo/tzdata.zi", "re");
+        if (!f)
+                return -errno;
 
-                        /* Found timezone name */
-                        k = strcspn(p, WHITESPACE);
-                        if (k <= 0)
-                                continue;
+        for (;;) {
+                _cleanup_free_ char *line = NULL, *type = NULL, *f1 = NULL, *f2 = NULL;
 
-                        w = strndup(p, k);
-                        if (!w)
-                                return -ENOMEM;
+                r = read_line(f, LONG_LINE_MAX, &line);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
 
-                        if (!GREEDY_REALLOC(zones, n_zones + 2))
-                                return -ENOMEM;
+                const char *p = line;
 
-                        zones[n_zones++] = TAKE_PTR(w);
-                        zones[n_zones] = NULL;
-                }
+                /* The only lines we care about are Zone and Link lines.
+                 * Zone line format is:
+                 * 'Zone' 'timezone' ...
+                 * Link line format is:
+                 * 'Link' 'target' 'alias'
+                 * See 'man zic' for more detail. */
+                r = extract_many_words(&p, NULL, 0, &type, &f1, &f2, NULL);
+                if (r < 0)
+                        continue;
 
-                strv_sort(zones);
-                strv_uniq(zones);
+                char *tz;
+                if (IN_SET(*type, 'Z', 'z'))
+                        /* Zone lines have timezone in field 1. */
+                        tz = f1;
+                else if (IN_SET(*type, 'L', 'l'))
+                        /* Link lines have timezone in field 2. */
+                        tz = f2;
+                else
+                        /* Not a line we care about. */
+                        continue;
 
-        } else if (errno != ENOENT)
-                return -errno;
+                r = strv_extend(&zones, tz);
+                if (r < 0)
+                        return r;
+        }
 
         *ret = TAKE_PTR(zones);
+        return 0;
+}
+
+int get_timezones(char ***ret) {
+        _cleanup_strv_free_ char **zones = NULL;
+        int r;
+
+        assert(ret);
+
+        r = get_timezones_from_tzdata_zi(&zones);
+        if (r == -ENOENT) {
+                log_debug_errno(r, "Could not get timezone data from tzdata.zi, using zone1970.tab: %m");
+                r = get_timezones_from_zone1970_tab(&zones);
+                if (r == -ENOENT)
+                        log_debug_errno(r, "Could not get timezone data from zone1970.tab, using UTC: %m");
+        }
+        if (r < 0 && r != -ENOENT)
+                return r;
 
+        /* Always include UTC */
+        r = strv_extend(&zones, "UTC");
+        if (r < 0)
+                return -ENOMEM;
+
+        strv_sort(zones);
+        strv_uniq(zones);
+
+        *ret = TAKE_PTR(zones);
         return 0;
 }
 #endif /* NM_IGNORED */
 
-bool timezone_is_valid(const char *name, int log_level) {
+int verify_timezone(const char *name, int log_level) {
         bool slash = false;
         const char *p, *t;
         _cleanup_close_ int fd = -1;
@@ -1325,26 +1393,26 @@ bool timezone_is_valid(const char *name, int log_level) {
         int r;
 
         if (isempty(name))
-                return false;
+                return -EINVAL;
 
         /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
         if (streq(name, "UTC"))
-                return true;
+                return 0;
 
         if (name[0] == '/')
-                return false;
+                return -EINVAL;
 
         for (p = name; *p; p++) {
                 if (!(*p >= '0' && *p <= '9') &&
                     !(*p >= 'a' && *p <= 'z') &&
                     !(*p >= 'A' && *p <= 'Z') &&
                     !IN_SET(*p, '-', '_', '+', '/'))
-                        return false;
+                        return -EINVAL;
 
                 if (*p == '/') {
 
                         if (slash)
-                                return false;
+                                return -EINVAL;
 
                         slash = true;
                 } else
@@ -1352,38 +1420,31 @@ bool timezone_is_valid(const char *name, int log_level) {
         }
 
         if (slash)
-                return false;
+                return -EINVAL;
 
         if (p - name >= PATH_MAX)
-                return false;
+                return -ENAMETOOLONG;
 
         t = strjoina("/usr/share/zoneinfo/", name);
 
         fd = open(t, O_RDONLY|O_CLOEXEC);
-        if (fd < 0) {
-                log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
-                return false;
-        }
+        if (fd < 0)
+                return log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
 
         r = fd_verify_regular(fd);
-        if (r < 0) {
-                log_full_errno(log_level, r, "Timezone file '%s' is not  a regular file: %m", t);
-                return false;
-        }
+        if (r < 0)
+                return log_full_errno(log_level, r, "Timezone file '%s' is not  a regular file: %m", t);
 
         r = loop_read_exact(fd, buf, 4, false);
-        if (r < 0) {
-                log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
-                return false;
-        }
+        if (r < 0)
+                return log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
 
         /* Magic from tzfile(5) */
-        if (memcmp(buf, "TZif", 4) != 0) {
-                log_full(log_level, "Timezone file '%s' has wrong magic bytes", t);
-                return false;
-        }
+        if (memcmp(buf, "TZif", 4) != 0)
+                return log_full_errno(log_level, SYNTHETIC_ERRNO(EBADMSG),
+                                      "Timezone file '%s' has wrong magic bytes", t);
 
-        return true;
+        return 0;
 }
 
 bool clock_boottime_supported(void) {
@@ -1445,7 +1506,7 @@ int get_timezone(char **ret) {
 
         r = readlink_malloc("/etc/localtime", &t);
         if (r == -ENOENT) {
-                /* If the symlink does not exist, assume "UTC", like glibc does*/
+                /* If the symlink does not exist, assume "UTC", like glibc does */
                 z = strdup("UTC");
                 if (!z)
                         return -ENOMEM;