]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
rtc: restore time from driftfile if later than RTC time
authorMiroslav Lichvar <mlichvar@redhat.com>
Tue, 6 Oct 2015 13:52:36 +0000 (15:52 +0200)
committerMiroslav Lichvar <mlichvar@redhat.com>
Tue, 6 Oct 2015 13:52:36 +0000 (15:52 +0200)
This is useful on computers that have an RTC, but there is no battery to
keep the time when they are turned off and start with the same time on
each boot.

chrony.texi.in
chronyd.8.in
rtc.c
rtc_linux.c
rtc_linux.h

index e3b0ce911df1332c5395e5a5884a99b3ce9f5782..fe8c1a407ba37f8b8f176423fdce8b7d3959f4cf 100644 (file)
@@ -972,13 +972,9 @@ This option is useful when restarting @code{chronyd} and can be used
 in conjunction with the `-r' option.
 
 @item -s
-This option will set the system clock from the computer's real-time
-clock.  This is analogous to supplying the `-s' flag to the
-@file{/sbin/hwclock} program during the Linux boot sequence.
-
-Support for real-time clocks is limited at present - the criteria are
-described in the section on the @code{rtcfile} directive (@pxref{rtcfile
-directive}).
+This option will set the system clock from the computer's real-time clock or
+to the last modification time of the file specified by the @code{driftfile}
+directive.  Real-time clocks are supported only on Linux.
 
 If used in conjunction with the `-r' flag, @code{chronyd} will attempt
 to preserve the old samples after setting the system clock from the real
@@ -989,11 +985,10 @@ to work well, it relies on @code{chronyd} having been able to determine
 accurate statistics for the difference between the RTC and
 system clock last time the computer was on.
 
-If @code{chronyd} doesn't support the RTC on your computer or there is no RTC
-installed, the system clock will be set with this option forward to the time of
-the last modification of the drift file (specified by the @code{driftfile}
-directive) to restore the system time at which @code{chronyd} was previously
-stopped.
+If the last modification time of the drift file is later than the current time
+and the RTC time, the system time will be set to it to restore the time when
+@code{chronyd} was previously stopped.  This is useful on computers that have
+no RTC or the RTC is broken (e.g. it has no battery).
 @item -u <user>
 This option sets the name of the system user to which @code{chronyd} will
 switch after start in order to drop root privileges.  It overrides the
index 0ac294184e00de9ff41141b0062cae9ff0a6d400..b5b0882c066d46bce4ccdb42b6d139ec9465e845 100644 (file)
@@ -80,13 +80,9 @@ option is useful when restarting \fBchronyd\fR and can be used in conjunction
 with the \fB-r\fR option.
 .TP
 .B \-s
-This option will set the system clock from the computer's real-time
-clock.  This is analogous to supplying the \fI-s\fR flag to the
-\fI/sbin/hwclock\fR program during the Linux boot sequence.
-
-Support for real-time clocks is limited at present - the criteria
-are described in the section on the \fIrtcfile\fR directive in the
-documentation supplied with the distribution.
+This option will set the system clock from the computer's real-time clock or
+to the last modification time of the file specified by the \fIdriftfile\fR
+directive.  Real-time clocks are supported only on Linux.
 
 If used in conjunction with the \fB-r\fR flag, \fBchronyd\fR will attempt
 to preserve the old samples after setting the system clock from
@@ -97,11 +93,10 @@ not in use.  For this to work well, it relies on \fBchronyd\fR having
 been able to determine accurate statistics for the difference
 between the RTC and system clock last time the computer was on.
 
-If \fBchronyd\fR doesn't support the RTC on your computer or there is no RTC
-installed, the system clock will be set with this option forward to the time of
-the last modification of the drift file (specified by the \fIdriftfile\fR
-directive) to restore the system time at which \fBchronyd\fR was previously
-stopped.
+If the last modification time of the drift file is later than the current time
+and the RTC time, the system time will be set to it to restore the time when
+\fBchronyd\fR was previously stopped.  This is useful on computers that have no
+RTC or the RTC is broken (e.g. it has no battery).
 .TP
 \fB\-u\fR \fIuser\fR
 This option sets the name of the system user to which \fBchronyd\fR will switch
diff --git a/rtc.c b/rtc.c
index 68da13e8b849f01d3476ba8cd299239a04668568..8d076c66f198680d2fd0817312d6e67fa93162d5 100644 (file)
--- a/rtc.c
+++ b/rtc.c
@@ -44,7 +44,7 @@ static int driver_preinit_ok = 0;
 static struct {
   int  (*init)(void);
   void (*fini)(void);
-  int  (*time_pre_init)(void);
+  int  (*time_pre_init)(time_t driftfile_time);
   void (*time_init)(void (*after_hook)(void*), void *anything);
   void (*start_measurements)(void);
   int  (*write_parameters)(void);
@@ -74,29 +74,37 @@ static struct {
 };
      
 /* ================================================== */
-/* Set the system clock to the time of last modification of driftfile
-   if it's in the future */
+/* Get the last modification time of the driftfile */
 
-static void
-fallback_time_init(void)
+static time_t
+get_driftfile_time(void)
 {
-  struct timeval now;
   struct stat buf;
   char *drift_file;
 
   drift_file = CNF_GetDriftFile();
   if (!drift_file)
-    return;
+    return 0;
 
   if (stat(drift_file, &buf))
-    return;
+    return 0;
+
+  return buf.st_mtime;
+}
+
+/* ================================================== */
+/* Set the system time to the driftfile time if it's in the future */
+
+static void
+apply_driftfile_time(time_t t)
+{
+  struct timeval now;
 
   LCL_ReadCookedTime(&now, NULL);
 
-  if (now.tv_sec < buf.st_mtime) {
-    if (LCL_ApplyStepOffset(now.tv_sec - buf.st_mtime))
-      LOG(LOGS_INFO, LOGF_Rtc, "System clock set from driftfile %s",
-          drift_file);
+  if (now.tv_sec < t) {
+    if (LCL_ApplyStepOffset(now.tv_sec - t))
+      LOG(LOGS_INFO, LOGF_Rtc, "System time restored from driftfile");
   }
 }
 
@@ -105,18 +113,24 @@ fallback_time_init(void)
 void
 RTC_Initialise(int initial_set)
 {
+  time_t driftfile_time;
   char *file_name;
 
-  /* Do an initial read of the RTC and set the system time to it.  This
-     is analogous to what /sbin/hwclock -s would do on Linux.  If that fails
-     or RTC is not supported, set the clock to the time of the last
-     modification of driftfile, so we at least get closer to the truth. */
+  /* If the -s option was specified, try to do an initial read of the RTC and
+     set the system time to it.  Also, read the last modification time of the
+     driftfile (i.e. system time when chronyd was previously stopped) and set
+     the system time to it if it's in the future to bring the clock closer to
+     the true time when the RTC is broken (e.g. it has no battery), is missing,
+     or there is no RTC driver. */
   if (initial_set) {
-    if (driver.time_pre_init && driver.time_pre_init()) {
+    driftfile_time = get_driftfile_time();
+
+    if (driver.time_pre_init && driver.time_pre_init(driftfile_time)) {
       driver_preinit_ok = 1;
     } else {
       driver_preinit_ok = 0;
-      fallback_time_init();
+      if (driftfile_time)
+        apply_driftfile_time(driftfile_time);
     }
   }
 
index 16f77c4334c22138f8ccdb3c72ec300f34218d74..8c33f153bb571b26984be30c8ac832366a8aa4e1 100644 (file)
@@ -974,7 +974,7 @@ RTC_Linux_WriteParameters(void)
    RTC behaviour than we do for the rest of the module. */
 
 int
-RTC_Linux_TimePreInit(void)
+RTC_Linux_TimePreInit(time_t driftfile_time)
 {
   int fd, status;
   struct rtc_time rtc_raw, rtc_raw_retry;
@@ -1039,6 +1039,11 @@ RTC_Linux_TimePreInit(void)
 
       UTI_AddDoubleToTimeval(&new_sys_time, -accumulated_error, &new_sys_time);
 
+      if (new_sys_time.tv_sec < driftfile_time) {
+        LOG(LOGS_WARN, LOGF_RtcLinux, "RTC time before last driftfile modification (ignored)");
+        return 0;
+      }
+
       UTI_DiffTimevalsToDouble(&sys_offset, &old_sys_time, &new_sys_time);
 
       /* Set system time only if the step is larger than 1 second */
index 8cf23a62d17417e4da654b54fe9f8115c7561e6b..fa33ef16155ac5a66b8095844c481fccb01d4c7f 100644 (file)
@@ -30,7 +30,7 @@
 
 extern int RTC_Linux_Initialise(void);
 extern void RTC_Linux_Finalise(void);
-extern int RTC_Linux_TimePreInit(void);
+extern int RTC_Linux_TimePreInit(time_t driftile_time);
 extern void RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything);
 extern void RTC_Linux_StartMeasurements(void);