From 0c0e880f3fdd38f7bbde618408378dc0a19ff005 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 3 Nov 2024 09:39:46 +0100 Subject: [PATCH 1/4] plugins: refactor rtc.so Factor out time_set() and time_get() for readability and reuse. Signed-off-by: Joachim Wiberg --- plugins/rtc.c | 116 +++++++++++++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/plugins/rtc.c b/plugins/rtc.c index 238791fe..9520c7d2 100644 --- a/plugins/rtc.c +++ b/plugins/rtc.c @@ -68,6 +68,60 @@ static void tz_restore(char *tz) tzset(); } +static int time_set(struct tm *tm) +{ + struct tm fallback = { 0 }; + struct timeval tv = { 0 }; + char tz[128]; + int rc = 0; + + tz_set(tz, sizeof(tz)); + + if (!tm) { + logit(LOG_NOTICE, "Resetting system clock to kernel default, %s.", rtc_timestamp); + tm = &fallback; + + /* Attempt to set RTC to a sane value ... */ + tv.tv_sec = rtc_date_fallback; + if (!gmtime_r(&tv.tv_sec, tm)) { + rc = 1; + goto out; + } + } + + tm->tm_isdst = -1; /* Use tzdata to figure it out, please. */ + tv.tv_sec = mktime(tm); + if (tv.tv_sec == (time_t)-1 || tv.tv_sec < rtc_date_fallback) { + errno = EINVAL; + rc = 2; + } else { + if (settimeofday(&tv, NULL) == -1) + rc = 1; + } +out: + tz_restore(tz); + return rc; +} + +static int time_get(struct tm *tm) +{ + struct timeval tv = { 0 }; + char tz[128]; + int rc = 0; + + tz_set(tz, sizeof(tz)); + + rc = gettimeofday(&tv, NULL); + if (rc < 0 || tv.tv_sec < rtc_date_fallback) + rc = 2; + else + gmtime_r(&tv.tv_sec, tm); + + tz_restore(tz); + + return rc; +} + static int rtc_open(void) { char *alt[] = { @@ -91,10 +145,8 @@ static int rtc_open(void) static void rtc_save(void *arg) { - struct timeval tv = { 0 }; struct tm tm = { 0 }; int fd, rc = 0; - char tz[128]; if (rescue) { dbg("Skipping %s plugin in rescue mode.", __FILE__); @@ -105,38 +157,26 @@ static void rtc_save(void *arg) if (fd < 0) return; - tz_set(tz, sizeof(tz)); - rc = gettimeofday(&tv, NULL); - if (rc < 0 || tv.tv_sec < rtc_date_fallback) { + if ((rc = time_get(&tm))) { print_desc(NULL, "System clock invalid, not saving to RTC"); - invalid: + } else { + print_desc(NULL, "Saving system clock (UTC) to RTC"); + rc = ioctl(fd, RTC_SET_TIME, &tm); + } + + if (rc && errno == EINVAL) { logit(LOG_ERR, "System clock invalid, before %s, not saving to RTC", rtc_timestamp); rc = 2; - goto out; } - print_desc(NULL, "Saving system time (UTC) to RTC"); - - gmtime_r(&tv.tv_sec, &tm); - if (ioctl(fd, RTC_SET_TIME, &tm) < 0) { - if (EINVAL == errno) - goto invalid; - rc = 1; - goto out; - } - -out: - tz_restore(tz); print(rc, NULL); close(fd); } static void rtc_restore(void *arg) { - struct timeval tv = { 0 }; struct tm tm = { 0 }; int fd, rc = 0; - char tz[128]; if (rescue) { dbg("Skipping %s plugin in rescue mode.", __FILE__); @@ -149,16 +189,19 @@ static void rtc_restore(void *arg) return; } - tz_set(tz, sizeof(tz)); - if (ioctl(fd, RTC_RD_TIME, &tm) < 0) { + if ((rc = ioctl(fd, RTC_RD_TIME, &tm)) < 0) { char msg[120]; snprintf(msg, sizeof(msg), "Failed restoring system clock, %s", EINVAL == errno ? "RTC time is too old" : ENOENT == errno ? "RTC has no saved time" : "see log for details"); print_desc(NULL, msg); + } else { + print_desc(NULL, "Restoring system clock (UTC) from RTC"); + rc = time_set(&tm); + } - invalid: + if (rc) { logit(LOG_ERR, "Failed restoring system clock from RTC."); if (EINVAL == errno) logit(LOG_ERR, "RTC time is too old (before %s)", rtc_timestamp); @@ -167,33 +210,10 @@ static void rtc_restore(void *arg) else logit(LOG_ERR, "RTC error code %d: %s", errno, strerror(errno)); - /* Been here already? */ - if (rc) - goto out; - - /* Attempt to set RTC to a sane value ... */ - tv.tv_sec = rtc_date_fallback; - if (!gmtime_r(&tv.tv_sec, &tm)) - goto out; - - logit(LOG_NOTICE, "Resetting RTC to kernel default, %s.", rtc_timestamp); + time_set(NULL); rc = 2; } - if (!rc) - print_desc(NULL, "Restoring system clock (UTC) from RTC"); - tm.tm_isdst = -1; /* Use tzdata to figure it out, please. */ - tv.tv_sec = mktime(&tm); - if (tv.tv_sec == (time_t)-1 || tv.tv_sec < rtc_date_fallback) { - errno = EINVAL; - goto invalid; - } - - if (settimeofday(&tv, NULL) == -1) - rc = 1; - -out: - tz_restore(tz); print(rc, NULL); close(fd); } From bc8118d515839dc598f437aa01f07a771646968d Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 3 Nov 2024 09:47:16 +0100 Subject: [PATCH 2/4] Fix #418: support systems with a broken RTC This patch introduces a new configure option --with-rtc-file=FILE. When enabled the RTC plugin detects missing RTC device and falls back to save and restore system time from a file instead. When --with-rtc-file is used without an argument the default file is /var/lib/misc/rtc, but the feature itself is disabled by default. The usefulness of this feature may not be obvious at first, but some systems are equipped with an RTC that resets to a random date at power on. This can be really bad in the case the date is far in the future, because an NTP sync would then cause time skips backwards, which shows up in logs and causes a whole lot of pain in alarm systems. The solution is to disable the RTC driver or device tree node, and when Finit starts up, the RTC plugin detects a the device node and instead restores time from the last save game. Meaning time will always only move forwards. NOTE: when Finit is built --with-rtc-file we always save to disk, but only restore from the "save game" if restoring from RTC fails. If the system has no RTC we always restore from disk. As an added bonus, this change also makes sure to periodically sync also the RTC with the system clock. Useful for systems that do not run an NTP client. Signed-off-by: Joachim Wiberg --- configure.ac | 12 ++++++ plugins/rtc.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 110 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 483457f5..ae7cd238 100644 --- a/configure.ac +++ b/configure.ac @@ -180,6 +180,10 @@ AC_ARG_WITH(rtc-date, AS_HELP_STRING([--with-rtc-date=DATE], [If RTC date/time is too old, restore to DATE, format "YYYY-MM-DD HH:MM", default "2000-01-01 00:00"]), [rtc_date=$withval], [rtc_date=no]) +AC_ARG_WITH(rtc-file, + AS_HELP_STRING([--with-rtc-file=FILE], [If RTC is missing, save and restore system clock from this file, default: no]), + [rtc_file=$withval], [rtc_file=no]) + ### Enable features ########################################################################### # Create config.h from selected features and fallback defaults @@ -281,6 +285,13 @@ AS_IF([test "x$rtc_date" != "xno"], [ AC_DEFINE(RTC_TIMESTAMP_CUSTOM, "$rtc_date", [Custom RTC restore date, default: 2000-01-01 00:00])], [ rtc_date=""]) +AS_IF([test "x$rtc_file" != "xno"], [ + AS_IF([test "x$rtc_file" = "xyes"], [ + rtc_file=/var/lib/misc/rtc]) + AC_EXPAND_DIR(rtcfile_path, "$rtc_file") + AC_DEFINE_UNQUOTED(RTC_FILE, "$rtcfile_path", [Save and restore system time from this file if /dev/rtc is missing.])],[ + AC_DEFINE_UNQUOTED(RTC_FILE, NULL)]) + AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes]) AS_IF([test "x$with_sulogin" != "xno"], [ @@ -387,6 +398,7 @@ Behavior: Boot heading..........: $heading Plugins...............: $plugins RTC restore date......: $RTC_DATE + RTC fallback file.....: $rtc_file Optional features: Install doc/..........: $enable_doc diff --git a/plugins/rtc.c b/plugins/rtc.c index 9520c7d2..9b4eeae5 100644 --- a/plugins/rtc.c +++ b/plugins/rtc.c @@ -36,8 +36,15 @@ #include "helpers.h" #include "plugin.h" -/* Kernel RTC driver validates against this date for sanity check */ +/* + * Kernel RTC driver validates against this date for sanity check. The + * on NTP sync the driver can also update the RTC every 11 mins. We use + * the same update interval to handle manual time set and file save. + */ #define RTC_TIMESTAMP_BEGIN_2000 "2000-01-01 00:00:00" +#define RTC_FMT "%Y-%m-%d %H:%M:%S" +#define RTC_PERIOD (11 * 60 * 1000) + #ifdef RTC_TIMESTAMP_CUSTOM static char *rtc_timestamp = RTC_TIMESTAMP_CUSTOM; #else @@ -45,6 +52,10 @@ static char *rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000; #endif static time_t rtc_date_fallback = 946684800LL; +static char *rtc_file = RTC_FILE; +static uev_t rtc_timer; + + static void tz_set(char *tz, size_t len) { char *ptr; @@ -122,6 +133,68 @@ static int time_get(struct tm *tm) return rc; } +static void file_save(void *arg) +{ + struct tm tm = { 0 }; + int rc = 0; + FILE *fp; + + fp = fopen(rtc_file, "w"); + if (!fp) { + logit(LOG_WARNING, "Failed saving system clock to %s, code %d: %s", + rtc_file, errno, strerror(errno)); + return; + } + + if ((rc = time_get(&tm))) { + logit(LOG_ERR, "System clock invalid, before %s, not saving", rtc_timestamp); + print_desc(NULL, "System clock invalid, skipping"); + } else { + char buf[32] = { 0 }; + + print_desc(NULL, "Saving system clock to file"); + strftime(buf, sizeof(buf), RTC_FMT, &tm); + fprintf(fp, "%s\n", buf); + } + + print(rc, NULL); + fclose(fp); +} + +static void file_restore(void *arg) +{ + struct tm tm = { 0 }; + int rc = 1; + FILE *fp; + + if (!rtc_file) { + logit(LOG_NOTICE, "System has no RTC (missing driver?), skipping restore."); + return; + } + + print_desc(NULL, "Restoring system clock from backup"); + + fp = fopen(rtc_file, "r"); + if (fp) { + char buf[32]; + + if (fgets(buf, sizeof(buf), fp)) { + chomp(buf); + strptime(buf, RTC_FMT, &tm); + rc = time_set(&tm); + } + fclose(fp); + } else + logit(LOG_WARNING, "Missing %s", rtc_file); + + if (rc) { + time_set(NULL); + rc = 2; + } + + print(rc, NULL); +} + static int rtc_open(void) { char *alt[] = { @@ -185,7 +258,7 @@ static void rtc_restore(void *arg) fd = rtc_open(); if (fd < 0) { - logit(LOG_NOTICE, "System has no RTC (missing driver?), skipping restore."); + file_restore(arg); return; } @@ -210,21 +283,37 @@ static void rtc_restore(void *arg) else logit(LOG_ERR, "RTC error code %d: %s", errno, strerror(errno)); - time_set(NULL); - rc = 2; - } + print(2, NULL); + + /* Try restoring from last save game */ + if (rtc_file) + file_restore(arg); + } else + print(0, NULL); - print(rc, NULL); close(fd); } + +static void save(void *arg) +{ + rtc_save(arg); + file_save(arg); +} + +static void update(uev_t *w, void *arg, int events) +{ + save(arg); +} + + static plugin_t plugin = { .name = __FILE__, .hook[HOOK_BASEFS_UP] = { .cb = rtc_restore }, .hook[HOOK_SHUTDOWN] = { - .cb = rtc_save + .cb = save } }; @@ -237,6 +326,8 @@ PLUGIN_INIT(plugin_init) else rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000; + uev_timer_init(ctx, &rtc_timer, update, NULL, RTC_PERIOD, RTC_PERIOD); + plugin_register(&plugin); } From 6be16f2f6d093ef495d0fe4313f7b05b4ba3e08f Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 3 Nov 2024 10:38:38 +0100 Subject: [PATCH 3/4] Fix buggy --with-rtc-date=DATE, introduced in Finit v4.4 In 42ef3d3c, for v4.4-rc1, support for setting a custom RTC restore date was introduced. Unfortunately the configure script was wrong and caused config.h to contain #define RTC_TIMESTAMP_CUSTOM "$rtc_date" instead of #define RTC_TIMESTAMP_CUSTOM "2023-04-10 14:35:42" Furthermore, the error handling for strptime() was wrong, so the restore date was always reverted to the default. This patch fixes both issues and extends the DATE of --with-rtc-date to also include seconds. Signed-off-by: Joachim Wiberg --- configure.ac | 4 ++-- plugins/rtc.c | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index ae7cd238..58b78ac5 100644 --- a/configure.ac +++ b/configure.ac @@ -177,7 +177,7 @@ AC_ARG_WITH(plugin-path, [plugin_path=$withval], [plugin_path=yes]) AC_ARG_WITH(rtc-date, - AS_HELP_STRING([--with-rtc-date=DATE], [If RTC date/time is too old, restore to DATE, format "YYYY-MM-DD HH:MM", default "2000-01-01 00:00"]), + AS_HELP_STRING([--with-rtc-date=DATE], [If RTC date/time is too old, restore to DATE, format "YYYY-MM-DD HH:MM:SS", default "2000-01-01 00:00:00"]), [rtc_date=$withval], [rtc_date=no]) AC_ARG_WITH(rtc-file, @@ -282,7 +282,7 @@ AS_IF([test "x$with_random_seed" != "xno"], [ AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])]) AS_IF([test "x$rtc_date" != "xno"], [ - AC_DEFINE(RTC_TIMESTAMP_CUSTOM, "$rtc_date", [Custom RTC restore date, default: 2000-01-01 00:00])], [ + AC_DEFINE_UNQUOTED(RTC_TIMESTAMP_CUSTOM, "$rtc_date", [Custom RTC restore date, default: 2000-01-01 00:00])], [ rtc_date=""]) AS_IF([test "x$rtc_file" != "xno"], [ diff --git a/plugins/rtc.c b/plugins/rtc.c index 9b4eeae5..a733f754 100644 --- a/plugins/rtc.c +++ b/plugins/rtc.c @@ -321,10 +321,12 @@ PLUGIN_INIT(plugin_init) { struct tm tm = { 0 }; - if (!strptime(rtc_timestamp, "%Y-%m-%d %H:%M", &tm)) - rtc_date_fallback = mktime(&tm); - else + if (!strptime(rtc_timestamp, RTC_FMT, &tm)) { + logit(LOG_ERR, "Invalid restore date '%s', reverting to '%s'", + rtc_timestamp, RTC_TIMESTAMP_BEGIN_2000); rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000; + } else + rtc_date_fallback = mktime(&tm); uev_timer_init(ctx, &rtc_timer, update, NULL, RTC_PERIOD, RTC_PERIOD); From 49c0557cedd8d3c1a2f74d27fa7db83dd529914a Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 3 Nov 2024 20:49:04 +0100 Subject: [PATCH 4/4] plugins: reduce log level LOG_ERR -> LOG_WARNING These plugins signal success and failure directly to the console, the user should inspect syslog for more information. This change is a follow-up to 340cae4, where kernel logs of LOG_ERR and higher are allowed to log directly to the console. Since syslogd has not been started before these plugins, the log messages would otherwise leak to the console. Signed-off-by: Joachim Wiberg --- plugins/rtc.c | 14 +++++++------- plugins/urandom.c | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/rtc.c b/plugins/rtc.c index a733f754..96203a0a 100644 --- a/plugins/rtc.c +++ b/plugins/rtc.c @@ -147,7 +147,7 @@ static void file_save(void *arg) } if ((rc = time_get(&tm))) { - logit(LOG_ERR, "System clock invalid, before %s, not saving", rtc_timestamp); + logit(LOG_WARNING, "System clock invalid, before %s, not saving", rtc_timestamp); print_desc(NULL, "System clock invalid, skipping"); } else { char buf[32] = { 0 }; @@ -238,7 +238,7 @@ static void rtc_save(void *arg) } if (rc && errno == EINVAL) { - logit(LOG_ERR, "System clock invalid, before %s, not saving to RTC", rtc_timestamp); + logit(LOG_WARNING, "System clock invalid, before %s, not saving to RTC", rtc_timestamp); rc = 2; } @@ -275,13 +275,13 @@ static void rtc_restore(void *arg) } if (rc) { - logit(LOG_ERR, "Failed restoring system clock from RTC."); + logit(LOG_WARNING, "Failed restoring system clock from RTC."); if (EINVAL == errno) - logit(LOG_ERR, "RTC time is too old (before %s)", rtc_timestamp); + logit(LOG_WARNING, "RTC time is too old (before %s)", rtc_timestamp); else if (ENOENT == errno) - logit(LOG_ERR, "RTC has no previously saved (valid) time."); + logit(LOG_WARNING, "RTC has no previously saved (valid) time."); else - logit(LOG_ERR, "RTC error code %d: %s", errno, strerror(errno)); + logit(LOG_WARNING, "RTC error code %d: %s", errno, strerror(errno)); print(2, NULL); @@ -322,7 +322,7 @@ PLUGIN_INIT(plugin_init) struct tm tm = { 0 }; if (!strptime(rtc_timestamp, RTC_FMT, &tm)) { - logit(LOG_ERR, "Invalid restore date '%s', reverting to '%s'", + logit(LOG_WARNING, "Invalid restore date '%s', reverting to '%s'", rtc_timestamp, RTC_TIMESTAMP_BEGIN_2000); rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000; } else diff --git a/plugins/urandom.c b/plugins/urandom.c index b9f6039d..6f827796 100644 --- a/plugins/urandom.c +++ b/plugins/urandom.c @@ -154,7 +154,7 @@ static void setup(void *arg) close(fd); free(rpi); if (rc < 0) - logit(LOG_ERR, "Failed adding entropy to kernel random pool: %s", strerror(err)); + logit(LOG_WARNING, "Failed adding entropy to kernel random pool: %s", strerror(err)); print_result(rc < 0); return; fallback: