| File: | build-scan/../src/basic/time-util.c |
| Warning: | line 751, column 29 The left operand of '!=' is a garbage value due to array index out of bounds |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | ||||||||
| 2 | |||||||||
| 3 | #include <errno(*__errno_location ()).h> | ||||||||
| 4 | #include <limits.h> | ||||||||
| 5 | #include <stdlib.h> | ||||||||
| 6 | #include <string.h> | ||||||||
| 7 | #include <sys/mman.h> | ||||||||
| 8 | #include <sys/stat.h> | ||||||||
| 9 | #include <sys/time.h> | ||||||||
| 10 | #include <sys/timerfd.h> | ||||||||
| 11 | #include <sys/timex.h> | ||||||||
| 12 | #include <sys/types.h> | ||||||||
| 13 | #include <unistd.h> | ||||||||
| 14 | |||||||||
| 15 | #include "alloc-util.h" | ||||||||
| 16 | #include "fd-util.h" | ||||||||
| 17 | #include "fileio.h" | ||||||||
| 18 | #include "fs-util.h" | ||||||||
| 19 | #include "io-util.h" | ||||||||
| 20 | #include "log.h" | ||||||||
| 21 | #include "macro.h" | ||||||||
| 22 | #include "parse-util.h" | ||||||||
| 23 | #include "path-util.h" | ||||||||
| 24 | #include "process-util.h" | ||||||||
| 25 | #include "stat-util.h" | ||||||||
| 26 | #include "string-util.h" | ||||||||
| 27 | #include "strv.h" | ||||||||
| 28 | #include "time-util.h" | ||||||||
| 29 | |||||||||
| 30 | static clockid_t map_clock_id(clockid_t c) { | ||||||||
| 31 | |||||||||
| 32 | /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will | ||||||||
| 33 | * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is | ||||||||
| 34 | * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on | ||||||||
| 35 | * those archs. */ | ||||||||
| 36 | |||||||||
| 37 | switch (c) { | ||||||||
| 38 | |||||||||
| 39 | case CLOCK_BOOTTIME_ALARM9: | ||||||||
| 40 | return CLOCK_BOOTTIME7; | ||||||||
| 41 | |||||||||
| 42 | case CLOCK_REALTIME_ALARM8: | ||||||||
| 43 | return CLOCK_REALTIME0; | ||||||||
| 44 | |||||||||
| 45 | default: | ||||||||
| 46 | return c; | ||||||||
| 47 | } | ||||||||
| 48 | } | ||||||||
| 49 | |||||||||
| 50 | usec_t now(clockid_t clock_id) { | ||||||||
| 51 | struct timespec ts; | ||||||||
| 52 | |||||||||
| 53 | assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0)do { if ((__builtin_expect(!!(!(clock_gettime(map_clock_id(clock_id ), &ts) == 0)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD , ("clock_gettime(map_clock_id(clock_id), &ts) == 0"), "../src/basic/time-util.c" , 53, __PRETTY_FUNCTION__); } while (0); | ||||||||
| 54 | |||||||||
| 55 | return timespec_load(&ts); | ||||||||
| 56 | } | ||||||||
| 57 | |||||||||
| 58 | nsec_t now_nsec(clockid_t clock_id) { | ||||||||
| 59 | struct timespec ts; | ||||||||
| 60 | |||||||||
| 61 | assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0)do { if ((__builtin_expect(!!(!(clock_gettime(map_clock_id(clock_id ), &ts) == 0)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD , ("clock_gettime(map_clock_id(clock_id), &ts) == 0"), "../src/basic/time-util.c" , 61, __PRETTY_FUNCTION__); } while (0); | ||||||||
| 62 | |||||||||
| 63 | return timespec_load_nsec(&ts); | ||||||||
| 64 | } | ||||||||
| 65 | |||||||||
| 66 | dual_timestamp* dual_timestamp_get(dual_timestamp *ts) { | ||||||||
| 67 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 67, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 68 | |||||||||
| 69 | ts->realtime = now(CLOCK_REALTIME0); | ||||||||
| 70 | ts->monotonic = now(CLOCK_MONOTONIC1); | ||||||||
| 71 | |||||||||
| 72 | return ts; | ||||||||
| 73 | } | ||||||||
| 74 | |||||||||
| 75 | triple_timestamp* triple_timestamp_get(triple_timestamp *ts) { | ||||||||
| 76 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 76, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 77 | |||||||||
| 78 | ts->realtime = now(CLOCK_REALTIME0); | ||||||||
| 79 | ts->monotonic = now(CLOCK_MONOTONIC1); | ||||||||
| 80 | ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME7) : USEC_INFINITY((usec_t) -1); | ||||||||
| 81 | |||||||||
| 82 | return ts; | ||||||||
| 83 | } | ||||||||
| 84 | |||||||||
| 85 | dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { | ||||||||
| 86 | int64_t delta; | ||||||||
| 87 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 87, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 88 | |||||||||
| 89 | if (u == USEC_INFINITY((usec_t) -1) || u <= 0) { | ||||||||
| 90 | ts->realtime = ts->monotonic = u; | ||||||||
| 91 | return ts; | ||||||||
| 92 | } | ||||||||
| 93 | |||||||||
| 94 | ts->realtime = u; | ||||||||
| 95 | |||||||||
| 96 | delta = (int64_t) now(CLOCK_REALTIME0) - (int64_t) u; | ||||||||
| 97 | ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC1), delta); | ||||||||
| 98 | |||||||||
| 99 | return ts; | ||||||||
| 100 | } | ||||||||
| 101 | |||||||||
| 102 | triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) { | ||||||||
| 103 | int64_t delta; | ||||||||
| 104 | |||||||||
| 105 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 105, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 106 | |||||||||
| 107 | if (u == USEC_INFINITY((usec_t) -1) || u <= 0) { | ||||||||
| 108 | ts->realtime = ts->monotonic = ts->boottime = u; | ||||||||
| 109 | return ts; | ||||||||
| 110 | } | ||||||||
| 111 | |||||||||
| 112 | ts->realtime = u; | ||||||||
| 113 | delta = (int64_t) now(CLOCK_REALTIME0) - (int64_t) u; | ||||||||
| 114 | ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC1), delta); | ||||||||
| 115 | ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME7), delta) : USEC_INFINITY((usec_t) -1); | ||||||||
| 116 | |||||||||
| 117 | return ts; | ||||||||
| 118 | } | ||||||||
| 119 | |||||||||
| 120 | dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { | ||||||||
| 121 | int64_t delta; | ||||||||
| 122 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 122, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 123 | |||||||||
| 124 | if (u == USEC_INFINITY((usec_t) -1)) { | ||||||||
| 125 | ts->realtime = ts->monotonic = USEC_INFINITY((usec_t) -1); | ||||||||
| 126 | return ts; | ||||||||
| 127 | } | ||||||||
| 128 | |||||||||
| 129 | ts->monotonic = u; | ||||||||
| 130 | delta = (int64_t) now(CLOCK_MONOTONIC1) - (int64_t) u; | ||||||||
| 131 | ts->realtime = usec_sub_signed(now(CLOCK_REALTIME0), delta); | ||||||||
| 132 | |||||||||
| 133 | return ts; | ||||||||
| 134 | } | ||||||||
| 135 | |||||||||
| 136 | dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { | ||||||||
| 137 | int64_t delta; | ||||||||
| 138 | |||||||||
| 139 | if (u == USEC_INFINITY((usec_t) -1)) { | ||||||||
| 140 | ts->realtime = ts->monotonic = USEC_INFINITY((usec_t) -1); | ||||||||
| 141 | return ts; | ||||||||
| 142 | } | ||||||||
| 143 | |||||||||
| 144 | dual_timestamp_get(ts); | ||||||||
| 145 | delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; | ||||||||
| 146 | ts->realtime = usec_sub_signed(ts->realtime, delta); | ||||||||
| 147 | ts->monotonic = usec_sub_signed(ts->monotonic, delta); | ||||||||
| 148 | |||||||||
| 149 | return ts; | ||||||||
| 150 | } | ||||||||
| 151 | |||||||||
| 152 | usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) { | ||||||||
| 153 | |||||||||
| 154 | switch (clock) { | ||||||||
| 155 | |||||||||
| 156 | case CLOCK_REALTIME0: | ||||||||
| 157 | case CLOCK_REALTIME_ALARM8: | ||||||||
| 158 | return ts->realtime; | ||||||||
| 159 | |||||||||
| 160 | case CLOCK_MONOTONIC1: | ||||||||
| 161 | return ts->monotonic; | ||||||||
| 162 | |||||||||
| 163 | case CLOCK_BOOTTIME7: | ||||||||
| 164 | case CLOCK_BOOTTIME_ALARM9: | ||||||||
| 165 | return ts->boottime; | ||||||||
| 166 | |||||||||
| 167 | default: | ||||||||
| 168 | return USEC_INFINITY((usec_t) -1); | ||||||||
| 169 | } | ||||||||
| 170 | } | ||||||||
| 171 | |||||||||
| 172 | usec_t timespec_load(const struct timespec *ts) { | ||||||||
| 173 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 173, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 174 | |||||||||
| 175 | if (ts->tv_sec < 0 || ts->tv_nsec < 0) | ||||||||
| 176 | return USEC_INFINITY((usec_t) -1); | ||||||||
| 177 | |||||||||
| 178 | if ((usec_t) ts->tv_sec > (UINT64_MAX(18446744073709551615UL) - (ts->tv_nsec / NSEC_PER_USEC((nsec_t) 1000ULL))) / USEC_PER_SEC((usec_t) 1000000ULL)) | ||||||||
| 179 | return USEC_INFINITY((usec_t) -1); | ||||||||
| 180 | |||||||||
| 181 | return | ||||||||
| 182 | (usec_t) ts->tv_sec * USEC_PER_SEC((usec_t) 1000000ULL) + | ||||||||
| 183 | (usec_t) ts->tv_nsec / NSEC_PER_USEC((nsec_t) 1000ULL); | ||||||||
| 184 | } | ||||||||
| 185 | |||||||||
| 186 | nsec_t timespec_load_nsec(const struct timespec *ts) { | ||||||||
| 187 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 187, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 188 | |||||||||
| 189 | if (ts->tv_sec < 0 || ts->tv_nsec < 0) | ||||||||
| 190 | return NSEC_INFINITY((nsec_t) -1); | ||||||||
| 191 | |||||||||
| 192 | if ((nsec_t) ts->tv_sec >= (UINT64_MAX(18446744073709551615UL) - ts->tv_nsec) / NSEC_PER_SEC((nsec_t) 1000000000ULL)) | ||||||||
| 193 | return NSEC_INFINITY((nsec_t) -1); | ||||||||
| 194 | |||||||||
| 195 | return (nsec_t) ts->tv_sec * NSEC_PER_SEC((nsec_t) 1000000000ULL) + (nsec_t) ts->tv_nsec; | ||||||||
| 196 | } | ||||||||
| 197 | |||||||||
| 198 | struct timespec *timespec_store(struct timespec *ts, usec_t u) { | ||||||||
| 199 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 199, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 200 | |||||||||
| 201 | if (u == USEC_INFINITY((usec_t) -1) || | ||||||||
| 202 | u / USEC_PER_SEC((usec_t) 1000000ULL) >= TIME_T_MAX(time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1 )) { | ||||||||
| 203 | ts->tv_sec = (time_t) -1; | ||||||||
| 204 | ts->tv_nsec = (long) -1; | ||||||||
| 205 | return ts; | ||||||||
| 206 | } | ||||||||
| 207 | |||||||||
| 208 | ts->tv_sec = (time_t) (u / USEC_PER_SEC((usec_t) 1000000ULL)); | ||||||||
| 209 | ts->tv_nsec = (long int) ((u % USEC_PER_SEC((usec_t) 1000000ULL)) * NSEC_PER_USEC((nsec_t) 1000ULL)); | ||||||||
| 210 | |||||||||
| 211 | return ts; | ||||||||
| 212 | } | ||||||||
| 213 | |||||||||
| 214 | usec_t timeval_load(const struct timeval *tv) { | ||||||||
| 215 | assert(tv)do { if ((__builtin_expect(!!(!(tv)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("tv"), "../src/basic/time-util.c", 215, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 216 | |||||||||
| 217 | if (tv->tv_sec < 0 || tv->tv_usec < 0) | ||||||||
| 218 | return USEC_INFINITY((usec_t) -1); | ||||||||
| 219 | |||||||||
| 220 | if ((usec_t) tv->tv_sec > (UINT64_MAX(18446744073709551615UL) - tv->tv_usec) / USEC_PER_SEC((usec_t) 1000000ULL)) | ||||||||
| 221 | return USEC_INFINITY((usec_t) -1); | ||||||||
| 222 | |||||||||
| 223 | return | ||||||||
| 224 | (usec_t) tv->tv_sec * USEC_PER_SEC((usec_t) 1000000ULL) + | ||||||||
| 225 | (usec_t) tv->tv_usec; | ||||||||
| 226 | } | ||||||||
| 227 | |||||||||
| 228 | struct timeval *timeval_store(struct timeval *tv, usec_t u) { | ||||||||
| 229 | assert(tv)do { if ((__builtin_expect(!!(!(tv)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("tv"), "../src/basic/time-util.c", 229, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 230 | |||||||||
| 231 | if (u == USEC_INFINITY((usec_t) -1) || | ||||||||
| 232 | u / USEC_PER_SEC((usec_t) 1000000ULL) > TIME_T_MAX(time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1 )) { | ||||||||
| 233 | tv->tv_sec = (time_t) -1; | ||||||||
| 234 | tv->tv_usec = (suseconds_t) -1; | ||||||||
| 235 | } else { | ||||||||
| 236 | tv->tv_sec = (time_t) (u / USEC_PER_SEC((usec_t) 1000000ULL)); | ||||||||
| 237 | tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC((usec_t) 1000000ULL)); | ||||||||
| 238 | } | ||||||||
| 239 | |||||||||
| 240 | return tv; | ||||||||
| 241 | } | ||||||||
| 242 | |||||||||
| 243 | static char *format_timestamp_internal( | ||||||||
| 244 | char *buf, | ||||||||
| 245 | size_t l, | ||||||||
| 246 | usec_t t, | ||||||||
| 247 | bool_Bool utc, | ||||||||
| 248 | bool_Bool us) { | ||||||||
| 249 | |||||||||
| 250 | /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our | ||||||||
| 251 | * generated timestamps may be parsed with parse_timestamp(), and always read the same. */ | ||||||||
| 252 | static const char * const weekdays[] = { | ||||||||
| 253 | [0] = "Sun", | ||||||||
| 254 | [1] = "Mon", | ||||||||
| 255 | [2] = "Tue", | ||||||||
| 256 | [3] = "Wed", | ||||||||
| 257 | [4] = "Thu", | ||||||||
| 258 | [5] = "Fri", | ||||||||
| 259 | [6] = "Sat", | ||||||||
| 260 | }; | ||||||||
| 261 | |||||||||
| 262 | struct tm tm; | ||||||||
| 263 | time_t sec; | ||||||||
| 264 | size_t n; | ||||||||
| 265 | |||||||||
| 266 | assert(buf)do { if ((__builtin_expect(!!(!(buf)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("buf"), "../src/basic/time-util.c", 266, __PRETTY_FUNCTION__); } while (0); | ||||||||
| 267 | |||||||||
| 268 | if (l < | ||||||||
| 269 | 3 + /* week day */ | ||||||||
| 270 | 1 + 10 + /* space and date */ | ||||||||
| 271 | 1 + 8 + /* space and time */ | ||||||||
| 272 | (us ? 1 + 6 : 0) + /* "." and microsecond part */ | ||||||||
| 273 | 1 + 1 + /* space and shortest possible zone */ | ||||||||
| 274 | 1) | ||||||||
| 275 | return NULL((void*)0); /* Not enough space even for the shortest form. */ | ||||||||
| 276 | if (t <= 0 || t == USEC_INFINITY((usec_t) -1)) | ||||||||
| 277 | return NULL((void*)0); /* Timestamp is unset */ | ||||||||
| 278 | |||||||||
| 279 | /* Let's not format times with years > 9999 */ | ||||||||
| 280 | if (t > USEC_TIMESTAMP_FORMATTABLE_MAX((usec_t) 253402214399000000)) { | ||||||||
| 281 | assert(l >= strlen("--- XXXX-XX-XX XX:XX:XX") + 1)do { if ((__builtin_expect(!!(!(l >= strlen("--- XXXX-XX-XX XX:XX:XX" ) + 1)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("l >= strlen(\"--- XXXX-XX-XX XX:XX:XX\") + 1" ), "../src/basic/time-util.c", 281, __PRETTY_FUNCTION__); } while (0); | ||||||||
| 282 | strcpy(buf, "--- XXXX-XX-XX XX:XX:XX"); | ||||||||
| 283 | return buf; | ||||||||
| 284 | } | ||||||||
| 285 | |||||||||
| 286 | sec = (time_t) (t / USEC_PER_SEC((usec_t) 1000000ULL)); /* Round down */ | ||||||||
| 287 | |||||||||
| 288 | if (!localtime_or_gmtime_r(&sec, &tm, utc)) | ||||||||
| 289 | return NULL((void*)0); | ||||||||
| 290 | |||||||||
| 291 | /* Start with the week day */ | ||||||||
| 292 | assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays))do { if ((__builtin_expect(!!(!((size_t) tm.tm_wday < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (weekdays), typeof(&*(weekdays))), sizeof(weekdays)/sizeof ((weekdays)[0]), ((void)0))))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD , ("(size_t) tm.tm_wday < ELEMENTSOF(weekdays)"), "../src/basic/time-util.c" , 292, __PRETTY_FUNCTION__); } while (0); | ||||||||
| 293 | memcpy(buf, weekdays[tm.tm_wday], 4); | ||||||||
| 294 | |||||||||
| 295 | /* Add the main components */ | ||||||||
| 296 | if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0) | ||||||||
| 297 | return NULL((void*)0); /* Doesn't fit */ | ||||||||
| 298 | |||||||||
| 299 | /* Append the microseconds part, if that's requested */ | ||||||||
| 300 | if (us) { | ||||||||
| 301 | n = strlen(buf); | ||||||||
| 302 | if (n + 8 > l) | ||||||||
| 303 | return NULL((void*)0); /* Microseconds part doesn't fit. */ | ||||||||
| 304 | |||||||||
| 305 | sprintf(buf + n, ".%06"PRI_USEC"l" "u", t % USEC_PER_SEC((usec_t) 1000000ULL)); | ||||||||
| 306 | } | ||||||||
| 307 | |||||||||
| 308 | /* Append the timezone */ | ||||||||
| 309 | n = strlen(buf); | ||||||||
| 310 | if (utc) { | ||||||||
| 311 | /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the | ||||||||
| 312 | * obsolete "GMT" instead. */ | ||||||||
| 313 | if (n + 5 > l) | ||||||||
| 314 | return NULL((void*)0); /* "UTC" doesn't fit. */ | ||||||||
| 315 | |||||||||
| 316 | strcpy(buf + n, " UTC"); | ||||||||
| 317 | |||||||||
| 318 | } else if (!isempty(tm.tm_zone)) { | ||||||||
| 319 | size_t tn; | ||||||||
| 320 | |||||||||
| 321 | /* An explicit timezone is specified, let's use it, if it fits */ | ||||||||
| 322 | tn = strlen(tm.tm_zone); | ||||||||
| 323 | if (n + 1 + tn + 1 > l) { | ||||||||
| 324 | /* The full time zone does not fit in. Yuck. */ | ||||||||
| 325 | |||||||||
| 326 | if (n + 1 + _POSIX_TZNAME_MAX6 + 1 > l) | ||||||||
| 327 | return NULL((void*)0); /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */ | ||||||||
| 328 | |||||||||
| 329 | /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX | ||||||||
| 330 | * minimum time zone length. In this case suppress the timezone entirely, in order not to dump | ||||||||
| 331 | * an overly long, hard to read string on the user. This should be safe, because the user will | ||||||||
| 332 | * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */ | ||||||||
| 333 | } else { | ||||||||
| 334 | buf[n++] = ' '; | ||||||||
| 335 | strcpy(buf + n, tm.tm_zone); | ||||||||
| 336 | } | ||||||||
| 337 | } | ||||||||
| 338 | |||||||||
| 339 | return buf; | ||||||||
| 340 | } | ||||||||
| 341 | |||||||||
| 342 | char *format_timestamp(char *buf, size_t l, usec_t t) { | ||||||||
| 343 | return format_timestamp_internal(buf, l, t, false0, false0); | ||||||||
| 344 | } | ||||||||
| 345 | |||||||||
| 346 | char *format_timestamp_utc(char *buf, size_t l, usec_t t) { | ||||||||
| 347 | return format_timestamp_internal(buf, l, t, true1, false0); | ||||||||
| 348 | } | ||||||||
| 349 | |||||||||
| 350 | char *format_timestamp_us(char *buf, size_t l, usec_t t) { | ||||||||
| 351 | return format_timestamp_internal(buf, l, t, false0, true1); | ||||||||
| 352 | } | ||||||||
| 353 | |||||||||
| 354 | char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) { | ||||||||
| 355 | return format_timestamp_internal(buf, l, t, true1, true1); | ||||||||
| 356 | } | ||||||||
| 357 | |||||||||
| 358 | char *format_timestamp_relative(char *buf, size_t l, usec_t t) { | ||||||||
| 359 | const char *s; | ||||||||
| 360 | usec_t n, d; | ||||||||
| 361 | |||||||||
| 362 | if (t <= 0 || t == USEC_INFINITY((usec_t) -1)) | ||||||||
| 363 | return NULL((void*)0); | ||||||||
| 364 | |||||||||
| 365 | n = now(CLOCK_REALTIME0); | ||||||||
| 366 | if (n > t) { | ||||||||
| 367 | d = n - t; | ||||||||
| 368 | s = "ago"; | ||||||||
| 369 | } else { | ||||||||
| 370 | d = t - n; | ||||||||
| 371 | s = "left"; | ||||||||
| 372 | } | ||||||||
| 373 | |||||||||
| 374 | if (d >= USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL)))) | ||||||||
| 375 | snprintf(buf, l, USEC_FMT"%" "l" "u" " years " USEC_FMT"%" "l" "u" " months %s", | ||||||||
| 376 | d / USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL))), | ||||||||
| 377 | (d % USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL)))) / USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL))), s); | ||||||||
| 378 | else if (d >= USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL)))) | ||||||||
| 379 | snprintf(buf, l, USEC_FMT"%" "l" "u" " months " USEC_FMT"%" "l" "u" " days %s", | ||||||||
| 380 | d / USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL))), | ||||||||
| 381 | (d % USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL)))) / USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))), s); | ||||||||
| 382 | else if (d >= USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL)))))))))) | ||||||||
| 383 | snprintf(buf, l, USEC_FMT"%" "l" "u" " weeks " USEC_FMT"%" "l" "u" " days %s", | ||||||||
| 384 | d / USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL))))))))), | ||||||||
| 385 | (d % USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL)))))))))) / USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))), s); | ||||||||
| 386 | else if (d >= 2*USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL )))))))) | ||||||||
| 387 | snprintf(buf, l, USEC_FMT"%" "l" "u" " days %s", d / USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))), s); | ||||||||
| 388 | else if (d >= 25*USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL)))))) | ||||||||
| 389 | snprintf(buf, l, "1 day " USEC_FMT"%" "l" "u" "h %s", | ||||||||
| 390 | (d - USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL )))))))) / USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))), s); | ||||||||
| 391 | else if (d >= 6*USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL)))))) | ||||||||
| 392 | snprintf(buf, l, USEC_FMT"%" "l" "u" "h %s", | ||||||||
| 393 | d / USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))), s); | ||||||||
| 394 | else if (d >= USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL)))))) | ||||||||
| 395 | snprintf(buf, l, USEC_FMT"%" "l" "u" "h " USEC_FMT"%" "l" "u" "min %s", | ||||||||
| 396 | d / USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))), | ||||||||
| 397 | (d % USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL)))))) / USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))), s); | ||||||||
| 398 | else if (d >= 5*USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL)))) | ||||||||
| 399 | snprintf(buf, l, USEC_FMT"%" "l" "u" "min %s", | ||||||||
| 400 | d / USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))), s); | ||||||||
| 401 | else if (d >= USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL)))) | ||||||||
| 402 | snprintf(buf, l, USEC_FMT"%" "l" "u" "min " USEC_FMT"%" "l" "u" "s %s", | ||||||||
| 403 | d / USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))), | ||||||||
| 404 | (d % USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL)))) / USEC_PER_SEC((usec_t) 1000000ULL), s); | ||||||||
| 405 | else if (d >= USEC_PER_SEC((usec_t) 1000000ULL)) | ||||||||
| 406 | snprintf(buf, l, USEC_FMT"%" "l" "u" "s %s", | ||||||||
| 407 | d / USEC_PER_SEC((usec_t) 1000000ULL), s); | ||||||||
| 408 | else if (d >= USEC_PER_MSEC((usec_t) 1000ULL)) | ||||||||
| 409 | snprintf(buf, l, USEC_FMT"%" "l" "u" "ms %s", | ||||||||
| 410 | d / USEC_PER_MSEC((usec_t) 1000ULL), s); | ||||||||
| 411 | else if (d > 0) | ||||||||
| 412 | snprintf(buf, l, USEC_FMT"%" "l" "u""us %s", | ||||||||
| 413 | d, s); | ||||||||
| 414 | else | ||||||||
| 415 | snprintf(buf, l, "now"); | ||||||||
| 416 | |||||||||
| 417 | buf[l-1] = 0; | ||||||||
| 418 | return buf; | ||||||||
| 419 | } | ||||||||
| 420 | |||||||||
| 421 | char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { | ||||||||
| 422 | static const struct { | ||||||||
| 423 | const char *suffix; | ||||||||
| 424 | usec_t usec; | ||||||||
| 425 | } table[] = { | ||||||||
| 426 | { "y", USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL))) }, | ||||||||
| 427 | { "month", USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL))) }, | ||||||||
| 428 | { "w", USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL))))))))) }, | ||||||||
| 429 | { "d", USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))) }, | ||||||||
| 430 | { "h", USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))) }, | ||||||||
| 431 | { "min", USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))) }, | ||||||||
| 432 | { "s", USEC_PER_SEC((usec_t) 1000000ULL) }, | ||||||||
| 433 | { "ms", USEC_PER_MSEC((usec_t) 1000ULL) }, | ||||||||
| 434 | { "us", 1 }, | ||||||||
| 435 | }; | ||||||||
| 436 | |||||||||
| 437 | size_t i; | ||||||||
| 438 | char *p = buf; | ||||||||
| 439 | bool_Bool something = false0; | ||||||||
| 440 | |||||||||
| 441 | assert(buf)do { if ((__builtin_expect(!!(!(buf)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("buf"), "../src/basic/time-util.c", 441, __PRETTY_FUNCTION__); } while (0); | ||||||||
| 442 | assert(l > 0)do { if ((__builtin_expect(!!(!(l > 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("l > 0"), "../src/basic/time-util.c", 442, __PRETTY_FUNCTION__); } while (0); | ||||||||
| 443 | |||||||||
| 444 | if (t == USEC_INFINITY((usec_t) -1)) { | ||||||||
| 445 | strncpy(p, "infinity", l-1); | ||||||||
| 446 | p[l-1] = 0; | ||||||||
| 447 | return p; | ||||||||
| 448 | } | ||||||||
| 449 | |||||||||
| 450 | if (t <= 0) { | ||||||||
| 451 | strncpy(p, "0", l-1); | ||||||||
| 452 | p[l-1] = 0; | ||||||||
| 453 | return p; | ||||||||
| 454 | } | ||||||||
| 455 | |||||||||
| 456 | /* The result of this function can be parsed with parse_sec */ | ||||||||
| 457 | |||||||||
| 458 | for (i = 0; i < ELEMENTSOF(table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(table), typeof(&*(table))), sizeof(table)/sizeof( (table)[0]), ((void)0))); i++) { | ||||||||
| 459 | int k = 0; | ||||||||
| 460 | size_t n; | ||||||||
| 461 | bool_Bool done = false0; | ||||||||
| 462 | usec_t a, b; | ||||||||
| 463 | |||||||||
| 464 | if (t <= 0) | ||||||||
| 465 | break; | ||||||||
| 466 | |||||||||
| 467 | if (t < accuracy && something) | ||||||||
| 468 | break; | ||||||||
| 469 | |||||||||
| 470 | if (t < table[i].usec) | ||||||||
| 471 | continue; | ||||||||
| 472 | |||||||||
| 473 | if (l <= 1) | ||||||||
| 474 | break; | ||||||||
| 475 | |||||||||
| 476 | a = t / table[i].usec; | ||||||||
| 477 | b = t % table[i].usec; | ||||||||
| 478 | |||||||||
| 479 | /* Let's see if we should shows this in dot notation */ | ||||||||
| 480 | if (t < USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))) && b > 0) { | ||||||||
| 481 | usec_t cc; | ||||||||
| 482 | signed char j; | ||||||||
| 483 | |||||||||
| 484 | j = 0; | ||||||||
| 485 | for (cc = table[i].usec; cc > 1; cc /= 10) | ||||||||
| 486 | j++; | ||||||||
| 487 | |||||||||
| 488 | for (cc = accuracy; cc > 1; cc /= 10) { | ||||||||
| 489 | b /= 10; | ||||||||
| 490 | j--; | ||||||||
| 491 | } | ||||||||
| 492 | |||||||||
| 493 | if (j > 0) { | ||||||||
| 494 | k = snprintf(p, l, | ||||||||
| 495 | "%s"USEC_FMT"%" "l" "u"".%0*"PRI_USEC"l" "u""%s", | ||||||||
| 496 | p > buf ? " " : "", | ||||||||
| 497 | a, | ||||||||
| 498 | j, | ||||||||
| 499 | b, | ||||||||
| 500 | table[i].suffix); | ||||||||
| 501 | |||||||||
| 502 | t = 0; | ||||||||
| 503 | done = true1; | ||||||||
| 504 | } | ||||||||
| 505 | } | ||||||||
| 506 | |||||||||
| 507 | /* No? Then let's show it normally */ | ||||||||
| 508 | if (!done) { | ||||||||
| 509 | k = snprintf(p, l, | ||||||||
| 510 | "%s"USEC_FMT"%" "l" "u""%s", | ||||||||
| 511 | p > buf ? " " : "", | ||||||||
| 512 | a, | ||||||||
| 513 | table[i].suffix); | ||||||||
| 514 | |||||||||
| 515 | t = b; | ||||||||
| 516 | } | ||||||||
| 517 | |||||||||
| 518 | n = MIN((size_t) k, l)__extension__ ({ const typeof(((size_t) k)) __unique_prefix_A16 = (((size_t) k)); const typeof((l)) __unique_prefix_B17 = (( l)); __unique_prefix_A16 < __unique_prefix_B17 ? __unique_prefix_A16 : __unique_prefix_B17; }); | ||||||||
| 519 | |||||||||
| 520 | l -= n; | ||||||||
| 521 | p += n; | ||||||||
| 522 | |||||||||
| 523 | something = true1; | ||||||||
| 524 | } | ||||||||
| 525 | |||||||||
| 526 | *p = 0; | ||||||||
| 527 | |||||||||
| 528 | return buf; | ||||||||
| 529 | } | ||||||||
| 530 | |||||||||
| 531 | void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { | ||||||||
| 532 | |||||||||
| 533 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/basic/time-util.c", 533, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 534 | assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name"), "../src/basic/time-util.c", 534 , __PRETTY_FUNCTION__); } while (0); | ||||||||
| 535 | assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t"), "../src/basic/time-util.c", 535, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 536 | |||||||||
| 537 | if (!dual_timestamp_is_set(t)) | ||||||||
| 538 | return; | ||||||||
| 539 | |||||||||
| 540 | fprintf(f, "%s="USEC_FMT"%" "l" "u"" "USEC_FMT"%" "l" "u""\n", | ||||||||
| 541 | name, | ||||||||
| 542 | t->realtime, | ||||||||
| 543 | t->monotonic); | ||||||||
| 544 | } | ||||||||
| 545 | |||||||||
| 546 | int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { | ||||||||
| 547 | uint64_t a, b; | ||||||||
| 548 | int r, pos; | ||||||||
| 549 | |||||||||
| 550 | assert(value)do { if ((__builtin_expect(!!(!(value)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("value"), "../src/basic/time-util.c", 550 , __PRETTY_FUNCTION__); } while (0); | ||||||||
| 551 | assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t"), "../src/basic/time-util.c", 551, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 552 | |||||||||
| 553 | pos = strspn(value, WHITESPACE" \t\n\r"); | ||||||||
| 554 | if (value[pos] == '-') | ||||||||
| 555 | return -EINVAL22; | ||||||||
| 556 | pos += strspn(value + pos, DIGITS"0123456789"); | ||||||||
| 557 | pos += strspn(value + pos, WHITESPACE" \t\n\r"); | ||||||||
| 558 | if (value[pos] == '-') | ||||||||
| 559 | return -EINVAL22; | ||||||||
| 560 | |||||||||
| 561 | r = sscanf(value, "%" PRIu64"l" "u" "%" PRIu64"l" "u" "%n", &a, &b, &pos); | ||||||||
| 562 | if (r != 2) { | ||||||||
| 563 | log_debug("Failed to parse dual timestamp value \"%s\".", value)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/time-util.c", 563, __func__, "Failed to parse dual timestamp value \"%s\"." , value) : -abs(_e); }); | ||||||||
| 564 | return -EINVAL22; | ||||||||
| 565 | } | ||||||||
| 566 | |||||||||
| 567 | if (value[pos] != '\0') | ||||||||
| 568 | /* trailing garbage */ | ||||||||
| 569 | return -EINVAL22; | ||||||||
| 570 | |||||||||
| 571 | t->realtime = a; | ||||||||
| 572 | t->monotonic = b; | ||||||||
| 573 | |||||||||
| 574 | return 0; | ||||||||
| 575 | } | ||||||||
| 576 | |||||||||
| 577 | int timestamp_deserialize(const char *value, usec_t *timestamp) { | ||||||||
| 578 | int r; | ||||||||
| 579 | |||||||||
| 580 | assert(value)do { if ((__builtin_expect(!!(!(value)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("value"), "../src/basic/time-util.c", 580 , __PRETTY_FUNCTION__); } while (0); | ||||||||
| 581 | |||||||||
| 582 | r = safe_atou64(value, timestamp); | ||||||||
| 583 | if (r < 0) | ||||||||
| 584 | return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/time-util.c", 584, __func__, "Failed to parse timestamp value \"%s\": %m" , value) : -abs(_e); }); | ||||||||
| 585 | |||||||||
| 586 | return r; | ||||||||
| 587 | } | ||||||||
| 588 | |||||||||
| 589 | static int parse_timestamp_impl(const char *t, usec_t *usec, bool_Bool with_tz) { | ||||||||
| 590 | static const struct { | ||||||||
| 591 | const char *name; | ||||||||
| 592 | const int nr; | ||||||||
| 593 | } day_nr[] = { | ||||||||
| 594 | { "Sunday", 0 }, | ||||||||
| 595 | { "Sun", 0 }, | ||||||||
| 596 | { "Monday", 1 }, | ||||||||
| 597 | { "Mon", 1 }, | ||||||||
| 598 | { "Tuesday", 2 }, | ||||||||
| 599 | { "Tue", 2 }, | ||||||||
| 600 | { "Wednesday", 3 }, | ||||||||
| 601 | { "Wed", 3 }, | ||||||||
| 602 | { "Thursday", 4 }, | ||||||||
| 603 | { "Thu", 4 }, | ||||||||
| 604 | { "Friday", 5 }, | ||||||||
| 605 | { "Fri", 5 }, | ||||||||
| 606 | { "Saturday", 6 }, | ||||||||
| 607 | { "Sat", 6 }, | ||||||||
| 608 | }; | ||||||||
| 609 | |||||||||
| 610 | const char *k, *utc = NULL((void*)0), *tzn = NULL((void*)0); | ||||||||
| 611 | struct tm tm, copy; | ||||||||
| 612 | time_t x; | ||||||||
| 613 | usec_t x_usec, plus = 0, minus = 0, ret; | ||||||||
| 614 | int r, weekday = -1, dst = -1; | ||||||||
| 615 | size_t i; | ||||||||
| 616 | |||||||||
| 617 | /* Allowed syntaxes: | ||||||||
| 618 | * | ||||||||
| 619 | * 2012-09-22 16:34:22 | ||||||||
| 620 | * 2012-09-22 16:34 (seconds will be set to 0) | ||||||||
| 621 | * 2012-09-22 (time will be set to 00:00:00) | ||||||||
| 622 | * 16:34:22 (date will be set to today) | ||||||||
| 623 | * 16:34 (date will be set to today, seconds to 0) | ||||||||
| 624 | * now | ||||||||
| 625 | * yesterday (time is set to 00:00:00) | ||||||||
| 626 | * today (time is set to 00:00:00) | ||||||||
| 627 | * tomorrow (time is set to 00:00:00) | ||||||||
| 628 | * +5min | ||||||||
| 629 | * -5days | ||||||||
| 630 | * @2147483647 (seconds since epoch) | ||||||||
| 631 | */ | ||||||||
| 632 | |||||||||
| 633 | assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t"), "../src/basic/time-util.c", 633, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| |||||||||
| 634 | assert(usec)do { if ((__builtin_expect(!!(!(usec)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("usec"), "../src/basic/time-util.c", 634 , __PRETTY_FUNCTION__); } while (0); | ||||||||
| 635 | |||||||||
| 636 | if (t[0] == '@' && !with_tz) | ||||||||
| 637 | return parse_sec(t + 1, usec); | ||||||||
| 638 | |||||||||
| 639 | ret = now(CLOCK_REALTIME0); | ||||||||
| 640 | |||||||||
| 641 | if (!with_tz) { | ||||||||
| 642 | if (streq(t, "now")(strcmp((t),("now")) == 0)) | ||||||||
| 643 | goto finish; | ||||||||
| 644 | |||||||||
| 645 | else if (t[0] == '+') { | ||||||||
| 646 | r = parse_sec(t+1, &plus); | ||||||||
| 647 | if (r < 0) | ||||||||
| 648 | return r; | ||||||||
| 649 | |||||||||
| 650 | goto finish; | ||||||||
| 651 | |||||||||
| 652 | } else if (t[0] == '-') { | ||||||||
| 653 | r = parse_sec(t+1, &minus); | ||||||||
| 654 | if (r < 0) | ||||||||
| 655 | return r; | ||||||||
| 656 | |||||||||
| 657 | goto finish; | ||||||||
| 658 | |||||||||
| 659 | } else if ((k = endswith(t, " ago"))) { | ||||||||
| 660 | t = strndupa(t, k - t)(__extension__ ({ const char *__old = (t); size_t __len = strnlen (__old, (k - t)); char *__new = (char *) __builtin_alloca (__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old, __len ); })); | ||||||||
| 661 | |||||||||
| 662 | r = parse_sec(t, &minus); | ||||||||
| 663 | if (r < 0) | ||||||||
| 664 | return r; | ||||||||
| 665 | |||||||||
| 666 | goto finish; | ||||||||
| 667 | |||||||||
| 668 | } else if ((k = endswith(t, " left"))) { | ||||||||
| 669 | t = strndupa(t, k - t)(__extension__ ({ const char *__old = (t); size_t __len = strnlen (__old, (k - t)); char *__new = (char *) __builtin_alloca (__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old, __len ); })); | ||||||||
| 670 | |||||||||
| 671 | r = parse_sec(t, &plus); | ||||||||
| 672 | if (r < 0) | ||||||||
| 673 | return r; | ||||||||
| 674 | |||||||||
| 675 | goto finish; | ||||||||
| 676 | } | ||||||||
| 677 | |||||||||
| 678 | /* See if the timestamp is suffixed with UTC */ | ||||||||
| 679 | utc = endswith_no_case(t, " UTC"); | ||||||||
| 680 | if (utc) | ||||||||
| 681 | t = strndupa(t, utc - t)(__extension__ ({ const char *__old = (t); size_t __len = strnlen (__old, (utc - t)); char *__new = (char *) __builtin_alloca ( __len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old , __len); })); | ||||||||
| 682 | else { | ||||||||
| 683 | const char *e = NULL((void*)0); | ||||||||
| 684 | int j; | ||||||||
| 685 | |||||||||
| 686 | tzset(); | ||||||||
| 687 | |||||||||
| 688 | /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only | ||||||||
| 689 | * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because | ||||||||
| 690 | * there are no nice APIs available to cover this. By accepting the local time zone strings, we make | ||||||||
| 691 | * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't | ||||||||
| 692 | * support arbitrary timezone specifications. */ | ||||||||
| 693 | |||||||||
| 694 | for (j = 0; j <= 1; j++) { | ||||||||
| 695 | |||||||||
| 696 | if (isempty(tzname[j])) | ||||||||
| 697 | continue; | ||||||||
| 698 | |||||||||
| 699 | e = endswith_no_case(t, tzname[j]); | ||||||||
| 700 | if (!e) | ||||||||
| 701 | continue; | ||||||||
| 702 | if (e == t) | ||||||||
| 703 | continue; | ||||||||
| 704 | if (e[-1] != ' ') | ||||||||
| 705 | continue; | ||||||||
| 706 | |||||||||
| 707 | break; | ||||||||
| 708 | } | ||||||||
| 709 | |||||||||
| 710 | if (IN_SET(j, 0, 1)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){0, 1})/sizeof(int)]; switch(j) { case 0: case 1: _found = 1; break; default: break; } _found; })) { | ||||||||
| 711 | /* Found one of the two timezones specified. */ | ||||||||
| 712 | t = strndupa(t, e - t - 1)(__extension__ ({ const char *__old = (t); size_t __len = strnlen (__old, (e - t - 1)); char *__new = (char *) __builtin_alloca (__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old , __len); })); | ||||||||
| 713 | dst = j; | ||||||||
| 714 | tzn = tzname[j]; | ||||||||
| 715 | } | ||||||||
| 716 | } | ||||||||
| 717 | } | ||||||||
| 718 | |||||||||
| 719 | x = (time_t) (ret / USEC_PER_SEC((usec_t) 1000000ULL)); | ||||||||
| 720 | x_usec = 0; | ||||||||
| 721 | |||||||||
| 722 | if (!localtime_or_gmtime_r(&x, &tm, utc)) | ||||||||
| 723 | return -EINVAL22; | ||||||||
| 724 | |||||||||
| 725 | tm.tm_isdst = dst; | ||||||||
| 726 | if (!with_tz
| ||||||||
| 727 | tm.tm_zone = tzn; | ||||||||
| 728 | |||||||||
| 729 | if (streq(t, "today")(strcmp((t),("today")) == 0)) { | ||||||||
| 730 | tm.tm_sec = tm.tm_min = tm.tm_hour = 0; | ||||||||
| 731 | goto from_tm; | ||||||||
| 732 | |||||||||
| 733 | } else if (streq(t, "yesterday")(strcmp((t),("yesterday")) == 0)) { | ||||||||
| 734 | tm.tm_mday--; | ||||||||
| 735 | tm.tm_sec = tm.tm_min = tm.tm_hour = 0; | ||||||||
| 736 | goto from_tm; | ||||||||
| 737 | |||||||||
| 738 | } else if (streq(t, "tomorrow")(strcmp((t),("tomorrow")) == 0)) { | ||||||||
| 739 | tm.tm_mday++; | ||||||||
| 740 | tm.tm_sec = tm.tm_min = tm.tm_hour = 0; | ||||||||
| 741 | goto from_tm; | ||||||||
| 742 | } | ||||||||
| 743 | |||||||||
| 744 | for (i = 0; i < ELEMENTSOF(day_nr)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(day_nr), typeof(&*(day_nr))), sizeof(day_nr)/sizeof ((day_nr)[0]), ((void)0))); i++) { | ||||||||
| 745 | size_t skip; | ||||||||
| 746 | |||||||||
| 747 | if (!startswith_no_case(t, day_nr[i].name)) | ||||||||
| 748 | continue; | ||||||||
| 749 | |||||||||
| 750 | skip = strlen(day_nr[i].name); | ||||||||
| 751 | if (t[skip] != ' ') | ||||||||
| |||||||||
| 752 | continue; | ||||||||
| 753 | |||||||||
| 754 | weekday = day_nr[i].nr; | ||||||||
| 755 | t += skip + 1; | ||||||||
| 756 | break; | ||||||||
| 757 | } | ||||||||
| 758 | |||||||||
| 759 | copy = tm; | ||||||||
| 760 | k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); | ||||||||
| 761 | if (k) { | ||||||||
| 762 | if (*k == '.') | ||||||||
| 763 | goto parse_usec; | ||||||||
| 764 | else if (*k == 0) | ||||||||
| 765 | goto from_tm; | ||||||||
| 766 | } | ||||||||
| 767 | |||||||||
| 768 | tm = copy; | ||||||||
| 769 | k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm); | ||||||||
| 770 | if (k) { | ||||||||
| 771 | if (*k == '.') | ||||||||
| 772 | goto parse_usec; | ||||||||
| 773 | else if (*k == 0) | ||||||||
| 774 | goto from_tm; | ||||||||
| 775 | } | ||||||||
| 776 | |||||||||
| 777 | tm = copy; | ||||||||
| 778 | k = strptime(t, "%y-%m-%d %H:%M", &tm); | ||||||||
| 779 | if (k && *k == 0) { | ||||||||
| 780 | tm.tm_sec = 0; | ||||||||
| 781 | goto from_tm; | ||||||||
| 782 | } | ||||||||
| 783 | |||||||||
| 784 | tm = copy; | ||||||||
| 785 | k = strptime(t, "%Y-%m-%d %H:%M", &tm); | ||||||||
| 786 | if (k && *k == 0) { | ||||||||
| 787 | tm.tm_sec = 0; | ||||||||
| 788 | goto from_tm; | ||||||||
| 789 | } | ||||||||
| 790 | |||||||||
| 791 | tm = copy; | ||||||||
| 792 | k = strptime(t, "%y-%m-%d", &tm); | ||||||||
| 793 | if (k && *k == 0) { | ||||||||
| 794 | tm.tm_sec = tm.tm_min = tm.tm_hour = 0; | ||||||||
| 795 | goto from_tm; | ||||||||
| 796 | } | ||||||||
| 797 | |||||||||
| 798 | tm = copy; | ||||||||
| 799 | k = strptime(t, "%Y-%m-%d", &tm); | ||||||||
| 800 | if (k && *k == 0) { | ||||||||
| 801 | tm.tm_sec = tm.tm_min = tm.tm_hour = 0; | ||||||||
| 802 | goto from_tm; | ||||||||
| 803 | } | ||||||||
| 804 | |||||||||
| 805 | tm = copy; | ||||||||
| 806 | k = strptime(t, "%H:%M:%S", &tm); | ||||||||
| 807 | if (k) { | ||||||||
| 808 | if (*k == '.') | ||||||||
| 809 | goto parse_usec; | ||||||||
| 810 | else if (*k == 0) | ||||||||
| 811 | goto from_tm; | ||||||||
| 812 | } | ||||||||
| 813 | |||||||||
| 814 | tm = copy; | ||||||||
| 815 | k = strptime(t, "%H:%M", &tm); | ||||||||
| 816 | if (k && *k == 0) { | ||||||||
| 817 | tm.tm_sec = 0; | ||||||||
| 818 | goto from_tm; | ||||||||
| 819 | } | ||||||||
| 820 | |||||||||
| 821 | return -EINVAL22; | ||||||||
| 822 | |||||||||
| 823 | parse_usec: | ||||||||
| 824 | { | ||||||||
| 825 | unsigned add; | ||||||||
| 826 | |||||||||
| 827 | k++; | ||||||||
| 828 | r = parse_fractional_part_u(&k, 6, &add); | ||||||||
| 829 | if (r < 0) | ||||||||
| 830 | return -EINVAL22; | ||||||||
| 831 | |||||||||
| 832 | if (*k) | ||||||||
| 833 | return -EINVAL22; | ||||||||
| 834 | |||||||||
| 835 | x_usec = add; | ||||||||
| 836 | } | ||||||||
| 837 | |||||||||
| 838 | from_tm: | ||||||||
| 839 | if (weekday >= 0 && tm.tm_wday != weekday) | ||||||||
| 840 | return -EINVAL22; | ||||||||
| 841 | |||||||||
| 842 | x = mktime_or_timegm(&tm, utc); | ||||||||
| 843 | if (x < 0) | ||||||||
| 844 | return -EINVAL22; | ||||||||
| 845 | |||||||||
| 846 | ret = (usec_t) x * USEC_PER_SEC((usec_t) 1000000ULL) + x_usec; | ||||||||
| 847 | if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX((usec_t) 253402214399000000)) | ||||||||
| 848 | return -EINVAL22; | ||||||||
| 849 | |||||||||
| 850 | finish: | ||||||||
| 851 | if (ret + plus < ret) /* overflow? */ | ||||||||
| 852 | return -EINVAL22; | ||||||||
| 853 | ret += plus; | ||||||||
| 854 | if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX((usec_t) 253402214399000000)) | ||||||||
| 855 | return -EINVAL22; | ||||||||
| 856 | |||||||||
| 857 | if (ret >= minus) | ||||||||
| 858 | ret -= minus; | ||||||||
| 859 | else | ||||||||
| 860 | return -EINVAL22; | ||||||||
| 861 | |||||||||
| 862 | *usec = ret; | ||||||||
| 863 | |||||||||
| 864 | return 0; | ||||||||
| 865 | } | ||||||||
| 866 | |||||||||
| 867 | typedef struct ParseTimestampResult { | ||||||||
| 868 | usec_t usec; | ||||||||
| 869 | int return_value; | ||||||||
| 870 | } ParseTimestampResult; | ||||||||
| 871 | |||||||||
| 872 | int parse_timestamp(const char *t, usec_t *usec) { | ||||||||
| 873 | char *last_space, *tz = NULL((void*)0); | ||||||||
| 874 | ParseTimestampResult *shared, tmp; | ||||||||
| 875 | int r; | ||||||||
| 876 | |||||||||
| 877 | last_space = strrchr(t, ' '); | ||||||||
| 878 | if (last_space != NULL((void*)0) && timezone_is_valid(last_space + 1, LOG_DEBUG7)) | ||||||||
| 879 | tz = last_space + 1; | ||||||||
| 880 | |||||||||
| 881 | if (!tz || endswith_no_case(t, " UTC")) | ||||||||
| 882 | return parse_timestamp_impl(t, usec, false0); | ||||||||
| 883 | |||||||||
| 884 | shared = mmap(NULL((void*)0), sizeof *shared, PROT_READ0x1|PROT_WRITE0x2, MAP_SHARED0x01|MAP_ANONYMOUS0x20, -1, 0); | ||||||||
| 885 | if (shared == MAP_FAILED((void *) -1)) | ||||||||
| 886 | return negative_errno(); | ||||||||
| 887 | |||||||||
| 888 | r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL((void*)0)); | ||||||||
| 889 | if (r < 0) { | ||||||||
| 890 | (void) munmap(shared, sizeof *shared); | ||||||||
| 891 | return r; | ||||||||
| 892 | } | ||||||||
| 893 | if (r == 0) { | ||||||||
| 894 | bool_Bool with_tz = true1; | ||||||||
| 895 | |||||||||
| 896 | if (setenv("TZ", tz, 1) != 0) { | ||||||||
| 897 | shared->return_value = negative_errno(); | ||||||||
| 898 | _exit(EXIT_FAILURE1); | ||||||||
| 899 | } | ||||||||
| 900 | |||||||||
| 901 | tzset(); | ||||||||
| 902 | |||||||||
| 903 | /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation. | ||||||||
| 904 | * Otherwise just cut it off. */ | ||||||||
| 905 | with_tz = !STR_IN_SET(tz, tzname[0], tzname[1])(!!strv_find((((char**) ((const char*[]) { tzname[0], tzname[ 1], ((void*)0) }))), (tz))); | ||||||||
| 906 | |||||||||
| 907 | /* Cut off the timezone if we dont need it. */ | ||||||||
| 908 | if (with_tz) | ||||||||
| 909 | t = strndupa(t, last_space - t)(__extension__ ({ const char *__old = (t); size_t __len = strnlen (__old, (last_space - t)); char *__new = (char *) __builtin_alloca (__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old , __len); })); | ||||||||
| 910 | |||||||||
| 911 | shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz); | ||||||||
| 912 | |||||||||
| 913 | _exit(EXIT_SUCCESS0); | ||||||||
| 914 | } | ||||||||
| 915 | |||||||||
| 916 | tmp = *shared; | ||||||||
| 917 | if (munmap(shared, sizeof *shared) != 0) | ||||||||
| 918 | return negative_errno(); | ||||||||
| 919 | |||||||||
| 920 | if (tmp.return_value == 0) | ||||||||
| 921 | *usec = tmp.usec; | ||||||||
| 922 | |||||||||
| 923 | return tmp.return_value; | ||||||||
| 924 | } | ||||||||
| 925 | |||||||||
| 926 | static char* extract_multiplier(char *p, usec_t *multiplier) { | ||||||||
| 927 | static const struct { | ||||||||
| 928 | const char *suffix; | ||||||||
| 929 | usec_t usec; | ||||||||
| 930 | } table[] = { | ||||||||
| 931 | { "seconds", USEC_PER_SEC((usec_t) 1000000ULL) }, | ||||||||
| 932 | { "second", USEC_PER_SEC((usec_t) 1000000ULL) }, | ||||||||
| 933 | { "sec", USEC_PER_SEC((usec_t) 1000000ULL) }, | ||||||||
| 934 | { "s", USEC_PER_SEC((usec_t) 1000000ULL) }, | ||||||||
| 935 | { "minutes", USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))) }, | ||||||||
| 936 | { "minute", USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))) }, | ||||||||
| 937 | { "min", USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))) }, | ||||||||
| 938 | { "months", USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL))) }, | ||||||||
| 939 | { "month", USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL))) }, | ||||||||
| 940 | { "M", USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL))) }, | ||||||||
| 941 | { "msec", USEC_PER_MSEC((usec_t) 1000ULL) }, | ||||||||
| 942 | { "ms", USEC_PER_MSEC((usec_t) 1000ULL) }, | ||||||||
| 943 | { "m", USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))) }, | ||||||||
| 944 | { "hours", USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))) }, | ||||||||
| 945 | { "hour", USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))) }, | ||||||||
| 946 | { "hr", USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))) }, | ||||||||
| 947 | { "h", USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))) }, | ||||||||
| 948 | { "days", USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))) }, | ||||||||
| 949 | { "day", USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))) }, | ||||||||
| 950 | { "d", USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))) }, | ||||||||
| 951 | { "weeks", USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL))))))))) }, | ||||||||
| 952 | { "week", USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL))))))))) }, | ||||||||
| 953 | { "w", USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL))))))))) }, | ||||||||
| 954 | { "years", USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL))) }, | ||||||||
| 955 | { "year", USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL))) }, | ||||||||
| 956 | { "y", USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL))) }, | ||||||||
| 957 | { "usec", 1ULL }, | ||||||||
| 958 | { "us", 1ULL }, | ||||||||
| 959 | { "µs", 1ULL }, | ||||||||
| 960 | }; | ||||||||
| 961 | size_t i; | ||||||||
| 962 | |||||||||
| 963 | for (i = 0; i < ELEMENTSOF(table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(table), typeof(&*(table))), sizeof(table)/sizeof( (table)[0]), ((void)0))); i++) { | ||||||||
| 964 | char *e; | ||||||||
| 965 | |||||||||
| 966 | e = startswith(p, table[i].suffix); | ||||||||
| 967 | if (e) { | ||||||||
| 968 | *multiplier = table[i].usec; | ||||||||
| 969 | return e; | ||||||||
| 970 | } | ||||||||
| 971 | } | ||||||||
| 972 | |||||||||
| 973 | return p; | ||||||||
| 974 | } | ||||||||
| 975 | |||||||||
| 976 | int parse_time(const char *t, usec_t *usec, usec_t default_unit) { | ||||||||
| 977 | const char *p, *s; | ||||||||
| 978 | usec_t r = 0; | ||||||||
| 979 | bool_Bool something = false0; | ||||||||
| 980 | |||||||||
| 981 | assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t"), "../src/basic/time-util.c", 981, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 982 | assert(usec)do { if ((__builtin_expect(!!(!(usec)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("usec"), "../src/basic/time-util.c", 982 , __PRETTY_FUNCTION__); } while (0); | ||||||||
| 983 | assert(default_unit > 0)do { if ((__builtin_expect(!!(!(default_unit > 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("default_unit > 0"), "../src/basic/time-util.c" , 983, __PRETTY_FUNCTION__); } while (0); | ||||||||
| 984 | |||||||||
| 985 | p = t; | ||||||||
| 986 | |||||||||
| 987 | p += strspn(p, WHITESPACE" \t\n\r"); | ||||||||
| 988 | s = startswith(p, "infinity"); | ||||||||
| 989 | if (s) { | ||||||||
| 990 | s += strspn(s, WHITESPACE" \t\n\r"); | ||||||||
| 991 | if (*s != 0) | ||||||||
| 992 | return -EINVAL22; | ||||||||
| 993 | |||||||||
| 994 | *usec = USEC_INFINITY((usec_t) -1); | ||||||||
| 995 | return 0; | ||||||||
| 996 | } | ||||||||
| 997 | |||||||||
| 998 | for (;;) { | ||||||||
| 999 | long long l, z = 0; | ||||||||
| 1000 | char *e; | ||||||||
| 1001 | unsigned n = 0; | ||||||||
| 1002 | usec_t multiplier = default_unit, k; | ||||||||
| 1003 | |||||||||
| 1004 | p += strspn(p, WHITESPACE" \t\n\r"); | ||||||||
| 1005 | |||||||||
| 1006 | if (*p == 0) { | ||||||||
| 1007 | if (!something) | ||||||||
| 1008 | return -EINVAL22; | ||||||||
| 1009 | |||||||||
| 1010 | break; | ||||||||
| 1011 | } | ||||||||
| 1012 | |||||||||
| 1013 | errno(*__errno_location ()) = 0; | ||||||||
| 1014 | l = strtoll(p, &e, 10); | ||||||||
| 1015 | if (errno(*__errno_location ()) > 0) | ||||||||
| 1016 | return -errno(*__errno_location ()); | ||||||||
| 1017 | if (l < 0) | ||||||||
| 1018 | return -ERANGE34; | ||||||||
| 1019 | |||||||||
| 1020 | if (*e == '.') { | ||||||||
| 1021 | char *b = e + 1; | ||||||||
| 1022 | |||||||||
| 1023 | errno(*__errno_location ()) = 0; | ||||||||
| 1024 | z = strtoll(b, &e, 10); | ||||||||
| 1025 | if (errno(*__errno_location ()) > 0) | ||||||||
| 1026 | return -errno(*__errno_location ()); | ||||||||
| 1027 | |||||||||
| 1028 | if (z < 0) | ||||||||
| 1029 | return -ERANGE34; | ||||||||
| 1030 | |||||||||
| 1031 | if (e == b) | ||||||||
| 1032 | return -EINVAL22; | ||||||||
| 1033 | |||||||||
| 1034 | n = e - b; | ||||||||
| 1035 | |||||||||
| 1036 | } else if (e == p) | ||||||||
| 1037 | return -EINVAL22; | ||||||||
| 1038 | |||||||||
| 1039 | e += strspn(e, WHITESPACE" \t\n\r"); | ||||||||
| 1040 | p = extract_multiplier(e, &multiplier); | ||||||||
| 1041 | |||||||||
| 1042 | something = true1; | ||||||||
| 1043 | |||||||||
| 1044 | k = (usec_t) z * multiplier; | ||||||||
| 1045 | |||||||||
| 1046 | for (; n > 0; n--) | ||||||||
| 1047 | k /= 10; | ||||||||
| 1048 | |||||||||
| 1049 | r += (usec_t) l * multiplier + k; | ||||||||
| 1050 | } | ||||||||
| 1051 | |||||||||
| 1052 | *usec = r; | ||||||||
| 1053 | |||||||||
| 1054 | return 0; | ||||||||
| 1055 | } | ||||||||
| 1056 | |||||||||
| 1057 | int parse_sec(const char *t, usec_t *usec) { | ||||||||
| 1058 | return parse_time(t, usec, USEC_PER_SEC((usec_t) 1000000ULL)); | ||||||||
| 1059 | } | ||||||||
| 1060 | |||||||||
| 1061 | int parse_sec_fix_0(const char *t, usec_t *usec) { | ||||||||
| 1062 | assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t"), "../src/basic/time-util.c", 1062, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 1063 | assert(usec)do { if ((__builtin_expect(!!(!(usec)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("usec"), "../src/basic/time-util.c", 1063 , __PRETTY_FUNCTION__); } while (0); | ||||||||
| 1064 | |||||||||
| 1065 | t += strspn(t, WHITESPACE" \t\n\r"); | ||||||||
| 1066 | |||||||||
| 1067 | if (streq(t, "0")(strcmp((t),("0")) == 0)) { | ||||||||
| 1068 | *usec = USEC_INFINITY((usec_t) -1); | ||||||||
| 1069 | return 0; | ||||||||
| 1070 | } | ||||||||
| 1071 | |||||||||
| 1072 | return parse_sec(t, usec); | ||||||||
| 1073 | } | ||||||||
| 1074 | |||||||||
| 1075 | int parse_sec_def_infinity(const char *t, usec_t *ret) { | ||||||||
| 1076 | t += strspn(t, WHITESPACE" \t\n\r"); | ||||||||
| 1077 | if (isempty(t)) { | ||||||||
| 1078 | *ret = USEC_INFINITY((usec_t) -1); | ||||||||
| 1079 | return 0; | ||||||||
| 1080 | } | ||||||||
| 1081 | return parse_sec(t, ret); | ||||||||
| 1082 | } | ||||||||
| 1083 | |||||||||
| 1084 | int parse_nsec(const char *t, nsec_t *nsec) { | ||||||||
| 1085 | static const struct { | ||||||||
| 1086 | const char *suffix; | ||||||||
| 1087 | nsec_t nsec; | ||||||||
| 1088 | } table[] = { | ||||||||
| 1089 | { "seconds", NSEC_PER_SEC((nsec_t) 1000000000ULL) }, | ||||||||
| 1090 | { "second", NSEC_PER_SEC((nsec_t) 1000000000ULL) }, | ||||||||
| 1091 | { "sec", NSEC_PER_SEC((nsec_t) 1000000000ULL) }, | ||||||||
| 1092 | { "s", NSEC_PER_SEC((nsec_t) 1000000000ULL) }, | ||||||||
| 1093 | { "minutes", NSEC_PER_MINUTE((nsec_t) (60ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
| 1094 | { "minute", NSEC_PER_MINUTE((nsec_t) (60ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
| 1095 | { "min", NSEC_PER_MINUTE((nsec_t) (60ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
| 1096 | { "months", NSEC_PER_MONTH((nsec_t) (2629800ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
| 1097 | { "month", NSEC_PER_MONTH((nsec_t) (2629800ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
| 1098 | { "msec", NSEC_PER_MSEC((nsec_t) 1000000ULL) }, | ||||||||
| 1099 | { "ms", NSEC_PER_MSEC((nsec_t) 1000000ULL) }, | ||||||||
| 1100 | { "m", NSEC_PER_MINUTE((nsec_t) (60ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
| 1101 | { "hours", NSEC_PER_HOUR((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL)))) ) }, | ||||||||
| 1102 | { "hour", NSEC_PER_HOUR((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL)))) ) }, | ||||||||
| 1103 | { "hr", NSEC_PER_HOUR((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL)))) ) }, | ||||||||
| 1104 | { "h", NSEC_PER_HOUR((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL)))) ) }, | ||||||||
| 1105 | { "days", NSEC_PER_DAY((nsec_t) (24ULL*((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL ))))))) }, | ||||||||
| 1106 | { "day", NSEC_PER_DAY((nsec_t) (24ULL*((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL ))))))) }, | ||||||||
| 1107 | { "d", NSEC_PER_DAY((nsec_t) (24ULL*((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL ))))))) }, | ||||||||
| 1108 | { "weeks", NSEC_PER_WEEK((nsec_t) (7ULL*((nsec_t) (24ULL*((nsec_t) (60ULL*((nsec_t) ( 60ULL*((nsec_t) 1000000000ULL))))))))) }, | ||||||||
| 1109 | { "week", NSEC_PER_WEEK((nsec_t) (7ULL*((nsec_t) (24ULL*((nsec_t) (60ULL*((nsec_t) ( 60ULL*((nsec_t) 1000000000ULL))))))))) }, | ||||||||
| 1110 | { "w", NSEC_PER_WEEK((nsec_t) (7ULL*((nsec_t) (24ULL*((nsec_t) (60ULL*((nsec_t) ( 60ULL*((nsec_t) 1000000000ULL))))))))) }, | ||||||||
| 1111 | { "years", NSEC_PER_YEAR((nsec_t) (31557600ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
| 1112 | { "year", NSEC_PER_YEAR((nsec_t) (31557600ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
| 1113 | { "y", NSEC_PER_YEAR((nsec_t) (31557600ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
| 1114 | { "usec", NSEC_PER_USEC((nsec_t) 1000ULL) }, | ||||||||
| 1115 | { "us", NSEC_PER_USEC((nsec_t) 1000ULL) }, | ||||||||
| 1116 | { "µs", NSEC_PER_USEC((nsec_t) 1000ULL) }, | ||||||||
| 1117 | { "nsec", 1ULL }, | ||||||||
| 1118 | { "ns", 1ULL }, | ||||||||
| 1119 | { "", 1ULL }, /* default is nsec */ | ||||||||
| 1120 | }; | ||||||||
| 1121 | |||||||||
| 1122 | const char *p, *s; | ||||||||
| 1123 | nsec_t r = 0; | ||||||||
| 1124 | bool_Bool something = false0; | ||||||||
| 1125 | |||||||||
| 1126 | assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t"), "../src/basic/time-util.c", 1126, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| 1127 | assert(nsec)do { if ((__builtin_expect(!!(!(nsec)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("nsec"), "../src/basic/time-util.c", 1127 , __PRETTY_FUNCTION__); } while (0); | ||||||||
| 1128 | |||||||||
| 1129 | p = t; | ||||||||
| 1130 | |||||||||
| 1131 | p += strspn(p, WHITESPACE" \t\n\r"); | ||||||||
| 1132 | s = startswith(p, "infinity"); | ||||||||
| 1133 | if (s) { | ||||||||
| 1134 | s += strspn(s, WHITESPACE" \t\n\r"); | ||||||||
| 1135 | if (*s != 0) | ||||||||
| 1136 | return -EINVAL22; | ||||||||
| 1137 | |||||||||
| 1138 | *nsec = NSEC_INFINITY((nsec_t) -1); | ||||||||
| 1139 | return 0; | ||||||||
| 1140 | } | ||||||||
| 1141 | |||||||||
| 1142 | for (;;) { | ||||||||
| 1143 | long long l, z = 0; | ||||||||
| 1144 | size_t n = 0, i; | ||||||||
| 1145 | char *e; | ||||||||
| 1146 | |||||||||
| 1147 | p += strspn(p, WHITESPACE" \t\n\r"); | ||||||||
| 1148 | |||||||||
| 1149 | if (*p == 0) { | ||||||||
| 1150 | if (!something) | ||||||||
| 1151 | return -EINVAL22; | ||||||||
| 1152 | |||||||||
| 1153 | break; | ||||||||
| 1154 | } | ||||||||
| 1155 | |||||||||
| 1156 | errno(*__errno_location ()) = 0; | ||||||||
| 1157 | l = strtoll(p, &e, 10); | ||||||||
| 1158 | |||||||||
| 1159 | if (errno(*__errno_location ()) > 0) | ||||||||
| 1160 | return -errno(*__errno_location ()); | ||||||||
| 1161 | |||||||||
| 1162 | if (l < 0) | ||||||||
| 1163 | return -ERANGE34; | ||||||||
| 1164 | |||||||||
| 1165 | if (*e == '.') { | ||||||||
| 1166 | char *b = e + 1; | ||||||||
| 1167 | |||||||||
| 1168 | errno(*__errno_location ()) = 0; | ||||||||
| 1169 | z = strtoll(b, &e, 10); | ||||||||
| 1170 | if (errno(*__errno_location ()) > 0) | ||||||||
| 1171 | return -errno(*__errno_location ()); | ||||||||
| 1172 | |||||||||
| 1173 | if (z < 0) | ||||||||
| 1174 | return -ERANGE34; | ||||||||
| 1175 | |||||||||
| 1176 | if (e == b) | ||||||||
| 1177 | return -EINVAL22; | ||||||||
| 1178 | |||||||||
| 1179 | n = e - b; | ||||||||
| 1180 | |||||||||
| 1181 | } else if (e == p) | ||||||||
| 1182 | return -EINVAL22; | ||||||||
| 1183 | |||||||||
| 1184 | e += strspn(e, WHITESPACE" \t\n\r"); | ||||||||
| 1185 | |||||||||
| 1186 | for (i = 0; i < ELEMENTSOF(table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(table), typeof(&*(table))), sizeof(table)/sizeof( (table)[0]), ((void)0))); i++) | ||||||||
| 1187 | if (startswith(e, table[i].suffix)) { | ||||||||
| 1188 | nsec_t k = (nsec_t) z * table[i].nsec; | ||||||||
| 1189 | |||||||||
| 1190 | for (; n > 0; n--) | ||||||||
| 1191 | k /= 10; | ||||||||
| 1192 | |||||||||
| 1193 | r += (nsec_t) l * table[i].nsec + k; | ||||||||
| 1194 | p = e + strlen(table[i].suffix); | ||||||||
| 1195 | |||||||||
| 1196 | something = true1; | ||||||||
| 1197 | break; | ||||||||
| 1198 | } | ||||||||
| 1199 | |||||||||
| 1200 | if (i >= ELEMENTSOF(table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(table), typeof(&*(table))), sizeof(table)/sizeof( (table)[0]), ((void)0)))) | ||||||||
| 1201 | return -EINVAL22; | ||||||||
| 1202 | |||||||||
| 1203 | } | ||||||||
| 1204 | |||||||||
| 1205 | *nsec = r; | ||||||||
| 1206 | |||||||||
| 1207 | return 0; | ||||||||
| 1208 | } | ||||||||
| 1209 | |||||||||
| 1210 | bool_Bool ntp_synced(void) { | ||||||||
| 1211 | struct timex txc = {}; | ||||||||
| 1212 | |||||||||
| 1213 | if (adjtimex(&txc) < 0) | ||||||||
| 1214 | return false0; | ||||||||
| 1215 | |||||||||
| 1216 | if (txc.status & STA_UNSYNC0x0040) | ||||||||
| 1217 | return false0; | ||||||||
| 1218 | |||||||||
| 1219 | return true1; | ||||||||
| 1220 | } | ||||||||
| 1221 | |||||||||
| 1222 | int get_timezones(char ***ret) { | ||||||||
| 1223 | _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f = NULL((void*)0); | ||||||||
| 1224 | _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **zones = NULL((void*)0); | ||||||||
| 1225 | size_t n_zones = 0, n_allocated = 0; | ||||||||
| 1226 | |||||||||
| 1227 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/basic/time-util.c", 1227 , __PRETTY_FUNCTION__); } while (0); | ||||||||
| 1228 | |||||||||
| 1229 | zones = strv_new("UTC", NULL((void*)0)); | ||||||||
| 1230 | if (!zones) | ||||||||
| 1231 | return -ENOMEM12; | ||||||||
| 1232 | |||||||||
| 1233 | n_allocated = 2; | ||||||||
| 1234 | n_zones = 1; | ||||||||
| 1235 | |||||||||
| 1236 | f = fopen("/usr/share/zoneinfo/zone.tab", "re"); | ||||||||
| 1237 | if (f) { | ||||||||
| 1238 | char l[LINE_MAX2048]; | ||||||||
| 1239 | |||||||||
| 1240 | FOREACH_LINE(l, f, return -errno)for (;;) if (!fgets(l, sizeof(l), f)) { if (ferror(f)) { return -(*__errno_location ()); } break; } else { | ||||||||
| 1241 | char *p, *w; | ||||||||
| 1242 | size_t k; | ||||||||
| 1243 | |||||||||
| 1244 | p = strstrip(l); | ||||||||
| 1245 | |||||||||
| 1246 | if (isempty(p) || *p == '#') | ||||||||
| 1247 | continue; | ||||||||
| 1248 | |||||||||
| 1249 | /* Skip over country code */ | ||||||||
| 1250 | p += strcspn(p, WHITESPACE" \t\n\r"); | ||||||||
| 1251 | p += strspn(p, WHITESPACE" \t\n\r"); | ||||||||
| 1252 | |||||||||
| 1253 | /* Skip over coordinates */ | ||||||||
| 1254 | p += strcspn(p, WHITESPACE" \t\n\r"); | ||||||||
| 1255 | p += strspn(p, WHITESPACE" \t\n\r"); | ||||||||
| 1256 | |||||||||
| 1257 | /* Found timezone name */ | ||||||||
| 1258 | k = strcspn(p, WHITESPACE" \t\n\r"); | ||||||||
| 1259 | if (k <= 0) | ||||||||
| 1260 | continue; | ||||||||
| 1261 | |||||||||
| 1262 | w = strndup(p, k); | ||||||||
| 1263 | if (!w) | ||||||||
| 1264 | return -ENOMEM12; | ||||||||
| 1265 | |||||||||
| 1266 | if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)greedy_realloc((void**) &(zones), &(n_allocated), (n_zones + 2), sizeof((zones)[0]))) { | ||||||||
| 1267 | free(w); | ||||||||
| 1268 | return -ENOMEM12; | ||||||||
| 1269 | } | ||||||||
| 1270 | |||||||||
| 1271 | zones[n_zones++] = w; | ||||||||
| 1272 | zones[n_zones] = NULL((void*)0); | ||||||||
| 1273 | } | ||||||||
| 1274 | |||||||||
| 1275 | strv_sort(zones); | ||||||||
| 1276 | |||||||||
| 1277 | } else if (errno(*__errno_location ()) != ENOENT2) | ||||||||
| 1278 | return -errno(*__errno_location ()); | ||||||||
| 1279 | |||||||||
| 1280 | *ret = TAKE_PTR(zones)({ typeof(zones) _ptr_ = (zones); (zones) = ((void*)0); _ptr_ ; }); | ||||||||
| 1281 | |||||||||
| 1282 | return 0; | ||||||||
| 1283 | } | ||||||||
| 1284 | |||||||||
| 1285 | bool_Bool timezone_is_valid(const char *name, int log_level) { | ||||||||
| 1286 | bool_Bool slash = false0; | ||||||||
| 1287 | const char *p, *t; | ||||||||
| 1288 | _cleanup_close___attribute__((cleanup(closep))) int fd = -1; | ||||||||
| 1289 | char buf[4]; | ||||||||
| 1290 | int r; | ||||||||
| 1291 | |||||||||
| 1292 | if (isempty(name)) | ||||||||
| 1293 | return false0; | ||||||||
| 1294 | |||||||||
| 1295 | if (name[0] == '/') | ||||||||
| 1296 | return false0; | ||||||||
| 1297 | |||||||||
| 1298 | for (p = name; *p; p++) { | ||||||||
| 1299 | if (!(*p >= '0' && *p <= '9') && | ||||||||
| 1300 | !(*p >= 'a' && *p <= 'z') && | ||||||||
| 1301 | !(*p >= 'A' && *p <= 'Z') && | ||||||||
| 1302 | !IN_SET(*p, '-', '_', '+', '/')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){'-', '_', '+', '/'})/sizeof(int)]; switch (*p) { case '-': case '_': case '+': case '/': _found = 1; break ; default: break; } _found; })) | ||||||||
| 1303 | return false0; | ||||||||
| 1304 | |||||||||
| 1305 | if (*p == '/') { | ||||||||
| 1306 | |||||||||
| 1307 | if (slash) | ||||||||
| 1308 | return false0; | ||||||||
| 1309 | |||||||||
| 1310 | slash = true1; | ||||||||
| 1311 | } else | ||||||||
| 1312 | slash = false0; | ||||||||
| 1313 | } | ||||||||
| 1314 | |||||||||
| 1315 | if (slash) | ||||||||
| 1316 | return false0; | ||||||||
| 1317 | |||||||||
| 1318 | if (p - name >= PATH_MAX4096) | ||||||||
| 1319 | return false0; | ||||||||
| 1320 | |||||||||
| 1321 | t = strjoina("/usr/share/zoneinfo/", name)({ const char *_appendees_[] = { "/usr/share/zoneinfo/", name }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | ||||||||
| 1322 | |||||||||
| 1323 | fd = open(t, O_RDONLY00|O_CLOEXEC02000000); | ||||||||
| 1324 | if (fd < 0) { | ||||||||
| 1325 | log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t)({ int _level = ((log_level)), _e = (((*__errno_location ())) ), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm ) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/basic/time-util.c", 1325 , __func__, "Failed to open timezone file '%s': %m", t) : -abs (_e); }); | ||||||||
| 1326 | return false0; | ||||||||
| 1327 | } | ||||||||
| 1328 | |||||||||
| 1329 | r = fd_verify_regular(fd); | ||||||||
| 1330 | if (r < 0) { | ||||||||
| 1331 | log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t)({ int _level = ((log_level)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/time-util.c", 1331, __func__, "Timezone file '%s' is not a regular file: %m" , t) : -abs(_e); }); | ||||||||
| 1332 | return false0; | ||||||||
| 1333 | } | ||||||||
| 1334 | |||||||||
| 1335 | r = loop_read_exact(fd, buf, 4, false0); | ||||||||
| 1336 | if (r < 0) { | ||||||||
| 1337 | log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t)({ int _level = ((log_level)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/time-util.c", 1337, __func__, "Failed to read from timezone file '%s': %m" , t) : -abs(_e); }); | ||||||||
| 1338 | return false0; | ||||||||
| 1339 | } | ||||||||
| 1340 | |||||||||
| 1341 | /* Magic from tzfile(5) */ | ||||||||
| 1342 | if (memcmp(buf, "TZif", 4) != 0) { | ||||||||
| 1343 | log_full(log_level, "Timezone file '%s' has wrong magic bytes", t)({ int _level = (((log_level))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/time-util.c", 1343, __func__, "Timezone file '%s' has wrong magic bytes" , t) : -abs(_e); }); | ||||||||
| 1344 | return false0; | ||||||||
| 1345 | } | ||||||||
| 1346 | |||||||||
| 1347 | return true1; | ||||||||
| 1348 | } | ||||||||
| 1349 | |||||||||
| 1350 | bool_Bool clock_boottime_supported(void) { | ||||||||
| 1351 | static int supported = -1; | ||||||||
| 1352 | |||||||||
| 1353 | /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */ | ||||||||
| 1354 | |||||||||
| 1355 | if (supported < 0) { | ||||||||
| 1356 | int fd; | ||||||||
| 1357 | |||||||||
| 1358 | fd = timerfd_create(CLOCK_BOOTTIME7, TFD_NONBLOCKTFD_NONBLOCK|TFD_CLOEXECTFD_CLOEXEC); | ||||||||
| 1359 | if (fd < 0) | ||||||||
| 1360 | supported = false0; | ||||||||
| 1361 | else { | ||||||||
| 1362 | safe_close(fd); | ||||||||
| 1363 | supported = true1; | ||||||||
| 1364 | } | ||||||||
| 1365 | } | ||||||||
| 1366 | |||||||||
| 1367 | return supported; | ||||||||
| 1368 | } | ||||||||
| 1369 | |||||||||
| 1370 | clockid_t clock_boottime_or_monotonic(void) { | ||||||||
| 1371 | if (clock_boottime_supported()) | ||||||||
| 1372 | return CLOCK_BOOTTIME7; | ||||||||
| 1373 | else | ||||||||
| 1374 | return CLOCK_MONOTONIC1; | ||||||||
| 1375 | } | ||||||||
| 1376 | |||||||||
| 1377 | bool_Bool clock_supported(clockid_t clock) { | ||||||||
| 1378 | struct timespec ts; | ||||||||
| 1379 | |||||||||
| 1380 | switch (clock) { | ||||||||
| 1381 | |||||||||
| 1382 | case CLOCK_MONOTONIC1: | ||||||||
| 1383 | case CLOCK_REALTIME0: | ||||||||
| 1384 | return true1; | ||||||||
| 1385 | |||||||||
| 1386 | case CLOCK_BOOTTIME7: | ||||||||
| 1387 | return clock_boottime_supported(); | ||||||||
| 1388 | |||||||||
| 1389 | case CLOCK_BOOTTIME_ALARM9: | ||||||||
| 1390 | if (!clock_boottime_supported()) | ||||||||
| 1391 | return false0; | ||||||||
| 1392 | |||||||||
| 1393 | _fallthrough_; | ||||||||
| 1394 | default: | ||||||||
| 1395 | /* For everything else, check properly */ | ||||||||
| 1396 | return clock_gettime(clock, &ts) >= 0; | ||||||||
| 1397 | } | ||||||||
| 1398 | } | ||||||||
| 1399 | |||||||||
| 1400 | int get_timezone(char **tz) { | ||||||||
| 1401 | _cleanup_free___attribute__((cleanup(freep))) char *t = NULL((void*)0); | ||||||||
| 1402 | const char *e; | ||||||||
| 1403 | char *z; | ||||||||
| 1404 | int r; | ||||||||
| 1405 | |||||||||
| 1406 | r = readlink_malloc("/etc/localtime", &t); | ||||||||
| 1407 | if (r < 0) | ||||||||
| 1408 | return r; /* returns EINVAL if not a symlink */ | ||||||||
| 1409 | |||||||||
| 1410 | e = path_startswith(t, "/usr/share/zoneinfo/"); | ||||||||
| 1411 | if (!e) | ||||||||
| 1412 | e = path_startswith(t, "../usr/share/zoneinfo/"); | ||||||||
| 1413 | if (!e) | ||||||||
| 1414 | return -EINVAL22; | ||||||||
| 1415 | |||||||||
| 1416 | if (!timezone_is_valid(e, LOG_DEBUG7)) | ||||||||
| 1417 | return -EINVAL22; | ||||||||
| 1418 | |||||||||
| 1419 | z = strdup(e); | ||||||||
| 1420 | if (!z) | ||||||||
| 1421 | return -ENOMEM12; | ||||||||
| 1422 | |||||||||
| 1423 | *tz = z; | ||||||||
| 1424 | return 0; | ||||||||
| 1425 | } | ||||||||
| 1426 | |||||||||
| 1427 | time_t mktime_or_timegm(struct tm *tm, bool_Bool utc) { | ||||||||
| 1428 | return utc ? timegm(tm) : mktime(tm); | ||||||||
| 1429 | } | ||||||||
| 1430 | |||||||||
| 1431 | struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool_Bool utc) { | ||||||||
| 1432 | return utc
| ||||||||
| 1433 | } | ||||||||
| 1434 | |||||||||
| 1435 | unsigned long usec_to_jiffies(usec_t u) { | ||||||||
| 1436 | static thread_local__thread unsigned long hz = 0; | ||||||||
| 1437 | long r; | ||||||||
| 1438 | |||||||||
| 1439 | if (hz == 0) { | ||||||||
| 1440 | r = sysconf(_SC_CLK_TCK_SC_CLK_TCK); | ||||||||
| 1441 | |||||||||
| 1442 | assert(r > 0)do { if ((__builtin_expect(!!(!(r > 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("r > 0"), "../src/basic/time-util.c", 1442, __PRETTY_FUNCTION__); } while (0); | ||||||||
| 1443 | hz = r; | ||||||||
| 1444 | } | ||||||||
| 1445 | |||||||||
| 1446 | return DIV_ROUND_UP(u , USEC_PER_SEC / hz)({ const typeof((u)) __unique_prefix_X18 = ((u)); const typeof ((((usec_t) 1000000ULL) / hz)) __unique_prefix_Y19 = ((((usec_t ) 1000000ULL) / hz)); (__unique_prefix_X18 / __unique_prefix_Y19 + !!(__unique_prefix_X18 % __unique_prefix_Y19)); }); | ||||||||
| 1447 | } | ||||||||
| 1448 | |||||||||
| 1449 | usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) { | ||||||||
| 1450 | usec_t a, b; | ||||||||
| 1451 | |||||||||
| 1452 | if (x == USEC_INFINITY((usec_t) -1)) | ||||||||
| 1453 | return USEC_INFINITY((usec_t) -1); | ||||||||
| 1454 | if (map_clock_id(from) == map_clock_id(to)) | ||||||||
| 1455 | return x; | ||||||||
| 1456 | |||||||||
| 1457 | a = now(from); | ||||||||
| 1458 | b = now(to); | ||||||||
| 1459 | |||||||||
| 1460 | if (x > a) | ||||||||
| 1461 | /* x lies in the future */ | ||||||||
| 1462 | return usec_add(b, usec_sub_unsigned(x, a)); | ||||||||
| 1463 | else | ||||||||
| 1464 | /* x lies in the past */ | ||||||||
| 1465 | return usec_sub_unsigned(b, usec_sub_unsigned(a, x)); | ||||||||
| 1466 | } | ||||||||
| 1467 | |||||||||
| 1468 | bool_Bool in_utc_timezone(void) { | ||||||||
| 1469 | tzset(); | ||||||||
| 1470 | |||||||||
| 1471 | return timezone == 0 && daylight == 0; | ||||||||
| 1472 | } | ||||||||
| 1473 | |||||||||
| 1474 | int time_change_fd(void) { | ||||||||
| 1475 | |||||||||
| 1476 | /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */ | ||||||||
| 1477 | static const struct itimerspec its = { | ||||||||
| 1478 | .it_value.tv_sec = TIME_T_MAX(time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1 ), | ||||||||
| 1479 | }; | ||||||||
| 1480 | |||||||||
| 1481 | _cleanup_close___attribute__((cleanup(closep))) int fd; | ||||||||
| 1482 | |||||||||
| 1483 | assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX))GCC diagnostic push
; GCC diagnostic ignored "-Wdeclaration-after-statement" ; struct _assert_struct_20 { char x[(sizeof(time_t) == sizeof ((time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1 ))) ? 0 : -1]; }; GCC diagnostic pop ; | ||||||||
| 1484 | |||||||||
| 1485 | /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to | ||||||||
| 1486 | * CLOCK_MONOTONIC. */ | ||||||||
| 1487 | |||||||||
| 1488 | fd = timerfd_create(CLOCK_REALTIME0, TFD_NONBLOCKTFD_NONBLOCK|TFD_CLOEXECTFD_CLOEXEC); | ||||||||
| 1489 | if (fd < 0) | ||||||||
| 1490 | return -errno(*__errno_location ()); | ||||||||
| 1491 | |||||||||
| 1492 | if (timerfd_settime(fd, TFD_TIMER_ABSTIMETFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SETTFD_TIMER_CANCEL_ON_SET, &its, NULL((void*)0)) < 0) | ||||||||
| 1493 | return -errno(*__errno_location ()); | ||||||||
| 1494 | |||||||||
| 1495 | return TAKE_FD(fd)({ int _fd_ = (fd); (fd) = -1; _fd_; }); | ||||||||
| 1496 | } |
| 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
| 2 | #pragma once |
| 3 | |
| 4 | #include <alloca.h> |
| 5 | #include <stdbool.h> |
| 6 | #include <stddef.h> |
| 7 | #include <string.h> |
| 8 | |
| 9 | #include "macro.h" |
| 10 | |
| 11 | /* What is interpreted as whitespace? */ |
| 12 | #define WHITESPACE" \t\n\r" " \t\n\r" |
| 13 | #define NEWLINE"\n\r" "\n\r" |
| 14 | #define QUOTES"\"\'" "\"\'" |
| 15 | #define COMMENTS"#;" "#;" |
| 16 | #define GLOB_CHARS"*?[" "*?[" |
| 17 | #define DIGITS"0123456789" "0123456789" |
| 18 | #define LOWERCASE_LETTERS"abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz" |
| 19 | #define UPPERCASE_LETTERS"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| 20 | #define LETTERS"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" LOWERCASE_LETTERS"abcdefghijklmnopqrstuvwxyz" UPPERCASE_LETTERS"ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| 21 | #define ALPHANUMERICAL"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" LETTERS"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" DIGITS"0123456789" |
| 22 | #define HEXDIGITS"0123456789" "abcdefABCDEF" DIGITS"0123456789" "abcdefABCDEF" |
| 23 | |
| 24 | #define streq(a,b)(strcmp((a),(b)) == 0) (strcmp((a),(b)) == 0) |
| 25 | #define strneq(a, b, n)(strncmp((a), (b), (n)) == 0) (strncmp((a), (b), (n)) == 0) |
| 26 | #define strcaseeq(a,b)(strcasecmp((a),(b)) == 0) (strcasecmp((a),(b)) == 0) |
| 27 | #define strncaseeq(a, b, n)(strncasecmp((a), (b), (n)) == 0) (strncasecmp((a), (b), (n)) == 0) |
| 28 | |
| 29 | int strcmp_ptr(const char *a, const char *b) _pure___attribute__ ((pure)); |
| 30 | |
| 31 | static inline bool_Bool streq_ptr(const char *a, const char *b) { |
| 32 | return strcmp_ptr(a, b) == 0; |
| 33 | } |
| 34 | |
| 35 | static inline const char* strempty(const char *s) { |
| 36 | return s ?: ""; |
| 37 | } |
| 38 | |
| 39 | static inline const char* strnull(const char *s) { |
| 40 | return s ?: "(null)"; |
| 41 | } |
| 42 | |
| 43 | static inline const char *strna(const char *s) { |
| 44 | return s ?: "n/a"; |
| 45 | } |
| 46 | |
| 47 | static inline bool_Bool isempty(const char *p) { |
| 48 | return !p || !p[0]; |
| 49 | } |
| 50 | |
| 51 | static inline const char *empty_to_null(const char *p) { |
| 52 | return isempty(p) ? NULL((void*)0) : p; |
| 53 | } |
| 54 | |
| 55 | static inline const char *empty_to_dash(const char *str) { |
| 56 | return isempty(str) ? "-" : str; |
| 57 | } |
| 58 | |
| 59 | static inline char *startswith(const char *s, const char *prefix) { |
| 60 | size_t l; |
| 61 | |
| 62 | l = strlen(prefix); |
| 63 | if (strncmp(s, prefix, l) == 0) |
| 64 | return (char*) s + l; |
| 65 | |
| 66 | return NULL((void*)0); |
| 67 | } |
| 68 | |
| 69 | static inline char *startswith_no_case(const char *s, const char *prefix) { |
| 70 | size_t l; |
| 71 | |
| 72 | l = strlen(prefix); |
| 73 | if (strncasecmp(s, prefix, l) == 0) |
| 74 | return (char*) s + l; |
| 75 | |
| 76 | return NULL((void*)0); |
| 77 | } |
| 78 | |
| 79 | char *endswith(const char *s, const char *postfix) _pure___attribute__ ((pure)); |
| 80 | char *endswith_no_case(const char *s, const char *postfix) _pure___attribute__ ((pure)); |
| 81 | |
| 82 | char *first_word(const char *s, const char *word) _pure___attribute__ ((pure)); |
| 83 | |
| 84 | const char* split(const char **state, size_t *l, const char *separator, bool_Bool quoted); |
| 85 | |
| 86 | #define FOREACH_WORD(word, length, s, state)for ((state) = (s), (word) = split(&(state), &(length ), (" \t\n\r"), (0)); (word); (word) = split(&(state), & (length), (" \t\n\r"), (0))) \ |
| 87 | _FOREACH_WORD(word, length, s, WHITESPACE, false, state)for ((state) = (s), (word) = split(&(state), &(length ), (" \t\n\r"), (0)); (word); (word) = split(&(state), & (length), (" \t\n\r"), (0))) |
| 88 | |
| 89 | #define FOREACH_WORD_SEPARATOR(word, length, s, separator, state)for ((state) = (s), (word) = split(&(state), &(length ), (separator), (0)); (word); (word) = split(&(state), & (length), (separator), (0))) \ |
| 90 | _FOREACH_WORD(word, length, s, separator, false, state)for ((state) = (s), (word) = split(&(state), &(length ), (separator), (0)); (word); (word) = split(&(state), & (length), (separator), (0))) |
| 91 | |
| 92 | #define _FOREACH_WORD(word, length, s, separator, quoted, state)for ((state) = (s), (word) = split(&(state), &(length ), (separator), (quoted)); (word); (word) = split(&(state ), &(length), (separator), (quoted))) \ |
| 93 | for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) |
| 94 | |
| 95 | char *strappend(const char *s, const char *suffix); |
| 96 | char *strnappend(const char *s, const char *suffix, size_t length); |
| 97 | |
| 98 | char *strjoin_real(const char *x, ...) _sentinel___attribute__ ((sentinel)); |
| 99 | #define strjoin(a, ...)strjoin_real((a), ..., ((void*)0)) strjoin_real((a), __VA_ARGS__, NULL((void*)0)) |
| 100 | |
| 101 | #define strjoina(a, ...)({ const char *_appendees_[] = { a, ... }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ ( __builtin_choose_expr( !__builtin_types_compatible_p(typeof(_appendees_ ), typeof(&*(_appendees_))), sizeof(_appendees_)/sizeof(( _appendees_)[0]), ((void)0))) && _appendees_[_i_]; _i_ ++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }) \ |
| 102 | ({ \ |
| 103 | const char *_appendees_[] = { a, __VA_ARGS__ }; \ |
| 104 | char *_d_, *_p_; \ |
| 105 | size_t _len_ = 0; \ |
| 106 | size_t _i_; \ |
| 107 | for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_[_i_]; _i_++) \ |
| 108 | _len_ += strlen(_appendees_[_i_]); \ |
| 109 | _p_ = _d_ = alloca(_len_ + 1)__builtin_alloca (_len_ + 1); \ |
| 110 | for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_[_i_]; _i_++) \ |
| 111 | _p_ = stpcpy(_p_, _appendees_[_i_]); \ |
| 112 | *_p_ = 0; \ |
| 113 | _d_; \ |
| 114 | }) |
| 115 | |
| 116 | char *strstrip(char *s); |
| 117 | char *delete_chars(char *s, const char *bad); |
| 118 | char *delete_trailing_chars(char *s, const char *bad); |
| 119 | char *truncate_nl(char *s); |
| 120 | |
| 121 | static inline char *skip_leading_chars(const char *s, const char *bad) { |
| 122 | |
| 123 | if (!s) |
| 124 | return NULL((void*)0); |
| 125 | |
| 126 | if (!bad) |
| 127 | bad = WHITESPACE" \t\n\r"; |
| 128 | |
| 129 | return (char*) s + strspn(s, bad); |
| 130 | } |
| 131 | |
| 132 | char ascii_tolower(char x); |
| 133 | char *ascii_strlower(char *s); |
| 134 | char *ascii_strlower_n(char *s, size_t n); |
| 135 | |
| 136 | char ascii_toupper(char x); |
| 137 | char *ascii_strupper(char *s); |
| 138 | |
| 139 | int ascii_strcasecmp_n(const char *a, const char *b, size_t n); |
| 140 | int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m); |
| 141 | |
| 142 | bool_Bool chars_intersect(const char *a, const char *b) _pure___attribute__ ((pure)); |
| 143 | |
| 144 | static inline bool_Bool _pure___attribute__ ((pure)) in_charset(const char *s, const char* charset) { |
| 145 | assert(s)do { if ((__builtin_expect(!!(!(s)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("s"), "../src/basic/string-util.h", 145, __PRETTY_FUNCTION__); } while (0); |
| 146 | assert(charset)do { if ((__builtin_expect(!!(!(charset)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("charset"), "../src/basic/string-util.h" , 146, __PRETTY_FUNCTION__); } while (0); |
| 147 | return s[strspn(s, charset)] == '\0'; |
| 148 | } |
| 149 | |
| 150 | bool_Bool string_has_cc(const char *p, const char *ok) _pure___attribute__ ((pure)); |
| 151 | |
| 152 | char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent); |
| 153 | static inline char *ellipsize(const char *s, size_t length, unsigned percent) { |
| 154 | return ellipsize_mem(s, strlen(s), length, percent); |
| 155 | } |
| 156 | |
| 157 | char *cellescape(char *buf, size_t len, const char *s); |
| 158 | |
| 159 | /* This limit is arbitrary, enough to give some idea what the string contains */ |
| 160 | #define CELLESCAPE_DEFAULT_LENGTH64 64 |
| 161 | |
| 162 | bool_Bool nulstr_contains(const char *nulstr, const char *needle); |
| 163 | |
| 164 | char* strshorten(char *s, size_t l); |
| 165 | |
| 166 | char *strreplace(const char *text, const char *old_string, const char *new_string); |
| 167 | |
| 168 | char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]); |
| 169 | |
| 170 | char *strextend_with_separator(char **x, const char *separator, ...) _sentinel___attribute__ ((sentinel)); |
| 171 | |
| 172 | #define strextend(x, ...)strextend_with_separator(x, ((void*)0), ...) strextend_with_separator(x, NULL((void*)0), __VA_ARGS__) |
| 173 | |
| 174 | char *strrep(const char *s, unsigned n); |
| 175 | |
| 176 | int split_pair(const char *s, const char *sep, char **l, char **r); |
| 177 | |
| 178 | int free_and_strdup(char **p, const char *s); |
| 179 | int free_and_strndup(char **p, const char *s, size_t l); |
| 180 | |
| 181 | /* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */ |
| 182 | static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { |
| 183 | |
| 184 | if (needlelen <= 0) |
| 185 | return (void*) haystack; |
| 186 | |
| 187 | if (haystacklen < needlelen) |
| 188 | return NULL((void*)0); |
| 189 | |
| 190 | assert(haystack)do { if ((__builtin_expect(!!(!(haystack)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("haystack"), "../src/basic/string-util.h" , 190, __PRETTY_FUNCTION__); } while (0); |
| 191 | assert(needle)do { if ((__builtin_expect(!!(!(needle)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("needle"), "../src/basic/string-util.h", 191, __PRETTY_FUNCTION__); } while (0); |
| 192 | |
| 193 | return memmem(haystack, haystacklen, needle, needlelen); |
| 194 | } |
| 195 | |
| 196 | #if !HAVE_EXPLICIT_BZERO1 |
| 197 | void explicit_bzero(void *p, size_t l); |
| 198 | #endif |
| 199 | |
| 200 | char *string_erase(char *x); |
| 201 | |
| 202 | char *string_free_erase(char *s); |
| 203 | DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase)static inline void string_free_erasep(char * *p) { if (*p) string_free_erase (*p); }; |
| 204 | #define _cleanup_string_free_erase___attribute__((cleanup(string_free_erasep))) _cleanup_(string_free_erasep)__attribute__((cleanup(string_free_erasep))) |
| 205 | |
| 206 | bool_Bool string_is_safe(const char *p) _pure___attribute__ ((pure)); |
| 207 | |
| 208 | static inline size_t strlen_ptr(const char *s) { |
| 209 | if (!s) |
| 210 | return 0; |
| 211 | |
| 212 | return strlen(s); |
| 213 | } |
| 214 | |
| 215 | /* Like startswith(), but operates on arbitrary memory blocks */ |
| 216 | static inline void *memory_startswith(const void *p, size_t sz, const char *token) { |
| 217 | size_t n; |
| 218 | |
| 219 | assert(token)do { if ((__builtin_expect(!!(!(token)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("token"), "../src/basic/string-util.h", 219 , __PRETTY_FUNCTION__); } while (0); |
| 220 | |
| 221 | n = strlen(token); |
| 222 | if (sz < n) |
| 223 | return NULL((void*)0); |
| 224 | |
| 225 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/basic/string-util.h", 225, __PRETTY_FUNCTION__); } while (0); |
| 226 | |
| 227 | if (memcmp(p, token, n) != 0) |
| 228 | return NULL((void*)0); |
| 229 | |
| 230 | return (uint8_t*) p + n; |
| 231 | } |