Compare commits

...

2 Commits

Author SHA1 Message Date
Arijit Kumar Das 250c012925
Merge 903acc1949 into 1a4c2e8807 2025-04-16 15:18:08 +02:00
ArijitKD 903acc1949 Fixed an issue in time-util.c where the timestamp of epoch is returned by
now() and now_nsec() in systems lacking RTC module.

Most embedded systems (like the Raspberry Pi) lack a hardware RTC module.
In such systems, the file /run/systemd/systemd-units-load has an extremely
old timestamp due to the time being reset on a reboot. Since no time
synchronization services are loaded at the earliest boot stage when this
file is created, now() and now_nsec() returns the epoch timestamp because
it uses clock_gettime() to query for the time of the real-time clock, which
is reset in this case. This patch  ensures that in such cases, a recent
timestamp from either the modification time of /var/lib/systemd/timesync/clock
or the last log time of journald is used instead of the RTC timestamp, before
falling back to the epoch timestamp.

The patch fixes a problem where utilities such as mount issue a warning
notifying the user that /etc/fstab has been modified, but no such modification
has been made since the last boot.

Limitations: If /var/lib/systemd/timesync/clock is missing (when, for example,
timesyncd is not installed) or there are no journald logs (possible on a freshly
installed system), now() and now_nsec() may still fall back to the epoch
timestamp, until the system clock is synchronized. This shouldn't be a major issue
unless the system completely operates offline.
2025-03-24 15:04:17 +05:30
1 changed files with 88 additions and 4 deletions

View File

@ -5,6 +5,7 @@
#include <limits.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/timerfd.h>
#include <sys/types.h>
@ -21,6 +22,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "sd-journal.h"
#include "stat-util.h"
#include "string-table.h"
#include "string-util.h"
@ -47,20 +49,102 @@ static clockid_t map_clock_id(clockid_t c) {
}
}
static usec_t get_timesyncd_clock_timestamp(void) {
struct stat st;
if (stat("/var/lib/systemd/timesync/clock", &st) == -1)
return USEC_INFINITY;
return (usec_t)(st.st_mtim.tv_sec * 1000000ULL +
st.st_mtim.tv_nsec / 1000ULL);
}
static usec_t get_journald_lastlog_timestamp(void) {
sd_journal *j = NULL;
usec_t timestamp;
int ret;
ret = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
if (ret < 0)
goto error;
ret = sd_journal_seek_tail(j);
if (ret < 0)
goto error;
ret = sd_journal_previous(j);
if (ret < 0)
goto error;
ret = sd_journal_get_realtime_usec(j, &timestamp);
if (ret < 0)
goto error;
sd_journal_close(j);
return timestamp;
error:
sd_journal_close(j);
return USEC_INFINITY;
}
static usec_t get_recent_timestamp(void) {
usec_t timesyncd_ts = get_timesyncd_clock_timestamp();
return (timesyncd_ts != USEC_INFINITY) ?
timesyncd_ts : get_journald_lastlog_timestamp();
}
usec_t now(clockid_t clock_id) {
struct timespec ts;
clockid_t clock_id_mapped = map_clock_id(clock_id);
assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
assert_se(clock_gettime(clock_id_mapped, &ts) == 0);
return timespec_load(&ts);
usec_t now_timestamp = timespec_load(&ts);
/* Devices that lack a hardware RTC will yield an incorrect time when clock_gettime() is
* called without the system clock being synced by time-sync services. This is especially
* a problem during the early boot phase when systemd has just initiated itself and no such
* services have been loaded yet. This may cause /run/systemd/systemd-units-load to have
* an extremely old epoch timestamp. The function get_recent_timestamp() loads a recent
* timestamp from (1) /var/lib/systemd/timesync/clock, and if it is absent then (2) from
* the timestamp of the last journald log. Only after these two cases fail (possible when
* the system boots for the first time), the ancient timestamp is used.
*/
if (clock_id_mapped == CLOCK_REALTIME) {
usec_t recent_timestamp = get_recent_timestamp();
if (recent_timestamp != USEC_INFINITY &&
now_timestamp < recent_timestamp)
now_timestamp = recent_timestamp;
}
return now_timestamp;
}
nsec_t now_nsec(clockid_t clock_id) {
struct timespec ts;
clockid_t clock_id_mapped = map_clock_id(clock_id);
assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
assert_se(clock_gettime(clock_id_mapped, &ts) == 0);
return timespec_load_nsec(&ts);
nsec_t now_nsec_timestamp = timespec_load_nsec(&ts);
if (clock_id_mapped == CLOCK_REALTIME) {
usec_t recent_timestamp = get_recent_timestamp();
nsec_t recent_nsec_timestamp = (recent_timestamp != USEC_INFINITY) ?
(nsec_t)(recent_timestamp * 1000ULL) : NSEC_INFINITY;
if (recent_nsec_timestamp != NSEC_INFINITY &&
now_nsec_timestamp < recent_nsec_timestamp)
now_nsec_timestamp = recent_nsec_timestamp;
}
return now_nsec_timestamp;
}
dual_timestamp* dual_timestamp_now(dual_timestamp *ts) {