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

Bug#66571: hwclock: [FIX] Here's a possible fix for #50572, #60734



Package: util-linux
Version: 2.10f-5.1
Severity: wishlist

Reported as a wishlist, because this needs some maintainer elbow-grease to
be applied :-)


Here's a strategy plan (plus patches) to address
#50572 (hwclock.sh runs too late), #60734 (hwclock starts too late). Someone
could merge these two bugs, BTW.

CCed to debian-devel because #60734 was considered RC by the submitter, and
downgraded without a fix because one is not exactly easy. I don't know if
this kind of change is a good idea during deep freeze, but here it is
anyway.

CCed to original bug submitters because I don't have an Alpha, and therefore
I may have misunderstood the problem.

This fix requires changes to the package, which I have not included because:
1. I'm not a developer, nor am I well versed in the packaging system 
2. I didn't understand some suspicious stuff in debian/rules regarding
   update-rc.d -f, the whole rm -f of /etc/rc?.d stuff and the lack of a
   update-rc.d remove in postrm.

WARNING: I did some very, very light testing with this, but as my clock is a
reasonably well behaved RTC which I keep in UTC, I don't even need hwclock
to begin with... so you'd better have a look at the scripts and look for
anything I missed.


The Issues: 

  - Machines with the hardware clock set to local time or with weird
  hardware clocks (some Alphas?) have problems with the current way things
  are done (see #60734, #50572), so this bug must be addressed sooner or
  later.

  - modutils rc script causes problems if started before the system clock is
  fixed (due to depmod -a) in certain configurations. This issue comes up
  every once in a while in the mailinglists, so it should be fixed.

  - modutils rc script must be started before mounting all filesystems,
  as modules might be needed to do the mount.

  - hwclock needs either read-only access to the timezone file or TZ set, if
  it is to know what to do with a local-time hardware clock. If the timezone
  file cannot be read before mounting all partitions, TZ is the only possible
  workaround.

  - hwclock needs read-write access to the /etc and /var filesystems if it
  is to run without the --noadjfile (see included patch) or needs to update
  the --badyear (/var/lib/lastdate) file.


The Fix Strategy:

  - Proper documentation and warnings added everywhere in the hwclock
  scripts and README.Debian.hwclock. I would suggest adding them to wherever
  local-time hardware-clocks are discussed in the release notes and install
  guides too.  Patches attached.

  - Patch to hwclock (--noadjfile option which works with /etc read-only
  without causing errors) to guarantee it can operate early in the boot
  process.  This is also useful if you use ntp and don't want hwclock ever
  creating /etc/adjtime file because it is useless. Patch attached, should
  be forwarded upstream as well.

  - New dual rc-file strategy to set the clock: do a preliminary set of the
  system clock early in rcS.d (S19), do any optional fancy adjusts later at
  rcS.d S50 (where hwclock.sh is right now).  If your hardware clock is in
  UTC, and your problem with the current method was the need for hwclock to
  do strange corrections to whatever it reads from your hardware clock (some
  Alphas? and PCs with broken RTCs/BIOS) then this solves your problem.
  Patch attached.

  - Even with the dual rc-file strategy, if you have your hardware clock in
  local time, you either have to manually set TZ in the hwclockset.sh
  script, or to insure that /etc/localtime is readable upon rc.S S19. The rc
  script will detect if it cannot read /etc/localtime and TZ is not set, and
  abort with an error.


Modifications needed in the util-linux package (NOT included):

  - hwclock.sh (/etc/init.d script) must be "update-rc.d -f remove"'d
    and removed from the package
  - conffile hwclockset.sh (/etc/init.d script) must be added, and 
    installed with "update-rc.d hwclockset.sh start 19 S . stop 25 0 6 ."
    Proper removal on package purge must be done as well, obviously.
  - conffile hwclockadj.sh (/etc/init.d script) must be added, and 
    installed with "update-rc.d hwclockadj.sh start 50 S . stop 25 0 6 ."
    Proper removal on package purge must be done as well, obviously.


Possible drawbacks:

  - User has the RTC in UTC: none. There is no need to edit anything.

  - User has the RTC in localtime: If the user has /usr as a separate
  partition, he'll have to edit the hwclockset.sh script to correctly set
  the TZ variable. This might cause problems with daylight saving time and
  forgetful users.

  - I didn't fix the problem for people needing --badyear at S19. I'm
  not even sure someone would need it at S19, maybe S50 is good enough.
  To fix, patch hwclock to read the year from somewhere in /etc instead
  of /var.

-- System Information
Debian Release: 2.2
Architecture: i386
Kernel: Linux godzillah.rivendell.sol 2.2.17pre9 #1 Wed Jun 28 20:38:34 BRT 2000 i586

Versions of packages util-linux depends on:
ii  libc6                         2.1.3-10   GNU C Library: Shared libraries an
ii  libncurses5                   5.0-6      Shared libraries for terminal hand
ii  slang1                        1.3.9-1    The S-Lang programming library - r

-- Configuration Files:
/etc/fdprm [Errno 2] No such file or directory: '/etc/fdprm'
/etc/init.d/hwclock.sh [Errno 2] No such file or directory: '/etc/init.d/hwclock.sh'

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh
--- ../util-linux-2.10f/debian/README.Debian.hwclock	Wed Jun 28 11:10:03 2000
+++ README.Debian.hwclock	Fri Jun 30 23:59:28 2000
@@ -63,3 +63,27 @@
 Don't use the hwclock --adjust facility, refer to alternate (and much safer)
 programs such as ntp, chrony or adjtimex if you need precision timekeeping.
 
+
+Debian's /etc/init.d/hwclock* init scripts:
+
+There are two init scripts. The first one, /etc/init.d/hwclockset.sh, is 
+run very early in the boot process, at a time where only /bin, /sbin and 
+/etc are guaranteed to be available (and they will most likely be 
+read-only). This script is used to guarantee that a reasonably sane 
+system clock time is available to other early-starting scrits, such as 
+the one for modutils.
+
+The second init script, hwclockadj.sh, runs after all filesystems were
+mounted. You are free to use hwclock options which do require write access
+(such as --adjust, --set).
+
+If you need any of the workaround options (--directisa, any of the
+Alpha-only options) you probably will need to edit and add them to *BOTH*
+init scripts.
+
+
+Hardware clocks in local (not UTC/GMT) time:
+
+If your hardware clock is in local time, you may need to manually set the
+timezone in /etc/init.d/hwclockset.sh. The script will print a warning
+message if such configuration is needed.
--- ../util-linux-2.10f/clock/hwclock.8	Wed Jun 28 11:10:03 2000
+++ hwclock.8	Fri Jun 30 23:47:20 2000
@@ -20,7 +20,7 @@
 .PP
 other options:
 .PP
-.B "\-\-utc  \-\-localtime \-\-directisa \-\-test \-\-debug"
+.B "\-\-utc  \-\-localtime \-\-noadjfile \-\-directisa \-\-test \-\-debug"
 .PP
 and arcane options for DEC Alpha:
 .PP
@@ -168,6 +168,17 @@
 .B \-\-adjust
 options), as recorded in the adjtime file.  If the adjtime file doesn't
 exist, the default is local time.
+
+.TP
+.B \-\-noadjfile
+disables the facilities provided by /etc/adjtime. 
+.B hwclock 
+will not read nor write to that file with this option, which is helpful 
+if you have to start it early in the boot process. Either 
+.B \-\-utc 
+or 
+.B \-\-localtime
+must be specified when using this option.
 
 .TP
 .B \-\-directisa
--- hwclock.c.orig	Sun Jun 25 09:22:13 2000
+++ hwclock.c	Sun Jun 25 13:37:52 2000
@@ -923,7 +923,8 @@
 }
 
 static void
-manipulate_clock(const bool show, const bool adjust, 
+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 struct timeval startup_time, 
@@ -945,7 +946,7 @@
 
   if (no_auth) *retcode_p = 1;
   else {
-    if (adjust || set || systohc || (!utc && !local_opt)) 
+    if (!noadjfile && (adjust || set || systohc || (!utc && !local_opt))) 
       read_adjtime(&adjtime, &rc);
     else {
       /* A little trick to avoid reading the file if we don't have to */
@@ -1017,7 +1018,9 @@
             *retcode_p = 1;
           } else *retcode_p = 0;
         }
-        save_adjtime(adjtime, testing);
+        if (!noadjfile) {
+	  save_adjtime(adjtime, testing);
+	}
       }
     }
   }
@@ -1107,6 +1110,8 @@
     "  --date        specifies the time to which to set the hardware clock\n"
     "  --epoch=year  specifies the year which is the beginning of the \n"
     "                hardware clock's epoch value\n"
+    "  --noadjfile   do not access /etc/adjtime. Requires the use of\n"
+    "                either --utc or --localtime\n"
     ));
 #ifdef __alpha__
   fprintf( usageto, _(
@@ -1146,7 +1151,7 @@
      may be modified after parsing is complete to effect an implied option.
      */
   bool help, show, set, systohc, hctosys, adjust, getepoch, setepoch, version;
-  bool ARCconsole, utc, testing, directisa, Jensen, SRM, funky_toy;
+  bool ARCconsole, utc, testing, directisa, Jensen, SRM, funky_toy, noadjfile;
   bool local_opt;
   char *date_opt;
   int epoch_opt;
@@ -1160,6 +1165,7 @@
     { 0,   (char *) "getepoch",  OPT_FLAG,   &getepoch,  0 },
     { 0,   (char *) "setepoch",  OPT_FLAG,   &setepoch,  0 },
     { 'a', (char *) "adjust",    OPT_FLAG,   &adjust,    0 },
+    { 0,   (char *) "noadjfile", OPT_FLAG,   &noadjfile, 0 },
     { 'v', (char *) "version",   OPT_FLAG,   &version,   0 },
     { 'V', (char *) "version",   OPT_FLAG,   &version,   0 },
     { 0,   (char *) "date",      OPT_STRING, &date_opt,  0 },
@@ -1188,8 +1194,8 @@
   textdomain(PACKAGE);
 
   /* set option defaults */
-  help = show = set = systohc = hctosys = adjust = getepoch = setepoch = 
-    version = utc = local_opt = ARCconsole = SRM = funky_toy =
+  help = show = set = systohc = hctosys = adjust = noadjfile = getepoch = 
+    setepoch = version = utc = local_opt = ARCconsole = SRM = funky_toy =
     directisa = badyear = Jensen = testing = debug = FALSE;
   date_opt = NULL;
   epoch_opt = -1; 
@@ -1198,8 +1204,8 @@
   optParseOptions(&argc_parse, argv_parse, option_def, 0);
     /* Uses and sets argc_parse, argv_parse. 
        Sets show, systohc, hctosys, adjust, utc, local_opt, version,
-       testing, debug, set, date_opt, getepoch, setepoch, epoch_opt
-       */
+       testing, debug, set, date_opt, getepoch, setepoch, epoch_opt,
+       noadjtime */
     /* This is an ugly routine - for example, if I give an incorrect
        option, it only says "unrecognized option" without telling
        me what options are recognized. Rewrite with standard
@@ -1227,6 +1233,18 @@
     exit(100);
   }
 
+  if (adjust && noadjfile) {
+    fprintf(stderr, _("%s: The --adjust and --noadjfile options are mutually "
+		      "exclusive.  You specified both.\n"), MYNAME);
+    exit(100);
+  }
+
+  if (noadjfile && !(utc || local_opt)) {
+    fprintf(stderr, _("%s: With --noadjfile, you must specify either "
+		      "--utc or --localtime\n"), MYNAME);
+    exit(100);
+  }
+   
 #ifdef __alpha__
   set_cmos_epoch(ARCconsole, SRM);
   set_cmos_access(Jensen, funky_toy);
@@ -1276,8 +1294,8 @@
                 "method.  Use --debug option to see the details of our "
                 "search for an access method.\n"));
       else
-        manipulate_clock(show, adjust, set, set_time, hctosys, systohc, 
-                         startup_time, utc, local_opt, testing, &rc);
+        manipulate_clock(show, adjust, noadjfile, set, set_time, hctosys, 
+		       systohc, startup_time, utc, local_opt, testing, &rc);
     }
   }
   exit(retcode);
#!/bin/sh
# hwclockset.sh	Set system clock to hardware clock, according to the UTC
#		setting in /etc/default/rcS (see also rcS(5)).
#
# 		This script is intended to run very early in the boot
#               process. If you want fancy clock adjustment, do that
#               elsewhere.
#
# WARNING:	Runs without write permission on /etc, and before
#		mounting all filesystems! If you need write permission 
#		to do something, do it in hwclockadj.sh.
#
# WARNING:      If your hardware clock is not in UTC/GMT, this script
#	 	must know the local time zone. This information is
#		stored in /etc/localtime. This might be a problem if 
#		your /etc/localtime is a symlink to something in 
#		/usr/share/zoneinfo AND /usr isn't in the root 
#		partition! The workaround is to define TZ either
#		in /etc/default/rcS, or in the proper place below.
#
# REMEMBER TO EDIT hwclockadj.sh AS WELL!

# Set this to any options you might need to give to hwclock, such
# as machine hardware clock type for Alphas.
HWCLOCKPARS=

[ ! -x /sbin/hwclock ] && exit 0

. /etc/default/rcS

# Define TZ to the desired timezone here if you need it.
# You could also define it in /etc/default/rcS.
# see the manpage tzset(3) for how to define TZ.
# WARNING: TZ takes precedence over /etc/localtime !
# TZ=

[ "$GMT" = "-u" ] && UTC="yes"
case "$UTC" in
       no|"") GMT="--localtime"
              UTC=""
	      if [ ! -r /etc/localtime -a -z "$TZ" ]
	      then
	      		echo
	      	       	echo "$0: Hardware clock time zone could not be determined." >&2
			echo "$0: WARNING: System clock was not updated at this time." >&2
			echo
			exit 1
	      fi
	      ;;
       yes)   GMT="--utc" 
              UTC="--utc"
	      ;;
       *)     echo "$0: Unknown UTC setting: \"$UTC\"" >&2 
              exit 1
	      ;;
esac


case "$1" in
	start)
		if [ "$VERBOSE" != no ] 
		then
			echo
			echo "System time was `date --utc`."
			echo "Setting the System Clock using the Hardware Clock as reference..."
		fi

		# Copies Hardware Clock time to System Clock using the correct
		# timezone for hardware clocks in local time, and sets kernel 
		# timezone. DO NOT REMOVE.
		TZ="${TZ}" hwclock --noadjfile --hctosys $GMT $HWCLOCKPARS
		
		if [ "$VERBOSE" != no ]
		then
			echo "System Clock set. System local time is now `date $UTC`."
		fi
		;;
	stop|restart|reload)
		# Does nothing
		exit 0
		;;
	*)
		echo "Usage: hwclockset.sh {start|stop|reload}" >&2
		echo "       start sets kernel (system) clock from hardware (RTC) clock" >&2
		echo "       stop and reload do nothing." >&2
		echo "       Refer to hwclockadj.sh as well." >&2
		exit 1
		;;
esac
#!/bin/sh
# hwclockadj.sh	Adjusts the system and hardware clocks, according to the 
#               UTC setting in /etc/default/rcS (see also rcS(5)).
#

# WARNING:	Please read /usr/doc/util-linux/README.Debian.hwclock
#		before changing this file. You risk serious clock
#		misbehaviour otherwise.

# WARNING:      hwclockset.sh runs before this one during startup,
#		and might have adjusted the system time already.
#
# REMEMBER TO EDIT hwclockset.sh AS WELL!


# Uncomment the HWCLOCKADJ=yes line below if you want hwclock to try 
# to correct systematic drift errors in the Hardware Clock.
#
# WARNING: If you uncomment this option, you must either make sure 
# *nothing* changes the Hardware Clock other than hwclock --systohc, 
# or you must delete /etc/adjtime every time someone else modifies 
# the Hardware Clock.
#
# Common "vilains" are: ntp, MS Windows, the BIOS Setup program. 
#
# WARNING: You must remember to invalidate (delete) /etc/adjtime if you 
# ever need to set the system clock to a very different value and 
# hwclock --adjust is being used.
#
# Please read /usr/doc/util-linux/README.Debian.hwclock before enabling 
# this option.
#
# HWCLOCKADJ=yes


# Set this to any options you might need to give to hwclock, such
# as machine hardware clock type for Alphas.
HWCLOCKPARS=

[ ! -x /sbin/hwclock ] && exit 0

. /etc/default/rcS
[ "$GMT" = "-u" ] && UTC="yes"
case "$UTC" in
       no|"") GMT="--localtime"
              UTC=""
	      ;;
       yes)   GMT="--utc" 
              UTC="--utc"
	      ;;
       *)     echo "$0: unknown UTC setting: \"$UTC\"" >&2 
              exit 1
	      ;;
esac


case "$1" in
	start)
		if [ ! -f /etc/adjtime ]
		then
			echo "0.0 0 0.0" > /etc/adjtime
		fi

		if [ "$HWCLOCKADJ" == "yes" ]
		then
			if [ "$VERBOSE" != no ]
			then
				echo "Compensating for Hardware Clock systematic drift..."
			fi
			hwclock --adjust $GMT $HWCLOCKPARS
			hwclock --hctosys $GMT $HWCLOCKPARS
			#
			#	Now that /usr/lib/zoneinfo should be available,
			#	announce the local time.
			#
			if [ "$VERBOSE" != no ]
			then
				echo "System Clock updated. Local time is now: `date`"
				echo
			fi
		fi
		;;
	stop|restart|reload)
		#
		# Updates the Hardware Clock with the System Clock time.
		# This will *override* any changes made to the Hardware Clock.
		#
		# WARNING: If you disable this, any changes to the system
		#          clock will not be carried across reboots.
		#
		if [ "$VERBOSE" != no ]
		then
			echo "Saving the System Clock time to the Hardware Clock..."
		fi
		hwclock --systohc $GMT
		if [ "$VERBOSE" != no ]
		then
			echo "Hardware Clock updated to `date $UTC`."
		fi
		;;
	show)
		hwclock --show $GMT
		;;
	*)
		echo "Usage: hwclockadj.sh {start|stop|reload|show}" >&2
		echo "       start sets kernel (system) clock from hardware (RTC) clock" >&2
		echo "       stop and reload set hardware (RTC) clock from kernel (system) clock" >&2
		exit 1
		;;
esac


Reply to: