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); }