From 88a3372e88c4ccd2714c3d95ef4719140436214a Mon Sep 17 00:00:00 2001 From: Scott James Remnant Date: Wed, 4 Mar 2009 17:10:43 +0000 Subject: [PATCH] hwclock: add --systz option to set system clock from itself Since the system clock time is already set from the hardware clock by the kernel (when compiled with CONFIG_RTC_HCTOSYS), 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. [kzak@redhat.com: - fix the condition that controls read_adjtime() call] Signed-off-by: Scott James Remnant Signed-off-by: Karel Zak --- hwclock/hwclock.8 | 19 ++++++++ hwclock/hwclock.c | 109 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 120 insertions(+), 8 deletions(-) diff --git a/hwclock/hwclock.8 b/hwclock/hwclock.8 index 31eda21f..7f0c53d9 100644 --- a/hwclock/hwclock.8 +++ b/hwclock/hwclock.8 @@ -54,6 +54,25 @@ 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. +.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 2749c0fc..11c606dd 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) { @@ -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,6 +1219,16 @@ 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) 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 based on the current timezone\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")); @@ -1515,7 +1607,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 +1621,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 +1658,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 */ -- 2.39.5