/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-***/
#include <errno.h>
#include <limits.h>
return NULL; /* Timestamp is unset */
/* Let's not format times with years > 9999 */
- if (t > USEC_TIMESTAMP_FORMATTABLE_MAX)
- return NULL;
+ if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) {
+ assert(l >= strlen("--- XXXX-XX-XX XX:XX:XX") + 1);
+ strcpy(buf, "--- XXXX-XX-XX XX:XX:XX");
+ return buf;
+ }
sec = (time_t) (t / USEC_PER_SEC); /* Round down */
/* Let's see if we should shows this in dot notation */
if (t < USEC_PER_MINUTE && b > 0) {
usec_t cc;
- int j;
+ signed char j;
j = 0;
for (cc = table[i].usec; cc > 1; cc /= 10)
int r;
last_space = strrchr(t, ' ');
- if (last_space != NULL && timezone_is_valid(last_space + 1))
+ if (last_space != NULL && timezone_is_valid(last_space + 1, LOG_DEBUG))
tz = last_space + 1;
if (!tz || endswith_no_case(t, " UTC"))
return 0;
}
-bool timezone_is_valid(const char *name) {
+bool timezone_is_valid(const char *name, int log_level) {
bool slash = false;
const char *p, *t;
_cleanup_close_ int fd = -1;
if (slash)
return false;
+ if (p - name >= PATH_MAX)
+ return false;
+
t = strjoina("/usr/share/zoneinfo/", name);
fd = open(t, O_RDONLY|O_CLOEXEC);
if (fd < 0) {
- log_debug_errno(errno, "Failed to open timezone file '%s': %m", t);
+ log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
return false;
}
r = fd_verify_regular(fd);
if (r < 0) {
- log_debug_errno(r, "Timezone file '%s' is not a regular file: %m", t);
+ log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
return false;
}
r = loop_read_exact(fd, buf, 4, false);
if (r < 0) {
- log_debug_errno(r, "Failed to read from timezone file '%s': %m", t);
+ log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
return false;
}
/* Magic from tzfile(5) */
if (memcmp(buf, "TZif", 4) != 0) {
- log_debug("Timezone file '%s' has wrong magic bytes", t);
+ log_full(log_level, "Timezone file '%s' has wrong magic bytes", t);
return false;
}
if (!e)
return -EINVAL;
- if (!timezone_is_valid(e))
+ if (!timezone_is_valid(e, LOG_DEBUG))
return -EINVAL;
z = strdup(e);
return timezone == 0 && daylight == 0;
}
+
+int time_change_fd(void) {
+
+ /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */
+ static const struct itimerspec its = {
+ .it_value.tv_sec = TIME_T_MAX,
+ };
+
+ _cleanup_close_ int fd;
+
+ assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
+
+ /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to
+ * CLOCK_MONOTONIC. */
+
+ fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0)
+ return -errno;
+
+ return TAKE_FD(fd);
+}