From: Noé Rubinstein <noe.rubinstein@xxxxxxxxx> This option is useful for systems that have a working hardware clock, but do not provide a way to write to it. Instead of setting the hardware clock, write the difference between hardware and system clock to a file; when getting the hardware clock, add the offset from this file to obtain the correct time. I have not checked how well this interacts with the adjtime mechanism, so this is an RFC patch. --- sys-utils/hwclock.8.in | 6 ++++ sys-utils/hwclock.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/sys-utils/hwclock.8.in b/sys-utils/hwclock.8.in index b11b45c..63fabda 100644 --- a/sys-utils/hwclock.8.in +++ b/sys-utils/hwclock.8.in @@ -113,6 +113,12 @@ not reset. .BR \-w , \ \-\-systohc Set the Hardware Clock to the current System Time. .TP +.BR \-\-offset[=filename] +This option is useful for systems that have a working hardware clock, but do +not provide a way to write to it. Instead of setting the hardware clock, write +the difference between hardware and system clock to a file; when getting the +hardware clock, add the offset from this file to obtain the correct time. +.TP .BR \-V , \ \-\-version Display version information and exit. .TP diff --git a/sys-utils/hwclock.c b/sys-utils/hwclock.c index e780158..c7deab9 100644 --- a/sys-utils/hwclock.c +++ b/sys-utils/hwclock.c @@ -98,6 +98,7 @@ struct clock_ops *ur; #define MAX_DRIFT 2145.0 const char *adj_file_name = NULL; +const char *offset_file_name = NULL; struct adjtime { /* @@ -234,6 +235,35 @@ hw_clock_is_utc(const bool utc, const bool local_opt, return ret; } +static int read_offset(struct timeval *offset) +{ + FILE *f; + int rc; + f = fopen(offset_file_name, "r"); + if (!f) + return -errno; + + rc = fread(offset, sizeof(*offset), 1, f); + fclose(f); + + return rc == 1 ? 0 : -EINVAL; +} + +static int write_offset(struct timeval offset) +{ + FILE *f; + int rc; + f = fopen(offset_file_name, "w"); + if (!f) + return -errno; + + rc = fwrite(&offset, sizeof(offset), 1, f); + fclose(f); + + return rc == 1 ? 0 : -EINVAL; +} + + /* * Read the adjustment parameters out of the /etc/adjtime file. * @@ -1290,6 +1320,8 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile, bool no_auth; /* The time at which we read the Hardware Clock */ struct timeval read_time; + /* The offset between system time and hardware time */ + struct timeval offset = {0}; /* * The Hardware Clock gives us a valid time, or at * least something close enough to fool mktime(). @@ -1320,6 +1352,11 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile, adjtime.dirty = FALSE; } + if ((hctosys || show) && offset_file_name) + { + read_offset(&offset); + } + universal = hw_clock_is_utc(utc, local_opt, adjtime); if ((set || systohc || adjust) && @@ -1328,7 +1365,8 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile, adjtime.dirty = TRUE; } - if (show || adjust || hctosys || (!noadjfile && !systz && !predict)) { + if (show || adjust || hctosys || (!noadjfile && !systz && !predict) || + offset_file_name) { /* data from HW-clock are required */ rc = synchronize_to_clock_tick(); @@ -1358,8 +1396,20 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile, } if (show) { - display_time(hclock_valid, hclocktime, + display_time(hclock_valid, hclocktime + offset.tv_sec, time_diff(read_time, startup_time)); + } else if (set && offset_file_name) { + if (!hclock_valid) { + printf(_("Hardware clock invalid, not writing offset.\n")); + } else { + offset.tv_sec = set_time - hclocktime; + rc = write_offset(offset); + if (rc) { + printf(_("Unable to write offset: %s."), strerror(-rc)); + return rc; + } + } + } else if (set) { set_hardware_clock_exact(set_time, startup_time, universal, testing); @@ -1371,6 +1421,18 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile, } else if (adjust) { do_adjustment(&adjtime, hclock_valid, hclocktime, read_time, universal, testing); + } else if (systohc && offset_file_name) { + if (!hclock_valid) { + printf(_("Hardware clock invalid, not writing offset.\n")); + } else { + gettimeofday(&offset, NULL); + offset.tv_sec -= hclocktime; + rc = write_offset(offset); + if (rc) { + printf(_("Unable to write offset: %s."), strerror(-rc)); + return rc; + } + } } else if (systohc) { struct timeval nowtime, reftime; /* @@ -1390,6 +1452,17 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile, reftime.tv_sec, hclock_valid, hclocktime, (double) read_time.tv_usec / 1E6); + } else if (hctosys && offset_file_name) { + if (!hclock_valid) { + printf(_("Hardware clock invalid, not setting time.\n")); + } else { + offset.tv_sec += hclocktime; + rc = settimeofday(&offset, NULL); + if (rc) { + printf(_("Unable to set system clock.\n")); + return rc; + } + } } else if (hctosys) { rc = set_system_clock(hclock_valid, hclocktime, testing); if (rc) { @@ -1547,6 +1620,7 @@ static void out_version(void) printf(UTIL_LINUX_VERSION); } +#define DEFAULT_OFFSET_FILE "/etc/time_offset" /* * usage - Output (error and) usage information * @@ -1577,6 +1651,10 @@ static void usage(const char *fmt, ...) " --adjust adjust the RTC to account for systematic drift since\n" " the clock was last set or adjusted\n"), usageto); fputs(_(" -c, --compare periodically compare the system clock with the CMOS clock\n"), usageto); + fprintf(usageto, + _(" --offset [file] instead of setting the clock, write an offset to file.\n" + " when reading the time, add this offset to the hardware time.\n" + " (default: %s)\n"), DEFAULT_OFFSET_FILE); #ifdef __linux__ fputs(_(" --getepoch print out the kernel's hardware clock epoch value\n" " --setepoch set the kernel's hardware clock epoch value to the \n" @@ -1653,6 +1731,7 @@ int main(int argc, char **argv) /* Long only options. */ enum { OPT_ADJFILE = CHAR_MAX + 1, + OPT_OFFSET, OPT_BADYEAR, OPT_DATE, OPT_DIRECTISA, @@ -1698,6 +1777,7 @@ int main(int argc, char **argv) {"test", 0, 0, OPT_TEST}, {"date", 1, 0, OPT_DATE}, {"epoch", 1, 0, OPT_EPOCH}, + {"offset", 2, 0, OPT_OFFSET}, #ifdef __linux__ {"rtc", 1, 0, 'f'}, #endif @@ -1837,6 +1917,10 @@ int main(int argc, char **argv) case OPT_PREDICT_HC: predict = TRUE; /* --predict-hc */ break; + case OPT_OFFSET: + offset_file_name = /* --offset */ + optarg ? optarg : DEFAULT_OFFSET_FILE; + break; #ifdef __linux__ case 'f': rtc_dev_name = optarg; /* --rtc */ -- 2.1.0 -- To unsubscribe from this list: send the line "unsubscribe util-linux" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html