[PATCH] hwclock: add --systz option to set system clock from itself

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Since the system clock time is already set from the hardware clock by
the kernel, there's no particular need to read the hardware clock
again.

This option sets the system clock using itself as a reference if the
hardware clock was in local time.  The resulting system clock time
is in UTC, with the kernel timezone set to the difference.

Signed-off-by: Scott James Remnant <scott@xxxxxxxxxx>
---
 hwclock/hwclock.8 |   23 ++++++++++
 hwclock/hwclock.c |  124 ++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 137 insertions(+), 10 deletions(-)

diff --git a/hwclock/hwclock.8 b/hwclock/hwclock.8
index 31eda21..4a6258e 100644
--- a/hwclock/hwclock.8
+++ b/hwclock/hwclock.8
@@ -54,6 +54,29 @@ This is a good option to use in one of the system startup scripts.
 .B \-w, \-\-systohc
 Set the Hardware Clock to the current System Time.
 .TP
+.B \-\-systz
+Reset the System Time based on the current timezone.
+
+Also set the kernel's timezone value to the local timezone
+as indicated by the TZ environment variable and/or
+.IR /usr/share/zoneinfo ,
+as
+.BR tzset (3)
+would interpret them.
+The obsolete tz_dsttime field of the kernel's timezone value is set
+to DST_NONE. (For details on what this field used to mean, see
+.BR settimeofday (2).)
+
+This is an alternate option to
+.B \-\-hctosys
+that does not read the hardware clock, and may be used in system startup
+scripts for recent 2.6 kernels where you know the System Time contains
+the Hardware Clock time.  You must specify either
+.B \-\-utc
+or
+.B \-\-localtime
+to indicate whether an adjustment needs to be made.
+.TP
 .B \-\-adjust
 Add or subtract time from the Hardware Clock to account for systematic
 drift since the last time the clock was set or adjusted.  See discussion
diff --git a/hwclock/hwclock.c b/hwclock/hwclock.c
index 2749c0f..aeebb49 100644
--- a/hwclock/hwclock.c
+++ b/hwclock/hwclock.c
@@ -753,6 +753,82 @@ set_system_clock(const bool hclock_valid, const time_t newtime,
 }
 
 
+static int
+set_system_clock_timezone(const bool testing) {
+/*----------------------------------------------------------------------------
+   Reset the System Clock from local time to UTC, based on its current
+   value and the timezone.
+
+   Also set the kernel time zone value to the value indicated by the
+   TZ environment variable and/or /usr/lib/zoneinfo/, interpreted as
+   tzset() would interpret them.
+
+   If 'testing' is true, don't actually update anything -- just say we
+   would have.
+-----------------------------------------------------------------------------*/
+  int retcode;
+  struct timeval tv;
+  struct tm *broken;
+  int minuteswest;
+  int rc;
+
+  gettimeofday(&tv, NULL);
+  if (debug) {
+    struct tm broken_time;
+    char ctime_now[200];
+
+    broken_time = *gmtime(&tv.tv_sec);
+    strftime(ctime_now, sizeof(ctime_now), "%Y/%m/%d %H:%M:%S", &broken_time);
+    printf(_("Current system time: %ld = %s\n"), (long) tv.tv_sec, ctime_now);
+  }
+
+  broken = localtime(&tv.tv_sec);
+#ifdef HAVE_TM_GMTOFF
+  minuteswest = -broken->tm_gmtoff/60;		/* GNU extension */
+#else
+  minuteswest = timezone/60;
+  if (broken->tm_isdst)
+	  minuteswest -= 60;
+#endif
+
+  gettimeofday(&tv, NULL);
+  tv.tv_sec += minuteswest * 60;
+
+  if (debug) {
+    struct tm broken_time;
+    char ctime_now[200];
+
+    broken_time = *gmtime(&tv.tv_sec);
+    strftime(ctime_now, sizeof(ctime_now), "%Y/%m/%d %H:%M:%S", &broken_time);
+
+    printf(_("Calling settimeofday:\n"));
+    printf(_("\tUTC: %s\n"), ctime_now);
+    printf(_("\ttv.tv_sec = %ld, tv.tv_usec = %ld\n"),
+           (long) tv.tv_sec, (long) tv.tv_usec);
+    printf(_("\ttz.tz_minuteswest = %d\n"), minuteswest);
+  }
+  if (testing) {
+    printf(_("Not setting system clock because running in test mode.\n"));
+    retcode = 0;
+  } else {
+    const struct timezone tz = { minuteswest, 0 };
+
+    rc = settimeofday(&tv, &tz);
+    if (rc) {
+	    if (errno == EPERM) {
+		    fprintf(stderr,
+			    _("Must be superuser to set system clock.\n"));
+		    retcode = EX_NOPERM;
+	    } else {
+		    outsyserr(_("settimeofday() failed"));
+		    retcode = 1;
+	    }
+    } else retcode = 0;
+  }
+  return retcode;
+}
+
+
 static void
 adjust_drift_factor(struct adjtime *adjtime_p,
                     const time_t nowtime,
@@ -1045,7 +1121,7 @@ determine_clock_access_method(const bool user_requests_ISA) {
 static int
 manipulate_clock(const bool show, const bool adjust, const bool noadjfile,
                  const bool set, const time_t set_time,
-                 const bool hctosys, const bool systohc,
+		 const bool hctosys, const bool systohc, const bool systz,
                  const struct timeval startup_time,
                  const bool utc, const bool local_opt,
 		 const bool testing) {
@@ -1065,7 +1141,7 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile,
     if (no_auth)
 	  return EX_NOPERM;
 
-    if (!noadjfile && (adjust || set || systohc || (!utc && !local_opt))) {
+    if (!noadjfile && !systz && (adjust || set || systohc || (!utc && !local_opt))) {
       rc = read_adjtime(&adjtime);
       if (rc)
 	      return rc;
@@ -1099,7 +1175,7 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile,
              Defined only if hclock_valid is true.
              */
 
-       if (show || adjust || hctosys || !noadjfile) {
+       if (show || adjust || hctosys || (!noadjfile && !systz)) {
           /* data from HW-clock are required */
           rc = synchronize_to_clock_tick();
           if (rc && rc != 2)		/* 2= synchronization timeout */
@@ -1143,8 +1219,18 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile,
             printf(_("Unable to set system clock.\n"));
 	    return rc;
           }
+	} else if (systz) {
+	  if (!universal) {
+	    rc = set_system_clock_timezone(testing);
+	    if (rc) {
+	      printf(_("Unable to set system clock.\n"));
+	      return rc;
+	    }
+	  } else if (debug) {
+	    printf(_("Clock in UTC, not changed.\n"));
+	  }
         }
-        if (!noadjfile)
+        if (!noadjfile && !systz)
          save_adjtime(adjtime, testing);
       }
     }
@@ -1232,6 +1318,7 @@ usage( const char *fmt, ... ) {
 	"       --set          set the rtc to the time given with --date\n"
 	"  -s | --hctosys      set the system time from the hardware clock\n"
 	"  -w | --systohc      set the hardware clock to the current system time\n"
+	"       --systz        set the system time only\n"
 	"       --adjust       adjust the rtc to account for systematic drift since\n"
 	"                      the clock was last set or adjusted\n"
 	"       --getepoch     print out the kernel's hardware clock epoch value\n"
@@ -1305,6 +1392,7 @@ static const struct option longopts[] = {
 	{ "epoch", 1, 0, 137 },
 	{ "rtc", 1, 0, 'f' },
 	{ "adjfile", 1, 0, 138 },
+	{ "systz", 0, 0, 139 },
 	{ NULL, 0, 0, 0 }
 };
 
@@ -1330,7 +1418,7 @@ main(int argc, char **argv) {
 
 	/* Variables set by various options; show may also be set later */
 	/* The options debug, badyear and epoch_option are global */
-	bool show, set, systohc, hctosys, adjust, getepoch, setepoch;
+	bool show, set, systohc, hctosys, systz, adjust, getepoch, setepoch;
 	bool utc, testing, local_opt, noadjfile, directisa;
 	bool ARCconsole, Jensen, SRM, funky_toy;
 	char *date_opt;
@@ -1360,7 +1448,7 @@ main(int argc, char **argv) {
 	textdomain(PACKAGE);
 
 	/* Set option defaults */
-	show = set = systohc = hctosys = adjust = noadjfile = FALSE;
+	show = set = systohc = hctosys = systz = adjust = noadjfile = FALSE;
 	getepoch = setepoch = utc = local_opt = testing = debug = FALSE;
 	ARCconsole = Jensen = SRM = funky_toy = directisa = badyear = FALSE;
 	date_opt = NULL;
@@ -1433,6 +1521,9 @@ main(int argc, char **argv) {
 		case 138:
 			adj_file_name = optarg;		/* --adjfile */
 			break;
+		case 139:
+			systz = TRUE;			/* --systz */
+			break;
 		case 'f':
 			rtc_dev_name = optarg;		/* --rtc */
 			break;
@@ -1464,7 +1555,8 @@ main(int argc, char **argv) {
 		      MYNAME, argc);
 	}
 
-	if (show + set + systohc + hctosys + adjust + getepoch + setepoch > 1){
+	if (show + set + systohc + hctosys + systz + adjust + getepoch
+	    + setepoch > 1){
 		fprintf(stderr, _("You have specified multiple functions.\n"
 				  "You can only perform one function "
 				  "at a time.\n"));
@@ -1491,6 +1583,12 @@ main(int argc, char **argv) {
 				  "both.\n"), MYNAME);
 		hwclock_exit(EX_USAGE);
 	}
+	if (adj_file_name && systz) {
+		fprintf(stderr, _("%s: With --systz, the adjustment file "
+				  "is not used but you specified "
+				  " --adjfile.\n"), MYNAME);
+		hwclock_exit(EX_USAGE);
+	}
 	if (!adj_file_name)
 		adj_file_name = ADJPATH;
 
@@ -1499,6 +1597,11 @@ main(int argc, char **argv) {
 				  "either --utc or --localtime\n"), MYNAME);
 		hwclock_exit(EX_USAGE);
 	}
+	if (systz && !(utc || local_opt)) {
+		fprintf(stderr, _("%s: With --systz, you must specify "
+				  "either --utc or --localtime\n"), MYNAME);
+		hwclock_exit(EX_USAGE);
+	}
 
 #ifdef __alpha__
 	set_cmos_epoch(ARCconsole, SRM);
@@ -1515,7 +1618,8 @@ main(int argc, char **argv) {
 		}
 	}
 
-	if (!(show | set | systohc | hctosys | adjust | getepoch | setepoch))
+	if (!(show | set | systohc | hctosys | systz | adjust | getepoch
+	      | setepoch))
 		show = 1; /* default to show */
 
 
@@ -1528,7 +1632,7 @@ main(int argc, char **argv) {
 				_("Sorry, only the superuser can change "
 				  "the Hardware Clock.\n"));
 			permitted = FALSE;
-		} else if (hctosys) {
+		} else if (systz || hctosys) {
 			fprintf(stderr,
 				_("Sorry, only the superuser can change "
 				  "the System Clock.\n"));
@@ -1565,7 +1669,7 @@ main(int argc, char **argv) {
 	}
 
 	rc = manipulate_clock(show, adjust, noadjfile, set, set_time,
-				hctosys, systohc, startup_time, utc,
+				hctosys, systohc, systz, startup_time, utc,
 				local_opt, testing);
 	hwclock_exit(rc);
 	return rc;	/* Not reached */
-- 
1.6.0.5

--
To unsubscribe from this list: send the line "unsubscribe util-linux-ng" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux