From 744fdd654f59048bf1323b434990f4ec8c7b7627 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Fri, 5 Apr 2019 21:27:39 +0200 Subject: [PATCH] plugins: hwclock: Native ioctl support for RTC, skip hwclock tool The BusyBox hwclock is really bad at handling, or rather displaying, errors properly. When the kernel signals EINVAL, or ENOENT, it just shows that message on stderr. This patch implements proper RTC load/store at boot/halt with quite some improved error handling suitable for embedded systems. Signed-off-by: Joachim Nilsson --- plugins/hwclock.c | 113 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 102 insertions(+), 11 deletions(-) diff --git a/plugins/hwclock.c b/plugins/hwclock.c index 2b0c67bb..b0d18c6b 100644 --- a/plugins/hwclock.c +++ b/plugins/hwclock.c @@ -1,6 +1,6 @@ /* Save and restore RTC using hwclock * - * Copyright (c) 2012 Joachim Nilsson + * Copyright (c) 2012-2019 Joachim Nilsson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,33 +21,124 @@ * THE SOFTWARE. */ +#include +#include +#include #include #include "finit.h" #include "helpers.h" #include "plugin.h" -static void save(void *arg) +/* Kernel RTC driver validates against this date for sanity check */ +#define RTC_TIMESTAMP_BEGIN_2000 946684800LL /* 2000-01-01 00:00:00 */ + +static int rtc_open(void) { - _d("Saving system clock to RTC ..."); - /* -w,--systohc, -u,--utc */ - run_interactive("hwclock -w -u", "Saving system time (UTC) to RTC"); + char *alt[] = { + "/dev/rtc", + "/dev/rtc0", + "/dev/misc/rtc" + }; + size_t i; + int fd; + + for (i = 0; i < NELEMS(alt); i++) { + fd = open(alt[i], O_RDONLY); + if (fd < 0) + continue; + + return fd; + } + + return -1; } -static void restore(void *arg) +static void rtc_save(void *arg) { - _d("Restoring system clock from RTC ..."); - /* -s,--hctosys, -u,--utc */ - run_interactive("hwclock -s -u", "Restoring system clock (UTC) from RTC"); + struct timeval tv = { 0 }; + struct tm tm = { 0 }; + int fd, rc = 0; + + fd = rtc_open(); + if (fd < 0) + return; + + rc = gettimeofday(&tv, NULL); + if (rc < 0 || tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) { + print_desc(NULL, "System clock invalid, not saving to RTC"); + invalid: + logit(LOG_ERR, "System clock invalid, before 2000-01-01 00:00, not saving to RTC"); + 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: + print(rc, NULL); + close(fd); +} + +static void rtc_restore(void *arg) +{ + struct timeval tv = { 0 }; + struct tm tm = { 0 }; + int fd, rc = 0; + + fd = rtc_open(); + if (fd < 0) + return; + + if (ioctl(fd, RTC_RD_TIME, &tm) < 0) { + char msg[120]; + + snprintf(msg, sizeof(msg), "Failed restoring system clock from RTC, %s", + EINVAL == errno ? "RTC time is too old" : + ENOENT == errno ? "RTC has no saved time" : "see log for details"); + print_desc(NULL, msg); + + invalid: + logit(LOG_ERR, "Failed restoring system clock from RTC."); + if (EINVAL == errno) + logit(LOG_ERR, "RTC time is too old (before 2000-01-01 00:00)"); + else if (ENOENT == errno) + logit(LOG_ERR, "RTC has no previously saved (valid) time."); + else + logit(LOG_ERR, "RTC error code %d: %m", errno); + rc = 2; + goto out; + } + + print_desc(NULL, "Restoring system clock (UTC) from RTC"); + tv.tv_sec = mktime(&tm); + if (tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) { + errno = EINVAL; + goto invalid; + } + + if (settimeofday(&tv, NULL) == -1) + rc = 1; + +out: + print(rc, NULL); + close(fd); } static plugin_t plugin = { .name = __FILE__, .hook[HOOK_BASEFS_UP] = { - .cb = restore + .cb = rtc_restore }, .hook[HOOK_SHUTDOWN] = { - .cb = save + .cb = rtc_save } };