summaryrefslogtreecommitdiff
path: root/hwclock
diff options
context:
space:
mode:
Diffstat (limited to 'hwclock')
-rw-r--r--hwclock/Makefile.am14
-rw-r--r--hwclock/Makefile.in664
-rw-r--r--hwclock/README.hwclock27
-rw-r--r--hwclock/clock-ppc.c459
-rw-r--r--hwclock/clock.h53
-rw-r--r--hwclock/cmos.c614
-rw-r--r--hwclock/hwclock.8628
-rw-r--r--hwclock/hwclock.c1829
-rw-r--r--hwclock/kd.c186
-rw-r--r--hwclock/rtc.c477
10 files changed, 4951 insertions, 0 deletions
diff --git a/hwclock/Makefile.am b/hwclock/Makefile.am
new file mode 100644
index 0000000..542521c
--- /dev/null
+++ b/hwclock/Makefile.am
@@ -0,0 +1,14 @@
+include $(top_srcdir)/config/include-Makefile.am
+
+dist_man_MANS = hwclock.8
+
+sbin_PROGRAMS = hwclock
+
+hwclock_SOURCES = hwclock.c cmos.c rtc.c kd.c clock.h
+hwclock_LDADD =
+
+if HAVE_AUDIT
+hwclock_LDADD += -laudit
+endif
+
+EXTRA_DIST = README.hwclock clock-ppc.c
diff --git a/hwclock/Makefile.in b/hwclock/Makefile.in
new file mode 100644
index 0000000..0a45c38
--- /dev/null
+++ b/hwclock/Makefile.in
@@ -0,0 +1,664 @@
+# Makefile.in generated by automake 1.11 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+DIST_COMMON = $(dist_man_MANS) $(dist_noinst_DATA) \
+ $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(top_srcdir)/config/include-Makefile.am
+sbin_PROGRAMS = hwclock$(EXEEXT)
+@HAVE_AUDIT_TRUE@am__append_1 = -laudit
+subdir = hwclock
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/m4/tls.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_hwclock_OBJECTS = hwclock.$(OBJEXT) cmos.$(OBJEXT) rtc.$(OBJEXT) \
+ kd.$(OBJEXT)
+hwclock_OBJECTS = $(am_hwclock_OBJECTS)
+am__DEPENDENCIES_1 =
+hwclock_DEPENDENCIES = $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/config/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(hwclock_SOURCES)
+DIST_SOURCES = $(hwclock_SOURCES)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(dist_man_MANS)
+DATA = $(dist_noinst_DATA)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BLKID = @BLKID@
+BLKID_CFLAGS = @BLKID_CFLAGS@
+BLKID_LIBS = @BLKID_LIBS@
+BLKID_LIBS_STATIC = @BLKID_LIBS_STATIC@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBBLKID_VERSION = @LIBBLKID_VERSION@
+LIBBLKID_VERSION_INFO = @LIBBLKID_VERSION_INFO@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUUID_VERSION = @LIBUUID_VERSION@
+LIBUUID_VERSION_INFO = @LIBUUID_VERSION_INFO@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGMERGE = @MSGMERGE@
+NCURSES_LIBS = @NCURSES_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SED = @SED@
+SELINUX_LIBS = @SELINUX_LIBS@
+SELINUX_LIBS_STATIC = @SELINUX_LIBS_STATIC@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SUID_CFLAGS = @SUID_CFLAGS@
+SUID_LDFLAGS = @SUID_LDFLAGS@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+VOLID = @VOLID@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libdirname = @libdirname@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+usrbin_execdir = @usrbin_execdir@
+usrlib_execdir = @usrlib_execdir@
+usrsbin_execdir = @usrsbin_execdir@
+AM_CPPFLAGS = -include $(top_builddir)/config.h -I$(top_srcdir)/include \
+ -DLOCALEDIR=\"$(localedir)\"
+
+AM_CFLAGS = -fsigned-char
+AM_LDFLAGS =
+
+# Automake (at least up to 1.10) mishandles dist_man_MANS inside conditionals.
+# Unlike with other dist primaries, the files are not distributed if the
+# conditional is false.
+# Work the bug around until it is fixed:
+dist_noinst_DATA = $(dist_man_MANS)
+
+# Paths to in-tree libraries (use ul_ prefix to avoid possible collisions)
+#
+# blkid
+ul_libblkid_srcdir = $(top_srcdir)/shlibs/blkid/src
+ul_libblkid_builddir = $(top_builddir)/shlibs/blkid/src
+ul_libblkid_la = $(top_builddir)/shlibs/blkid/src/libblkid.la
+
+# uuid
+ul_libuuid_srcdir = $(top_srcdir)/shlibs/uuid/src
+ul_libuuid_builddir = $(top_builddir)/shlibs/uuid/src
+ul_libuuid_la = $(top_builddir)/shlibs/uuid/src/libuuid.la
+dist_man_MANS = hwclock.8
+hwclock_SOURCES = hwclock.c cmos.c rtc.c kd.c clock.h
+hwclock_LDADD = $(am__append_1)
+EXTRA_DIST = README.hwclock clock-ppc.c
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/config/include-Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign hwclock/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign hwclock/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+hwclock$(EXEEXT): $(hwclock_OBJECTS) $(hwclock_DEPENDENCIES)
+ @rm -f hwclock$(EXEEXT)
+ $(LINK) $(hwclock_OBJECTS) $(hwclock_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmos.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwclock.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rtc.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(dist_man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
+ @list=''; test -n "$(man8dir)" || exit 0; \
+ { for i in $$list; do echo "$$i"; done; \
+ l2='$(dist_man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.8[a-z]*$$/p'; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(dist_man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.8[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ test -z "$$files" || { \
+ echo " ( cd '$(DESTDIR)$(man8dir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(man8dir)" && rm -f $$files; }
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @list='$(MANS)'; if test -n "$$list"; then \
+ list=`for p in $$list; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
+ if test -n "$$list" && \
+ grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
+ echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
+ grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
+ echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
+ echo " typically \`make maintainer-clean' will remove them" >&2; \
+ exit 1; \
+ else :; fi; \
+ else :; fi
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(MANS) $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man8
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-sbinPROGRAMS ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-man8 install-pdf install-pdf-am install-ps \
+ install-ps-am install-sbinPROGRAMS install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-man uninstall-man8 \
+ uninstall-sbinPROGRAMS
+
+
+$(ul_libblkid_la):
+ $(MAKE) -C $(ul_libblkid_builddir)
+
+$(ul_libuuid_la):
+ $(MAKE) -C $(ul_libuuid_builddir)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/hwclock/README.hwclock b/hwclock/README.hwclock
new file mode 100644
index 0000000..ba8bc44
--- /dev/null
+++ b/hwclock/README.hwclock
@@ -0,0 +1,27 @@
+Hwclock is a program that runs under Linux and sets and queries the
+Hardware Clock, which is often called the Real Time Clock, RTC, or
+CMOS clock.
+
+Sometimes, you need to install hwclock setuid root. If you want users
+other than the superuser to be able to display the clock value using the
+direct ISA I/O method, install it setuid root. If you have the /dev/rtc
+interface on your system or are on a non-ISA system, there's probably
+no need for users to use the direct ISA I/O method, so don't bother.
+
+To install setuid root, do something like this:
+
+ chmod a=rx,u=s /sbin/hwclock
+
+In any case, hwclock will not allow you to set anything unless you have
+the superuser _real_ uid. (This is restriction is not necessary if you
+haven't installed setuid root, but it's there for now).
+
+You may want to preformat and/or compress the man page before installing.
+
+If you want to build hwclock, just cd to the source directory and invoke
+make with no parameters.
+
+hwclock calls option processing routines in the libsshopt library,
+which is part of Sverre H. Huseby's "shhopt" package. You
+can find a more authoritative copy of this package on metalab
+(ftp://metalab.unc.edu/pub/Linux/libs/shhopt-X.Y).
diff --git a/hwclock/clock-ppc.c b/hwclock/clock-ppc.c
new file mode 100644
index 0000000..b73d0ad
--- /dev/null
+++ b/hwclock/clock-ppc.c
@@ -0,0 +1,459 @@
+/*
+From t-matsuu@protein.osaka-u.ac.jp Sat Jan 22 13:43:20 2000
+Date: Sat, 22 Jan 2000 21:42:54 +0900 (JST)
+To: Andries.Brouwer@cwi.nl
+Subject: Please merge the source for PPC
+From: MATSUURA Takanori <t-matsuu@protein.osaka-u.ac.jp>
+
+Even now, it is used clock-1.1 based source on Linux for PowerPC
+architecture, attached on this mail.
+
+Please merge this source in main util-linux source.
+
+But I'm not an author of this source, but Paul Mackerras.
+http://linuxcare.com.au/paulus/
+shows details of him.
+
+MATSUURA Takanori @ Division of Protein Chemistry,
+ Institute for Protein Research, Osaka University, Japan
+E-Mail: t-matsuu@protein.osaka-u.ac.jp
+Web Page: http://www.protein.osaka-u.ac.jp/chemistry/matsuura/
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <sys/time.h>
+
+#include <asm/cuda.h>
+
+/*
+ * Adapted for Power Macintosh by Paul Mackerras.
+ */
+
+/* V1.0
+ * CMOS clock manipulation - Charles Hedrick, hedrick@cs.rutgers.edu, Apr 1992
+ *
+ * clock [-u] -r - read cmos clock
+ * clock [-u] -w - write cmos clock from system time
+ * clock [-u] -s - set system time from cmos clock
+ * clock [-u] -a - set system time from cmos clock, adjust the time to
+ * correct for systematic error, and put it back to the cmos.
+ * -u indicates cmos clock is kept in universal time
+ *
+ * The program is designed to run setuid, since we need to be able to
+ * write to the CUDA.
+ *
+ *********************
+ * V1.1
+ * Modified for clock adjustments - Rob Hooft, hooft@chem.ruu.nl, Nov 1992
+ * Also moved error messages to stderr. The program now uses getopt.
+ * Changed some exit codes. Made 'gcc 2.3 -Wall' happy.
+ *
+ * I think a small explanation of the adjustment routine should be given
+ * here. The problem with my machine is that its CMOS clock is 10 seconds
+ * per day slow. With this version of clock.c, and my '/etc/rc.local'
+ * reading '/etc/clock -au' instead of '/etc/clock -u -s', this error
+ * is automatically corrected at every boot.
+ *
+ * To do this job, the program reads and writes the file '/etc/adjtime'
+ * to determine the correction, and to save its data. In this file are
+ * three numbers:
+ *
+ * 1) the correction in seconds per day (So if your clock runs 5
+ * seconds per day fast, the first number should read -5.0)
+ * 2) the number of seconds since 1/1/1970 the last time the program was
+ * used.
+ * 3) the remaining part of a second which was leftover after the last
+ * adjustment
+ *
+ * Installation and use of this program:
+ *
+ * a) create a file '/etc/adjtime' containing as the first and only line:
+ * '0.0 0 0.0'
+ * b) run 'clock -au' or 'clock -a', depending on whether your cmos is in
+ * universal or local time. This updates the second number.
+ * c) set your system time using the 'date' command.
+ * d) update your cmos time using 'clock -wu' or 'clock -w'
+ * e) replace the first number in /etc/adjtime by your correction.
+ * f) put the command 'clock -au' or 'clock -a' in your '/etc/rc.local'
+ *
+ * If the adjustment doesn't work for you, try contacting me by E-mail.
+ *
+ ******
+ * V1.2
+ *
+ * Applied patches by Harald Koenig (koenig@nova.tat.physik.uni-tuebingen.de)
+ * Patched and indented by Rob Hooft (hooft@EMBL-Heidelberg.DE)
+ *
+ * A free quote from a MAIL-message (with spelling corrections):
+ *
+ * "I found the explanation and solution for the CMOS reading 0xff problem
+ * in the 0.99pl13c (ALPHA) kernel: the RTC goes offline for a small amount
+ * of time for updating. Solution is included in the kernel source
+ * (linux/kernel/time.c)."
+ *
+ * "I modified clock.c to fix this problem and added an option (now default,
+ * look for USE_INLINE_ASM_IO) that I/O instructions are used as inline
+ * code and not via /dev/port (still possible via #undef ...)."
+ *
+ * With the new code, which is partially taken from the kernel sources,
+ * the CMOS clock handling looks much more "official".
+ * Thanks Harald (and Torsten for the kernel code)!
+ *
+ ******
+ * V1.3
+ * Canges from alan@spri.levels.unisa.edu.au (Alan Modra):
+ * a) Fix a few typos in comments and remove reference to making
+ * clock -u a cron job. The kernel adjusts cmos time every 11
+ * minutes - see kernel/sched.c and kernel/time.c set_rtc_mmss().
+ * This means we should really have a cron job updating
+ * /etc/adjtime every 11 mins (set last_time to the current time
+ * and not_adjusted to ???).
+ * b) Swapped arguments of outb() to agree with asm/io.h macro of the
+ * same name. Use outb() from asm/io.h as it's slightly better.
+ * c) Changed CMOS_READ and CMOS_WRITE to inline functions. Inserted
+ * cli()..sti() pairs in appropriate places to prevent possible
+ * errors, and changed ioperm() call to iopl() to allow cli.
+ * d) Moved some variables around to localise them a bit.
+ * e) Fixed bug with clock -ua or clock -us that cleared environment
+ * variable TZ. This fix also cured the annoying display of bogus
+ * day of week on a number of machines. (Use mktime(), ctime()
+ * rather than asctime() )
+ * f) Use settimeofday() rather than stime(). This one is important
+ * as it sets the kernel's timezone offset, which is returned by
+ * gettimeofday(), and used for display of MSDOS and OS2 file
+ * times.
+ * g) faith@cs.unc.edu added -D flag for debugging
+ *
+ * V1.4: alan@SPRI.Levels.UniSA.Edu.Au (Alan Modra)
+ * Wed Feb 8 12:29:08 1995, fix for years > 2000.
+ * faith@cs.unc.edu added -v option to print version.
+ *
+ * August 1996 Tom Dyas (tdyas@eden.rutgers.edu)
+ * Converted to be compatible with the SPARC /dev/rtc driver.
+ *
+ */
+
+#define VERSION "1.4"
+
+/* Here the information for time adjustments is kept. */
+#define ADJPATH "/etc/adjtime"
+
+/* Apparently the RTC on PowerMacs stores seconds since 1 Jan 1904 */
+#define RTC_OFFSET 2082844800
+
+/* used for debugging the code. */
+/*#define KEEP_OFF */
+
+/* Globals */
+int readit = 0;
+int adjustit = 0;
+int writeit = 0;
+int setit = 0;
+int universal = 0;
+int debug = 0;
+
+time_t mkgmtime(struct tm *);
+
+volatile void
+usage ( void )
+{
+ (void) fprintf (stderr,
+ "clock [-u] -r|w|s|a|v\n"
+ " r: read and print CMOS clock\n"
+ " w: write CMOS clock from system time\n"
+ " s: set system time from CMOS clock\n"
+ " a: get system time and adjust CMOS clock\n"
+ " u: CMOS clock is in universal time\n"
+ " v: print version (" VERSION ") and exit\n"
+ );
+ exit(EXIT_FAILURE);
+}
+
+int adb_fd;
+
+void
+adb_init ( void )
+{
+ adb_fd = open ("/dev/adb", 2);
+ if (adb_fd < 0)
+ {
+ perror ("unable to open /dev/adb read/write : ");
+ exit(EXIT_FAILURE);
+ }
+}
+
+unsigned char get_packet[2] = { (unsigned char) CUDA_PACKET,
+ (unsigned char) CUDA_GET_TIME };
+unsigned char set_packet[6] = { (unsigned char) CUDA_PACKET,
+ (unsigned char) CUDA_SET_TIME };
+
+int
+main (int argc, char **argv )
+{
+ struct tm tm, *tmp;
+ time_t systime;
+ time_t last_time;
+ time_t clock_time;
+ int i, arg;
+ double factor;
+ double not_adjusted;
+ int adjustment = 0;
+ /* unsigned char save_control, save_freq_select; */
+ unsigned char reply[16];
+
+ while ((arg = getopt (argc, argv, "rwsuaDv")) != -1)
+ {
+ switch (arg)
+ {
+ case 'r':
+ readit = 1;
+ break;
+ case 'w':
+ writeit = 1;
+ break;
+ case 's':
+ setit = 1;
+ break;
+ case 'u':
+ universal = 1;
+ break;
+ case 'a':
+ adjustit = 1;
+ break;
+ case 'D':
+ debug = 1;
+ break;
+ case 'v':
+ (void) fprintf( stderr, "clock " VERSION "\n" );
+ exit(EXIT_SUCCESS);
+ default:
+ usage ();
+ }
+ }
+
+ /* If we are in MkLinux do not even bother trying to set the clock */
+ if(!access("/proc/osfmach3/version", R_OK))
+ { /* We're running MkLinux */
+ if ( readit | writeit | setit | adjustit )
+ printf("You must change the clock setting in MacOS.\n");
+ exit(0);
+ }
+
+ if (readit + writeit + setit + adjustit > 1)
+ usage (); /* only allow one of these */
+
+ if (!(readit | writeit | setit | adjustit)) /* default to read */
+ readit = 1;
+
+ adb_init ();
+
+ if (adjustit)
+ { /* Read adjustment parameters first */
+ FILE *adj;
+ if ((adj = fopen (ADJPATH, "r")) == NULL)
+ {
+ perror (ADJPATH);
+ exit(EXIT_FAILURE);
+ }
+ if (fscanf (adj, "%lf %d %lf", &factor, (int *) (&last_time),
+ &not_adjusted) < 0)
+ {
+ perror (ADJPATH);
+ exit(EXIT_FAILURE);
+ }
+ (void) fclose (adj);
+ if (debug) (void) printf(
+ "Last adjustment done at %d seconds after 1/1/1970\n",
+ (int) last_time);
+ }
+
+ if (readit || setit || adjustit)
+ {
+ int ii;
+
+ if (write(adb_fd, get_packet, sizeof(get_packet)) < 0) {
+ perror("write adb");
+ exit(EXIT_FAILURE);
+ }
+ ii = (int) read(adb_fd, reply, sizeof(reply));
+ if (ii < 0) {
+ perror("read adb");
+ exit(EXIT_FAILURE);
+ }
+ if (ii != 7)
+ (void) fprintf(stderr,
+ "Warning: bad reply length from CUDA (%d)\n", ii);
+ clock_time = (time_t) ((reply[3] << 24) + (reply[4] << 16)
+ + (reply[5] << 8)) + (time_t) reply[6];
+ clock_time -= RTC_OFFSET;
+
+ if (universal) {
+ systime = clock_time;
+ } else {
+ tm = *gmtime(&clock_time);
+ (void) printf("time in rtc is %s", asctime(&tm));
+ tm.tm_isdst = -1; /* don't know whether it's DST */
+ systime = mktime(&tm);
+ }
+ }
+
+ if (readit)
+ {
+ (void) printf ("%s", ctime (&systime ));
+ }
+
+ if (setit || adjustit)
+ {
+ struct timeval tv;
+ struct timezone tz;
+
+/* program is designed to run setuid, be secure! */
+
+ if (getuid () != 0)
+ {
+ (void) fprintf (stderr,
+ "Sorry, must be root to set or adjust time\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (adjustit)
+ { /* the actual adjustment */
+ double exact_adjustment;
+
+ exact_adjustment = ((double) (systime - last_time))
+ * factor / (24 * 60 * 60)
+ + not_adjusted;
+ if (exact_adjustment > 0.)
+ adjustment = (int) (exact_adjustment + 0.5);
+ else
+ adjustment = (int) (exact_adjustment - 0.5);
+ not_adjusted = exact_adjustment - (double) adjustment;
+ systime += adjustment;
+ if (debug) {
+ (void) printf ("Time since last adjustment is %d seconds\n",
+ (int) (systime - last_time));
+ (void) printf ("Adjusting time by %d seconds\n",
+ adjustment);
+ (void) printf ("remaining adjustment is %.3f seconds\n",
+ not_adjusted);
+ }
+ }
+#ifndef KEEP_OFF
+ tv.tv_sec = systime;
+ tv.tv_usec = 0;
+ tz.tz_minuteswest = timezone / 60;
+ tz.tz_dsttime = daylight;
+
+ if (settimeofday (&tv, &tz) != 0)
+ {
+ (void) fprintf (stderr,
+ "Unable to set time -- probably you are not root\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (debug) {
+ (void) printf( "Called settimeofday:\n" );
+ (void) printf( "\ttv.tv_sec = %ld, tv.tv_usec = %ld\n",
+ tv.tv_sec, tv.tv_usec );
+ (void) printf( "\ttz.tz_minuteswest = %d, tz.tz_dsttime = %d\n",
+ tz.tz_minuteswest, tz.tz_dsttime );
+ }
+#endif
+ }
+
+ if (writeit || (adjustit && adjustment != 0))
+ {
+ systime = time (NULL);
+
+ if (universal) {
+ clock_time = systime;
+
+ } else {
+ tmp = localtime(&systime);
+ clock_time = mkgmtime(tmp);
+ }
+
+ clock_time += RTC_OFFSET;
+ set_packet[2] = clock_time >> 24;
+ set_packet[3] = clock_time >> 16;
+ set_packet[4] = clock_time >> 8;
+ set_packet[5] = (unsigned char) clock_time;
+
+ if (write(adb_fd, set_packet, sizeof(set_packet)) < 0) {
+ perror("write adb (set)");
+ exit(EXIT_FAILURE);
+ }
+ i = (int) read(adb_fd, reply, sizeof(reply));
+ if (debug) {
+ int j;
+ (void) printf("set reply %d bytes:", i);
+ for (j = 0; j < i; ++j)
+ (void) printf(" %.2x", (unsigned int) reply[j]);
+ (void) printf("\n");
+ }
+ if (i != 3 || reply[1] != (unsigned char) 0)
+ (void) fprintf(stderr, "Warning: error %d setting RTC\n",
+ (int) reply[1]);
+
+ if (debug) {
+ clock_time -= RTC_OFFSET;
+ (void) printf("set RTC to %s", asctime(gmtime(&clock_time)));
+ }
+ }
+ else
+ if (debug) (void) printf ("CMOS clock unchanged.\n");
+ /* Save data for next 'adjustit' call */
+ if (adjustit)
+ {
+ FILE *adj;
+ if ((adj = fopen (ADJPATH, "w")) == NULL)
+ {
+ perror (ADJPATH);
+ exit(EXIT_FAILURE);
+ }
+ (void) fprintf (adj, "%f %d %f\n", factor, (int) systime, not_adjusted);
+ (void) fclose (adj);
+ }
+ exit(EXIT_SUCCESS);
+}
+
+/* Stolen from linux/arch/i386/kernel/time.c. */
+/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+ * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * machines were long is 32-bit! (However, as time_t is signed, we
+ * will already get problems at other places on 2038-01-19 03:14:08)
+ */
+time_t mkgmtime(struct tm *tm)
+{
+ int mon = tm->tm_mon + 1;
+ int year = tm->tm_year + 1900;
+
+ if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
+ mon += 12; /* Puts Feb last since it has leap day */
+ year -= 1;
+ }
+ return (((
+ (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12) +
+ tm->tm_mday + year*365 - 719499
+ )*24 + tm->tm_hour /* now have hours */
+ )*60 + tm->tm_min /* now have minutes */
+ )*60 + tm->tm_sec; /* finally seconds */
+}
diff --git a/hwclock/clock.h b/hwclock/clock.h
new file mode 100644
index 0000000..cbdf999
--- /dev/null
+++ b/hwclock/clock.h
@@ -0,0 +1,53 @@
+#ifndef HWCLOCK_CLOCK_H
+#define HWCLOCK_CLOCK_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h> /* for errno, EPERM, EINVAL, ENOENT */
+#include <time.h>
+
+struct clock_ops {
+ char *interface_name;
+ int (*get_permissions)(void);
+ int (*read_hardware_clock)(struct tm *tm);
+ int (*set_hardware_clock)(const struct tm *tm);
+ int (*synchronize_to_clock_tick)(void);
+};
+
+extern struct clock_ops *probe_for_cmos_clock(void);
+extern struct clock_ops *probe_for_rtc_clock(void);
+extern struct clock_ops *probe_for_kd_clock(void);
+
+typedef int bool;
+#define TRUE 1
+#define FALSE 0
+
+/* hwclock.c */
+extern char *progname;
+extern int debug;
+extern int epoch_option;
+extern void outsyserr(char *msg, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 1, 2)));
+#else
+ ;
+#endif
+extern double time_diff(struct timeval subtrahend, struct timeval subtractor);
+/* cmos.c */
+extern void set_cmos_epoch(int ARCconsole, int SRM);
+extern void set_cmos_access(int Jensen, int funky_toy);
+
+/* rtc.c */
+extern int get_epoch_rtc(unsigned long *epoch, int silent);
+extern int set_epoch_rtc(unsigned long epoch);
+extern char *rtc_dev_name;
+
+#ifdef HAVE_LIBAUDIT
+extern void hwaudit_exit(int status);
+# define hwclock_exit(_status) hwaudit_exit(_status)
+#else
+# define hwclock_exit(_status) exit(_status)
+#endif
+
+#endif /* HWCLOCK_CLOCK_H */
diff --git a/hwclock/cmos.c b/hwclock/cmos.c
new file mode 100644
index 0000000..8b3495b
--- /dev/null
+++ b/hwclock/cmos.c
@@ -0,0 +1,614 @@
+/*
+ * i386 CMOS starts out with 14 bytes clock data
+ * alpha has something similar, but with details
+ * depending on the machine type.
+ *
+ * byte 0: seconds (0-59)
+ * byte 2: minutes (0-59)
+ * byte 4: hours (0-23 in 24hr mode,
+ * 1-12 in 12hr mode, with high bit unset/set if am/pm)
+ * byte 6: weekday (1-7, Sunday=1)
+ * byte 7: day of the month (1-31)
+ * byte 8: month (1-12)
+ * byte 9: year (0-99)
+ * Numbers are stored in BCD/binary if bit 2 of byte 11 is unset/set
+ * The clock is in 12hr/24hr mode if bit 1 of byte 11 is unset/set
+ * The clock is undefined (being updated) if bit 7 of byte 10 is set.
+ * The clock is frozen (to be updated) by setting bit 7 of byte 11
+ * Bit 7 of byte 14 indicates whether the CMOS clock is reliable:
+ * it is 1 if RTC power has been good since this bit was last read;
+ * it is 0 when the battery is dead and system power has been off.
+ *
+ * Avoid setting the RTC clock within 2 seconds of the day rollover
+ * that starts a new month or enters daylight saving time.
+ *
+ * The century situation is messy:
+ * Usually byte 50 (0x32) gives the century (in BCD, so 19 or 20 hex),
+ * but IBM PS/2 has (part of) a checksum there and uses byte 55 (0x37).
+ * Sometimes byte 127 (0x7f) or Bank 1, byte 0x48 gives the century.
+ * The original RTC will not access any century byte; some modern
+ * versions will. If a modern RTC or BIOS increments the century byte
+ * it may go from 0x19 to 0x20, but in some buggy cases 0x1a is produced.
+ */
+
+/*
+ * A struct tm has int fields
+ * tm_sec (0-59, 60 or 61 only for leap seconds)
+ * tm_min (0-59)
+ * tm_hour (0-23)
+ * tm_mday (1-31)
+ * tm_mon (0-11)
+ * tm_year (number of years since 1900)
+ * tm_wday (0-6, 0=Sunday)
+ * tm_yday (0-365)
+ * tm_isdst (>0: yes, 0: no, <0: unknown)
+ */
+
+#include <unistd.h> /* for geteuid() */
+#include <fcntl.h> /* for O_RDWR */
+#include <errno.h>
+#include "nls.h"
+
+#if defined(__i386__)
+#ifdef HAVE_SYS_IO_H
+#include <sys/io.h>
+#else
+#include <asm/io.h> /* for inb, outb */
+#endif
+#elif defined(__alpha__)
+/* <asm/io.h> fails to compile, probably because of u8 etc */
+extern unsigned int inb(unsigned long port);
+extern void outb(unsigned char b,unsigned long port);
+#else
+void outb(int a, int b){}
+int inb(int c){ return 0; }
+#endif
+
+#include "clock.h"
+
+#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
+#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
+
+/*
+ * The epoch.
+ *
+ * Unix uses 1900 as epoch for a struct tm, and 1970 for a time_t.
+ * But what was written to CMOS?
+ * Digital DECstations use 1928 - this is on a mips or alpha
+ * Digital Unix uses 1952, e.g. on AXPpxi33
+ * Windows NT uses 1980.
+ * The ARC console expects to boot Windows NT and uses 1980.
+ * (But a Ruffian uses 1900, just like SRM.)
+ * It is reported that ALPHA_PRE_V1_2_SRM_CONSOLE uses 1958.
+ */
+#define TM_EPOCH 1900
+int cmos_epoch = 1900;
+
+/* Martin Ostermann writes:
+The problem with the Jensen is twofold: First, it has the clock at a
+different address. Secondly, it has a distinction beween "local" and
+normal bus addresses. The local ones pertain to the hardware integrated
+into the chipset, like serial/parallel ports and of course, the RTC.
+Those need to be addressed differently. This is handled fine in the kernel,
+and it's not a problem, since this usually gets totally optimized by the
+compile. But the i/o routines of (g)libc lack this support so far.
+The result of this is, that the old clock program worked only on the
+Jensen when USE_DEV_PORT was defined, but not with the normal inb/outb
+functions.
+ */
+int use_dev_port = 0; /* 1 for Jensen */
+int dev_port_fd;
+unsigned short clock_ctl_addr = 0x70; /* 0x170 for Jensen */
+unsigned short clock_data_addr = 0x71; /* 0x171 for Jensen */
+
+
+int century_byte = 0; /* 0: don't access a century byte
+ 50 (0x32): usual PC value
+ 55 (0x37): PS/2 */
+
+#ifdef __alpha__
+int funkyTOY = 0; /* 1 for PC164/LX164/SX164 type alpha */
+#endif
+
+#ifdef __alpha
+
+static int
+is_in_cpuinfo(char *fmt, char *str)
+{
+ FILE *cpuinfo;
+ char field[256];
+ char format[256];
+ int found = 0;
+
+ sprintf(format, "%s : %s", fmt, "%255s");
+
+ if ((cpuinfo = fopen ("/proc/cpuinfo", "r")) != NULL) {
+ while (!feof(cpuinfo)) {
+ if (fscanf (cpuinfo, format, field) == 1) {
+ if (strncmp(field, str, strlen(str)) == 0)
+ found = 1;
+ break;
+ }
+ fgets (field, 256, cpuinfo);
+ }
+ fclose(cpuinfo);
+ }
+ return found;
+}
+
+/* Set cmos_epoch, either from user options, or by asking the kernel,
+ or by looking at /proc/cpu_info */
+void
+set_cmos_epoch(int ARCconsole, int SRM) {
+ unsigned long epoch;
+
+ /* Believe the user */
+ if (epoch_option != -1) {
+ cmos_epoch = epoch_option;
+ return;
+ }
+
+ if (ARCconsole)
+ cmos_epoch = 1980;
+
+ if (ARCconsole || SRM)
+ return;
+
+
+ /* If we can ask the kernel, we don't need guessing from /proc/cpuinfo */
+ if (get_epoch_rtc(&epoch, 1) == 0) {
+ cmos_epoch = epoch;
+ return;
+ }
+
+ /* The kernel source today says: read the year.
+ If it is in 0-19 then the epoch is 2000.
+ If it is in 20-47 then the epoch is 1980.
+ If it is in 48-69 then the epoch is 1952.
+ If it is in 70-99 then the epoch is 1928.
+ Otherwise the epoch is 1900.
+ Clearly, this must be changed before 2019. */
+
+ /* See whether we are dealing with SRM or MILO, as they have
+ different "epoch" ideas. */
+ if (is_in_cpuinfo("system serial number", "MILO")) {
+ ARCconsole = 1;
+ if (debug) printf (_("booted from MILO\n"));
+ }
+
+ /* See whether we are dealing with a RUFFIAN aka Alpha PC-164 UX (or BX),
+ as they have REALLY different TOY (TimeOfYear) format: BCD, and not
+ an ARC-style epoch.
+ BCD is detected dynamically, but we must NOT adjust like ARC. */
+ if (ARCconsole && is_in_cpuinfo("system type", "Ruffian")) {
+ ARCconsole = 0;
+ if (debug) printf (_("Ruffian BCD clock\n"));
+ }
+
+ if (ARCconsole)
+ cmos_epoch = 1980;
+}
+
+void
+set_cmos_access(int Jensen, int funky_toy) {
+
+ /* See whether we're dealing with a Jensen---it has a weird I/O
+ system. DEC was just learning how to build Alpha PCs. */
+ if (Jensen || is_in_cpuinfo("system type", "Jensen")) {
+ use_dev_port = 1;
+ clock_ctl_addr = 0x170;
+ clock_data_addr = 0x171;
+ if (debug) printf (_("clockport adjusted to 0x%x\n"), clock_ctl_addr);
+ }
+
+ /* see whether we are dealing with PC164/LX164/SX164, as they have a TOY
+ that must be accessed differently to work correctly. */
+ /* Nautilus stuff reported by Neoklis Kyriazis */
+ if (funky_toy ||
+ is_in_cpuinfo("system variation", "PC164") ||
+ is_in_cpuinfo("system variation", "LX164") ||
+ is_in_cpuinfo("system variation", "SX164") ||
+ is_in_cpuinfo("system type", "Nautilus")) {
+ funkyTOY = 1;
+ if (debug) printf (_("funky TOY!\n"));
+ }
+}
+#endif
+
+
+#if __alpha__
+/*
+ * The Alpha doesn't allow user-level code to disable interrupts (for
+ * good reasons). Instead, we ensure atomic operation by performing
+ * the operation and checking whether the high 32 bits of the cycle
+ * counter changed. If they did, a context switch must have occurred
+ * and we redo the operation. As long as the operation is reasonably
+ * short, it will complete atomically, eventually.
+ */
+
+static unsigned long
+atomic(const char *name, unsigned long (*op)(unsigned long),
+ unsigned long arg)
+{
+ unsigned long ts1, ts2, n, v;
+
+ for (n = 0; n < 1000; ++n) {
+ asm volatile ("rpcc %0" : "r="(ts1));
+ v = (*op)(arg);
+ asm volatile ("rpcc %0" : "r="(ts2));
+
+ if ((ts1 ^ ts2) >> 32 == 0) {
+ return v;
+ }
+ }
+ fprintf(stderr, _("%s: atomic %s failed for 1000 iterations!"), progname, name);
+ exit(1);
+}
+#else
+
+/*
+ * Hmmh, this isn't very atomic. Maybe we should force an error
+ * instead?
+ *
+ * TODO: optimize the access to CMOS by mlockall(MCL_CURRENT)
+ * and SCHED_FIFO
+ */
+static unsigned long
+atomic(const char *name, unsigned long (*op)(unsigned long),
+ unsigned long arg)
+{
+ return (*op)(arg);
+}
+
+#endif
+
+
+static inline
+unsigned long cmos_read(unsigned long reg)
+{
+ if (use_dev_port) {
+ unsigned char v = reg | 0x80;
+ lseek(dev_port_fd, clock_ctl_addr, 0);
+ if (write(dev_port_fd, &v, 1) == -1 && debug)
+ printf(_("cmos_read(): write to control address %X failed: %s\n"), clock_ctl_addr, strerror(errno));
+ lseek(dev_port_fd, clock_data_addr, 0);
+ if (read(dev_port_fd, &v, 1) == -1 && debug)
+ printf(_("cmos_read(): read data address %X failed: %s\n"), clock_data_addr, strerror(errno));
+ return v;
+ } else {
+ /* We only want to read CMOS data, but unfortunately
+ writing to bit 7 disables (1) or enables (0) NMI;
+ since this bit is read-only we have to guess the old status.
+ Various docs suggest that one should disable NMI while
+ reading/writing CMOS data, and enable it again afterwards.
+ This would yield the sequence
+ outb (reg | 0x80, 0x70);
+ val = inb(0x71);
+ outb (0x0d, 0x70); // 0x0d: random read-only location
+ Other docs state that "any write to 0x70 should be followed
+ by an action to 0x71 or the RTC wil be left in an unknown state".
+ Most docs say that it doesnt matter at all what one does.
+ */
+ /* bit 0x80: disable NMI while reading - should we?
+ Let us follow the kernel and not disable.
+ Called only with 0 <= reg < 128 */
+ outb (reg, clock_ctl_addr);
+ return inb (clock_data_addr);
+ }
+}
+
+static inline
+unsigned long cmos_write(unsigned long reg, unsigned long val)
+{
+ if (use_dev_port) {
+ unsigned char v = reg | 0x80;
+ lseek(dev_port_fd, clock_ctl_addr, 0);
+ if (write(dev_port_fd, &v, 1) == -1 && debug)
+ printf(_("cmos_write(): write to control address %X failed: %s\n"), clock_ctl_addr, strerror(errno));
+ v = (val & 0xff);
+ lseek(dev_port_fd, clock_data_addr, 0);
+ if (write(dev_port_fd, &v, 1) == -1 && debug)
+ printf(_("cmos_write(): write to data address %X failed: %s\n"), clock_data_addr, strerror(errno));
+ } else {
+ outb (reg, clock_ctl_addr);
+ outb (val, clock_data_addr);
+ }
+ return 0;
+}
+
+static unsigned long cmos_set_time(unsigned long arg)
+{
+ unsigned char save_control, save_freq_select, pmbit = 0;
+ struct tm tm = *(struct tm *) arg;
+ unsigned int century;
+
+/*
+ * CMOS byte 10 (clock status register A) has 3 bitfields:
+ * bit 7: 1 if data invalid, update in progress (read-only bit)
+ * (this is raised 224 us before the actual update starts)
+ * 6-4 select base frequency
+ * 010: 32768 Hz time base (default)
+ * 111: reset
+ * all other combinations are manufacturer-dependent
+ * (e.g.: DS1287: 010 = start oscillator, anything else = stop)
+ * 3-0 rate selection bits for interrupt
+ * 0000 none (may stop RTC)
+ * 0001, 0010 give same frequency as 1000, 1001
+ * 0011 122 microseconds (minimum, 8192 Hz)
+ * .... each increase by 1 halves the frequency, doubles the period
+ * 1111 500 milliseconds (maximum, 2 Hz)
+ * 0110 976.562 microseconds (default 1024 Hz)
+ */
+
+ save_control = cmos_read (11); /* tell the clock it's being set */
+ cmos_write (11, (save_control | 0x80));
+ save_freq_select = cmos_read (10); /* stop and reset prescaler */
+ cmos_write (10, (save_freq_select | 0x70));
+
+ tm.tm_year += TM_EPOCH;
+ century = tm.tm_year/100;
+ tm.tm_year -= cmos_epoch;
+ tm.tm_year %= 100;
+ tm.tm_mon += 1;
+ tm.tm_wday += 1;
+
+ if (!(save_control & 0x02)) { /* 12hr mode; the default is 24hr mode */
+ if (tm.tm_hour == 0)
+ tm.tm_hour = 24;
+ if (tm.tm_hour > 12) {
+ tm.tm_hour -= 12;
+ pmbit = 0x80;
+ }
+ }
+
+ if (!(save_control & 0x04)) { /* BCD mode - the default */
+ BIN_TO_BCD(tm.tm_sec);
+ BIN_TO_BCD(tm.tm_min);
+ BIN_TO_BCD(tm.tm_hour);
+ BIN_TO_BCD(tm.tm_wday);
+ BIN_TO_BCD(tm.tm_mday);
+ BIN_TO_BCD(tm.tm_mon);
+ BIN_TO_BCD(tm.tm_year);
+ BIN_TO_BCD(century);
+ }
+
+ cmos_write (0, tm.tm_sec);
+ cmos_write (2, tm.tm_min);
+ cmos_write (4, tm.tm_hour | pmbit);
+ cmos_write (6, tm.tm_wday);
+ cmos_write (7, tm.tm_mday);
+ cmos_write (8, tm.tm_mon);
+ cmos_write (9, tm.tm_year);
+ if (century_byte)
+ cmos_write (century_byte, century);
+
+
+ /* The kernel sources, linux/arch/i386/kernel/time.c, have the
+ following comment:
+
+ The following flags have to be released exactly in this order,
+ otherwise the DS12887 (popular MC146818A clone with integrated
+ battery and quartz) will not reset the oscillator and will not
+ update precisely 500 ms later. You won't find this mentioned
+ in the Dallas Semiconductor data sheets, but who believes data
+ sheets anyway ... -- Markus Kuhn
+ */
+
+ cmos_write (11, save_control);
+ cmos_write (10, save_freq_select);
+ return 0;
+}
+
+static int
+hclock_read(unsigned long reg) {
+ return atomic("clock read", cmos_read, (reg));
+}
+
+static void
+hclock_set_time(const struct tm *tm) {
+ atomic("set time", cmos_set_time, (unsigned long)(tm));
+}
+
+static inline int
+cmos_clock_busy(void) {
+ return
+#ifdef __alpha__
+ /* poll bit 4 (UF) of Control Register C */
+ funkyTOY ? (hclock_read(12) & 0x10) :
+#endif
+ /* poll bit 7 (UIP) of Control Register A */
+ (hclock_read(10) & 0x80);
+}
+
+
+static int
+synchronize_to_clock_tick_cmos(void) {
+ int i;
+
+ /* Wait for rise. Should be within a second, but in case something
+ weird happens, we have a limit on this loop to reduce the impact
+ of this failure.
+ */
+ for (i = 0; !cmos_clock_busy(); i++)
+ if (i >= 10000000)
+ return 1;
+
+ /* Wait for fall. Should be within 2.228 ms. */
+ for (i = 0; cmos_clock_busy(); i++)
+ if (i >= 1000000)
+ return 1;
+ return 0;
+}
+
+
+
+static int
+read_hardware_clock_cmos(struct tm *tm) {
+/*----------------------------------------------------------------------------
+ Read the hardware clock and return the current time via <tm> argument.
+ Assume we have an ISA machine and read the clock directly with CPU I/O
+ instructions.
+
+ This function is not totally reliable. It takes a finite and
+ unpredictable amount of time to execute the code below. During that
+ time, the clock may change and we may even read an invalid value in
+ the middle of an update. We do a few checks to minimize this
+ possibility, but only the kernel can actually read the clock
+ properly, since it can execute code in a short and predictable
+ amount of time (by turning of interrupts).
+
+ In practice, the chance of this function returning the wrong time is
+ extremely remote.
+
+-----------------------------------------------------------------------------*/
+ bool got_time = FALSE;
+ unsigned char status, pmbit;
+
+ status = pmbit = 0; /* just for gcc */
+
+ while (!got_time) {
+ /* Bit 7 of Byte 10 of the Hardware Clock value is the Update In Progress
+ (UIP) bit, which is on while and 244 uS before the Hardware Clock
+ updates itself. It updates the counters individually, so reading
+ them during an update would produce garbage. The update takes 2mS,
+ so we could be spinning here that long waiting for this bit to turn
+ off.
+
+ Furthermore, it is pathologically possible for us to be in this
+ code so long that even if the UIP bit is not on at first, the
+ clock has changed while we were running. We check for that too,
+ and if it happens, we start over.
+ */
+
+ if (!cmos_clock_busy()) {
+ /* No clock update in progress, go ahead and read */
+ tm->tm_sec = hclock_read(0);
+ tm->tm_min = hclock_read(2);
+ tm->tm_hour = hclock_read(4);
+ tm->tm_wday = hclock_read(6);
+ tm->tm_mday = hclock_read(7);
+ tm->tm_mon = hclock_read(8);
+ tm->tm_year = hclock_read(9);
+ status = hclock_read(11);
+#if 0
+ if (century_byte)
+ century = hclock_read(century_byte);
+#endif
+
+ /* Unless the clock changed while we were reading, consider this
+ a good clock read .
+ */
+ if (tm->tm_sec == hclock_read (0))
+ got_time = TRUE;
+ }
+ /* Yes, in theory we could have been running for 60 seconds and
+ the above test wouldn't work!
+ */
+ }
+
+ if (!(status & 0x04)) { /* BCD mode - the default */
+ BCD_TO_BIN(tm->tm_sec);
+ BCD_TO_BIN(tm->tm_min);
+ pmbit = (tm->tm_hour & 0x80);
+ tm->tm_hour &= 0x7f;
+ BCD_TO_BIN(tm->tm_hour);
+ BCD_TO_BIN(tm->tm_wday);
+ BCD_TO_BIN(tm->tm_mday);
+ BCD_TO_BIN(tm->tm_mon);
+ BCD_TO_BIN(tm->tm_year);
+#if 0
+ BCD_TO_BIN(century);
+#endif
+ }
+
+ /* We don't use the century byte of the Hardware Clock
+ since we don't know its address (usually 50 or 55).
+ Here, we follow the advice of the X/Open Base Working Group:
+ "if century is not specified, then values in the range [69-99]
+ refer to years in the twentieth century (1969 to 1999 inclusive),
+ and values in the range [00-68] refer to years in the twenty-first
+ century (2000 to 2068 inclusive)."
+ */
+
+ tm->tm_wday -= 1;
+ tm->tm_mon -= 1;
+ tm->tm_year += (cmos_epoch - TM_EPOCH);
+ if (tm->tm_year < 69)
+ tm->tm_year += 100;
+ if (pmbit) {
+ tm->tm_hour += 12;
+ if (tm->tm_hour == 24)
+ tm->tm_hour = 0;
+ }
+
+ tm->tm_isdst = -1; /* don't know whether it's daylight */
+ return 0;
+}
+
+
+
+static int
+set_hardware_clock_cmos(const struct tm *new_broken_time) {
+
+ hclock_set_time(new_broken_time);
+ return 0;
+}
+
+static int
+i386_iopl(const int level) {
+#if defined(__i386__) || defined(__alpha__)
+ extern int iopl(const int lvl);
+ return iopl(level);
+#else
+ return -2;
+#endif
+}
+
+static int
+get_permissions_cmos(void) {
+ int rc;
+
+ if (use_dev_port) {
+ if ((dev_port_fd = open("/dev/port", O_RDWR)) < 0) {
+ int errsv = errno;
+ fprintf(stderr, _("Cannot open /dev/port: %s"), strerror(errsv));
+ rc = 1;
+ } else
+ rc = 0;
+ } else {
+ rc = i386_iopl(3);
+ if (rc == -2) {
+ fprintf(stderr, _("I failed to get permission because I didn't try.\n"));
+ } else if (rc != 0) {
+ rc = errno;
+ fprintf(stderr, _("%s is unable to get I/O port access: "
+ "the iopl(3) call failed.\n"), progname);
+ if(rc == EPERM && geteuid())
+ fprintf(stderr, _("Probably you need root privileges.\n"));
+ }
+ }
+ return rc ? 1 : 0;
+}
+
+static struct clock_ops cmos = {
+ "direct I/O instructions to ISA clock",
+ get_permissions_cmos,
+ read_hardware_clock_cmos,
+ set_hardware_clock_cmos,
+ synchronize_to_clock_tick_cmos,
+};
+
+
+/* return &cmos if cmos clock present, NULL otherwise */
+/* choose this construction to avoid gcc messages about unused variables */
+
+struct clock_ops *
+probe_for_cmos_clock(void){
+ int have_cmos =
+#if defined(__i386__) || defined(__alpha__)
+ TRUE;
+#else
+ FALSE;
+#endif
+ return have_cmos ? &cmos : NULL;
+}
diff --git a/hwclock/hwclock.8 b/hwclock/hwclock.8
new file mode 100644
index 0000000..dc21acd
--- /dev/null
+++ b/hwclock/hwclock.8
@@ -0,0 +1,628 @@
+.TH HWCLOCK 8 "06 August 2008"
+.SH NAME
+hwclock \- query and set the hardware clock (RTC)
+.SH SYNOPSIS
+.B hwclock
+.RI [ functions ]
+.RI [ options ]
+
+.SH DESCRIPTION
+.B hwclock
+is a tool for accessing the Hardware Clock. You can display the
+current time, set the Hardware Clock to a specified time, set the
+Hardware Clock to the System Time, and set the System Time from the
+Hardware Clock.
+.PP
+You can also run
+.B hwclock
+periodically to insert or remove time from the Hardware Clock to
+compensate for systematic drift (where the clock consistently gains or
+loses time at a certain rate if left to run).
+
+.SH FUNCTIONS
+You need exactly one of the following options to tell
+.B hwclock
+what function to perform:
+.PP
+.TP
+.BR \-r , \ \-\-show
+Read the Hardware Clock and print the time on Standard Output.
+The time shown is always in local time, even if you keep your Hardware Clock
+in Coordinated Universal Time. See the
+.B \-\-utc
+option.
+
+.TP
+.B \-\-set
+Set the Hardware Clock to the time given by the
+.B \-\-date
+option.
+.TP
+.BR \-s , \ \-\-hctosys
+Set the System Time from the Hardware Clock.
+
+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 a good option to use in one of the system startup scripts.
+.TP
+.BR \-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
+below.
+.TP
+.B \-\-getepoch
+Print the kernel's Hardware Clock epoch value to standard output.
+This is the number of years into AD to which a zero year value in the
+Hardware Clock refers. For example, if you are using the convention
+that the year counter in your Hardware Clock contains the number of
+full years since 1952, then the kernel's Hardware Counter epoch value
+must be 1952.
+
+This epoch value is used whenever hwclock reads or sets the Hardware Clock.
+.TP
+.B \-\-setepoch
+Set the kernel's Hardware Clock epoch value to the value specified by the
+.B \-\-epoch
+option. See the
+.B \-\-getepoch
+option for details.
+.TP
+.BR \-v , \ \-\-version
+Print the version of
+.B hwclock
+on Standard Output.
+.TP
+.BI \-\-date= date_string
+You need this option if you specify the
+.B \-\-set
+option. Otherwise, it is ignored.
+This specifies the time to which to set the Hardware Clock.
+The value of this option is an argument to the
+.BR date (1)
+program.
+For example,
+.sp
+.I hwclock --set --date="9/22/96 16:45:05"
+.sp
+The argument is in local time, even if you keep your Hardware Clock in
+Coordinated Universal time. See the
+.B \-\-utc
+option.
+
+.TP
+.BI \-\-epoch= year
+Specifies the year which is the beginning of the Hardware Clock's
+epoch. I.e. the number of years into AD to which a zero value in the
+Hardware Clock's year counter refers. It is used together with
+the \-\-setepoch option to set the kernel's idea of the epoch of the
+Hardware Clock, or otherwise to specify the epoch for use with
+direct ISA access.
+
+For example, on a Digital Unix machine:
+.sp
+.I hwclock --setepoch --epoch=1952
+
+
+.SH OPTIONS
+.PP
+The following options apply to most functions.
+.TP
+.BR \-u , \ \-\-utc
+.TP
+.B \-\-localtime
+Indicates that the Hardware Clock is kept in Coordinated Universal
+Time or local time, respectively. It is your choice whether to keep
+your clock in UTC or local time, but nothing in the clock tells which
+you've chosen. So this option is how you give that information to
+.BR hwclock .
+
+If you specify the wrong one of these options (or specify neither and
+take a wrong default), both setting and querying of the Hardware Clock
+will be messed up.
+
+If you specify neither
+.B \-\-utc
+nor
+.B \-\-localtime
+, the default is whichever was specified the last time
+.B hwclock
+was used to set the clock (i.e. hwclock was successfully run with the
+.BR \-\-set ,
+.BR \-\-systohc ,
+or
+.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
+.IR /etc/adjtime .
+.B hwclock
+will not read nor write to that file with this option. Either
+.B \-\-utc
+or
+.B \-\-localtime
+must be specified when using this option.
+
+.TP
+.BI \-\-adjfile= filename
+overrides the default /etc/adjtime.
+
+.TP
+.BR \-f , \ \-\-rtc=\fIfilename\fB
+overrides the default /dev file name, which is
+.IR /dev/rtc
+on many platforms but may be
+.IR /dev/rtc0 ,
+.IR /dev/rtc1 ,
+and so on.
+
+.TP
+.B \-\-directisa
+is meaningful only on an ISA machine or an Alpha (which implements enough
+of ISA to be, roughly speaking, an ISA machine for
+.BR hwclock 's
+purposes). For other machines, it has no effect. This option tells
+.B hwclock
+to use explicit I/O instructions to access the Hardware Clock.
+Without this option,
+.B hwclock
+will try to use the /dev/rtc device (which it assumes to be driven by the
+rtc device driver). If it is unable to open the device (for read), it will
+use the explicit I/O instructions anyway.
+
+The rtc device driver was new in Linux Release 2.
+.TP
+.B \-\-badyear
+Indicates that the Hardware Clock is incapable of storing years outside
+the range 1994-1999. There is a problem in some BIOSes (almost all
+Award BIOSes made between 4/26/94 and 5/31/95) wherein they are unable
+to deal with years after 1999. If one attempts to set the year-of-century
+value to something less than 94 (or 95 in some cases), the value that
+actually gets set is 94 (or 95). Thus, if you have one of these machines,
+.B hwclock
+cannot set the year after 1999 and cannot use the value of the clock as
+the true time in the normal way.
+
+To compensate for this (without your getting a BIOS update, which would
+definitely be preferable), always use
+.B \-\-badyear
+if you have one of these machines. When
+.B hwclock
+knows it's working with a brain-damaged clock, it ignores the year part of
+the Hardware Clock value and instead tries to guess the year based on the
+last calibrated date in the adjtime file, by assuming that that date is
+within the past year. For this to work, you had better do a
+.I hwclock \-\-set
+or
+.I hwclock \-\-systohc
+at least once a year!
+
+Though
+.B hwclock
+ignores the year value when it reads the Hardware Clock, it sets the
+year value when it sets the clock. It sets it to 1995, 1996, 1997, or
+1998, whichever one has the same position in the leap year cycle as
+the true year. That way, the Hardware Clock inserts leap days where
+they belong. Again, if you let the Hardware Clock run for more than a
+year without setting it, this scheme could be defeated and you could
+end up losing a day.
+
+.B hwclock
+warns you that you probably need
+.B \-\-badyear
+whenever it finds your Hardware Clock set to 1994 or 1995.
+
+.TP
+.B \-\-srm
+This option is equivalent to
+.B \-\-epoch=1900
+and is used to specify the most common epoch on Alphas
+with SRM console.
+.TP
+.B \-\-arc
+This option is equivalent to
+.B \-\-epoch=1980
+and is used to specify the most common epoch on Alphas
+with ARC console (but Ruffians have epoch 1900).
+.TP
+.B \-\-jensen
+.TP
+.B \-\-funky\-toy
+These two options specify what kind of Alpha machine you have. They
+are invalid if you don't have an Alpha and are usually unnecessary
+if you do, because
+.B hwclock
+should be able to determine by itself what it's
+running on, at least when
+.I /proc
+is mounted.
+(If you find you need one of these options to make
+.B hwclock
+work, contact the maintainer to see if the program can be improved
+to detect your system automatically. Output of `hwclock --debug'
+and `cat /proc/cpuinfo' may be of interest.)
+
+.B \-\-jensen
+means you are running on a Jensen model.
+
+.B \-\-funky\-toy
+means that on your machine, one has to use the UF bit instead
+of the UIP bit in the Hardware Clock to detect a time transition. "Toy"
+in the option name refers to the Time Of Year facility of the machine.
+
+
+.TP
+.B \-\-test
+Do everything except actually updating the Hardware Clock or anything
+else. This is useful, especially in conjunction with
+.BR \-\-debug ,
+in learning about
+.BR hwclock .
+.TP
+.B \-\-debug
+Display a lot of information about what
+.B hwclock
+is doing internally. Some of its function is complex and this output
+can help you understand how the program works.
+
+
+.SH NOTES
+
+
+.SH Clocks in a Linux System
+.PP
+There are two main clocks in a Linux system:
+.PP
+.B The Hardware Clock:
+This is a clock that runs independently of any control program running
+in the CPU and even when the machine is powered off.
+
+On an ISA system, this clock is specified as part of the ISA standard.
+The control program can read or set this clock to a whole second, but
+the control program can also detect the edges of the 1 second clock
+ticks, so the clock actually has virtually infinite precision.
+.PP
+This clock is commonly called the hardware clock, the real time clock,
+the RTC, the BIOS clock, and the CMOS clock. Hardware Clock, in its
+capitalized form, was coined for use by
+.B hwclock
+because all of the other names are inappropriate to the point of being
+misleading.
+.PP
+So for example, some non-ISA systems have a few real time clocks with
+only one of them having its own power domain.
+A very low power external I2C or SPI clock chip might be used with a
+backup battery as the hardware clock to initialize a more functional
+integrated real-time clock which is used for most other purposes.
+.PP
+.B The System Time:
+This is the time kept by a clock inside the Linux kernel and driven by
+a timer interrupt. (On an ISA machine, the timer interrupt is part of
+the ISA standard). It has meaning only while Linux is running on the
+machine. The System Time is the number of seconds since 00:00:00
+January 1, 1970 UTC (or more succinctly, the number of seconds since
+1969). The System Time is not an integer, though. It has virtually
+infinite precision.
+.PP
+The System Time is the time that matters. The Hardware Clock's basic
+purpose in a Linux system is to keep time when Linux is not running. You
+initialize the System Time to the time from the Hardware Clock when Linux
+starts up, and then never use the Hardware Clock again. Note that in DOS,
+for which ISA was designed, the Hardware Clock is the only real time clock.
+.PP
+It is important that the System Time not have any discontinuities such as
+would happen if you used the
+.BR date (1L)
+program to set it while the system is running. You can, however, do whatever
+you want to the Hardware Clock while the system is running, and the next
+time Linux starts up, it will do so with the adjusted time from the Hardware
+Clock. You can also use the program
+.BR adjtimex (8)
+to smoothly adjust the System Time while the system runs.
+.PP
+A Linux kernel maintains a concept of a local timezone for the system.
+But don't be misled -- almost nobody cares what timezone the kernel
+thinks it is in. Instead, programs that care about the timezone
+(perhaps because they want to display a local time for you) almost
+always use a more traditional method of determining the timezone: They
+use the TZ environment variable and/or the
+.I /usr/share/zoneinfo
+directory, as explained in the man page for
+.BR tzset (3).
+However, some
+programs and fringe parts of the Linux kernel such as filesystems use
+the kernel timezone value. An example is the vfat filesystem. If the
+kernel timezone value is wrong, the vfat filesystem will report and
+set the wrong timestamps on files.
+.PP
+.B hwclock
+sets the kernel timezone to the value indicated by TZ and/or
+.I /usr/share/zoneinfo
+when you set the System Time using the
+.B \-\-hctosys
+option.
+.PP
+The timezone value actually consists of two parts: 1) a field
+tz_minuteswest indicating how many minutes local time (not adjusted
+for DST) lags behind UTC, and 2) a field tz_dsttime indicating
+the type of Daylight Savings Time (DST) convention that is in effect
+in the locality at the present time.
+This second field is not used under Linux and is always zero.
+(See also
+.BR settimeofday (2).)
+
+.SH How hwclock Accesses the Hardware Clock
+.PP
+.B hwclock
+uses many different ways to get and set Hardware Clock values.
+The most normal way is to do I/O to the device special file /dev/rtc,
+which is presumed to be driven by the rtc device driver. However,
+this method is not always available. For one thing, the rtc driver is
+a relatively recent addition to Linux. Older systems don't have it.
+Also, though there are versions of the rtc driver that work on DEC
+Alphas, there appear to be plenty of Alphas on which the rtc driver
+does not work (a common symptom is hwclock hanging).
+Moreover, recent Linux systems have more generic support for RTCs,
+even systems that have more than one, so you might need to override
+the default by specifying
+.I /dev/rtc0
+or
+.I /dev/rtc1
+instead.
+.PP
+On older systems, the method of accessing the Hardware Clock depends on
+the system hardware.
+.PP
+On an ISA system,
+.B hwclock
+can directly access the "CMOS memory" registers that
+constitute the clock, by doing I/O to Ports 0x70 and 0x71. It does
+this with actual I/O instructions and consequently can only do it if
+running with superuser effective userid. (In the case of a Jensen
+Alpha, there is no way for
+.B hwclock
+to execute those I/O instructions, and so it uses instead the
+/dev/port device special file, which provides almost as low-level an
+interface to the I/O subsystem).
+
+This is a really poor method of accessing the clock, for all the
+reasons that user space programs are generally not supposed to do
+direct I/O and disable interrupts. Hwclock provides it because it is
+the only method available on ISA and Alpha systems which don't have
+working rtc device drivers available.
+
+.PP
+On an m68k system,
+.B hwclock
+can access the clock via the console driver, via the device special
+file /dev/tty1.
+.PP
+.B hwclock
+tries to use /dev/rtc. If it is compiled for a kernel that doesn't have
+that function or it is unable to open /dev/rtc
+(or the alternative special file you've defined on the command line)
+.B hwclock
+will fall back to another method, if available. On an ISA or Alpha
+machine, you can force
+.B hwclock
+to use the direct manipulation of the CMOS registers without even trying
+.I /dev/rtc
+by specifying the
+.B \-\-directisa
+option.
+
+
+.SH The Adjust Function
+.PP
+The Hardware Clock is usually not very accurate. However, much of its
+inaccuracy is completely predictable - it gains or loses the same amount
+of time every day. This is called systematic drift.
+.BR hwclock 's
+"adjust" function lets you make systematic corrections to correct the
+systematic drift.
+.PP
+It works like this:
+.B hwclock
+keeps a file,
+.IR /etc/adjtime ,
+that keeps some historical information. This is called the adjtime file.
+.PP
+Suppose you start with no adjtime file. You issue a
+.I hwclock \-\-set
+command to set the Hardware Clock to the true current time.
+.B Hwclock
+creates the adjtime file and records in it the current time as the
+last time the clock was calibrated.
+5 days later, the clock has gained 10 seconds, so you issue another
+.I hwclock \-\-set
+command to set it back 10 seconds.
+.B Hwclock
+updates the adjtime file to show the current time as the last time the
+clock was calibrated, and records 2 seconds per day as the systematic
+drift rate. 24 hours go by, and then you issue a
+.I hwclock \-\-adjust
+command.
+.B Hwclock
+consults the adjtime file and sees that the clock gains 2 seconds per
+day when left alone and that it has been left alone for exactly one
+day. So it subtracts 2 seconds from the Hardware Clock. It then
+records the current time as the last time the clock was adjusted.
+Another 24 hours goes by and you issue another
+.IR "hwclock \-\-adjust" .
+.B Hwclock
+does the same thing: subtracts 2 seconds and updates the adjtime file
+with the current time as the last time the clock was adjusted.
+.PP
+Every time you calibrate (set) the clock (using
+.I \-\-set
+or
+.IR \-\-systohc ),
+.B hwclock
+recalculates the systematic drift rate based on how long it has been
+since the last calibration, how long it has been since the last
+adjustment, what drift rate was assumed in any intervening
+adjustments, and the amount by which the clock is presently off.
+.PP
+A small amount of error creeps in any time
+.B hwclock
+sets the clock, so it refrains from making an adjustment that would be
+less than 1 second. Later on, when you request an adjustment again,
+the accumulated drift will be more than a second and
+.B hwclock
+will do the adjustment then.
+.PP
+It is good to do a
+.I hwclock \-\-adjust
+just before the
+.I hwclock \-\-hctosys
+at system startup time, and maybe periodically while the system is
+running via cron.
+.PP
+The adjtime file, while named for its historical purpose of controlling
+adjustments only, actually contains other information for use by hwclock
+in remembering information from one invocation to the next.
+.PP
+The format of the adjtime file is, in ASCII:
+.PP
+Line 1: 3 numbers, separated by blanks: 1) systematic drift rate in
+seconds per day, floating point decimal; 2) Resulting number of
+seconds since 1969 UTC of most recent adjustment or calibration,
+decimal integer; 3) zero (for compatibility with
+.BR clock (8))
+as a decimal integer.
+.PP
+Line 2: 1 number: Resulting number of seconds since 1969 UTC of most
+recent calibration. Zero if there has been no calibration yet or it
+is known that any previous calibration is moot (for example, because
+the Hardware Clock has been found, since that calibration, not to
+contain a valid time). This is a decimal integer.
+.PP
+Line 3: "UTC" or "LOCAL". Tells whether the Hardware Clock is set to
+Coordinated Universal Time or local time. You can always override this
+value with options on the
+.B hwclock
+command line.
+.PP
+You can use an adjtime file that was previously used with the
+.BR clock (8)
+program with
+.BR hwclock .
+
+
+.SH "Automatic Hardware Clock Synchronization By the Kernel"
+
+You should be aware of another way that the Hardware Clock is kept
+synchronized in some systems. The Linux kernel has a mode wherein it
+copies the System Time to the Hardware Clock every 11 minutes.
+This is a good mode to use when you are using something sophisticated
+like ntp to keep your System Time synchronized. (ntp is a way to keep
+your System Time synchronized either to a time server somewhere on the
+network or to a radio clock hooked up to your system. See RFC 1305).
+
+This mode (we'll call it "11 minute mode") is off until something
+turns it on. The ntp daemon xntpd is one thing that turns it on. You
+can turn it off by running anything, including
+.IR "hwclock \-\-hctosys" ,
+that sets the System Time the old fashioned way.
+
+To see if it is on or
+off, use the command
+.I adjtimex \-\-print
+and look at the value of "status". If the "64" bit of this number
+(expressed in binary) equal to 0, 11 minute mode is on. Otherwise, it
+is off.
+
+If your system runs with 11 minute mode on, don't use
+.I hwclock \-\-adjust
+or
+.IR "hwclock \-\-hctosys" .
+You'll just make a mess. It is acceptable to use a
+.I hwclock \-\-hctosys
+at startup time to get a reasonable System Time until your system is
+able to set the System Time from the external source and start 11
+minute mode.
+
+
+.SH ISA Hardware Clock Century value
+
+There is some sort of standard that defines CMOS memory Byte 50 on an ISA
+machine as an indicator of what century it is.
+.B hwclock
+does not use or set that byte because there are some machines that
+don't define the byte that way, and it really isn't necessary anyway,
+since the year-of-century does a good job of implying which century it
+is.
+
+If you have a bona fide use for a CMOS century byte, contact the
+.B hwclock
+maintainer; an option may be appropriate.
+
+Note that this section is only relevant when you are using the "direct
+ISA" method of accessing the Hardware Clock.
+ACPI provides a standard way to access century values, when they
+are supported by the hardware.
+
+.SH "ENVIRONMENT VARIABLES"
+.I TZ
+
+.SH FILES
+.I /etc/adjtime
+.I /usr/share/zoneinfo/
+.I /dev/rtc
+.I /dev/rtc0
+.I /dev/port
+.I /dev/tty1
+.I /proc/cpuinfo
+
+.SH "SEE ALSO"
+.BR adjtimex (8),
+.BR date (1),
+.BR gettimeofday (2),
+.BR settimeofday (2),
+.BR crontab (1),
+.BR tzset (3)
+.BR /etc/init.d/hwclock.sh,
+.BR /usr/share/doc/util-linux/README.Debian.hwclock
+
+.SH AUTHORS
+Written by Bryan Henderson, September 1996 (bryanh@giraffe-data.com),
+based on work done on the
+.I clock
+program by Charles Hedrick, Rob Hooft, and Harald Koenig.
+See the source code for complete history and credits.
+
+.SH AVAILABILITY
+The hwclock command is part of the util-linux-ng package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/.
diff --git a/hwclock/hwclock.c b/hwclock/hwclock.c
new file mode 100644
index 0000000..5591d04
--- /dev/null
+++ b/hwclock/hwclock.c
@@ -0,0 +1,1829 @@
+/*
+ * hwclock.c
+ *
+ * clock.c was written by Charles Hedrick, hedrick@cs.rutgers.edu, Apr 1992
+ * Modified for clock adjustments - Rob Hooft <hooft@chem.ruu.nl>, Nov 1992
+ * Improvements by Harald Koenig <koenig@nova.tat.physik.uni-tuebingen.de>
+ * and Alan Modra <alan@spri.levels.unisa.edu.au>.
+ *
+ * Major rewrite by Bryan Henderson <bryanh@giraffe-data.com>, 96.09.19.
+ * The new program is called hwclock. New features:
+ * - You can set the hardware clock without also modifying the system clock.
+ * - You can read and set the clock with finer than 1 second precision.
+ * - When you set the clock, hwclock automatically refigures the drift
+ * rate, based on how far off the clock was before you set it.
+ *
+ * Reshuffled things, added sparc code, and re-added alpha stuff
+ * by David Mosberger <davidm@azstarnet.com>
+ * and Jay Estabrook <jestabro@amt.tay1.dec.com>
+ * and Martin Ostermann <ost@coments.rwth-aachen.de>, aeb@cwi.nl, 990212.
+ *
+ * Fix for Award 2094 bug, Dave Coffin (dcoffin@shore.net) 11/12/98
+ * Change of local time handling, Stefan Ring <e9725446@stud3.tuwien.ac.at>
+ * Change of adjtime handling, James P. Rutledge <ao112@rgfn.epcc.edu>.
+ *
+ * Distributed under GPL
+ */
+
+/*
+ * clock [-u] -r - read hardware clock
+ * clock [-u] -w - write hardware clock from system time
+ * clock [-u] -s - set system time from hardware clock
+ * clock [-u] -a - set system time from hardware clock, adjust the time
+ * to correct for systematic error, and write it back to
+ * the hardware clock
+ * -u indicates cmos clock is kept in universal time
+ * -A indicates cmos clock is kept in Alpha ARC console time (0 == 1980)
+ * -J indicates we're dealing with a Jensen (early DEC Alpha PC)
+ */
+
+/*
+ * Explanation of `adjusting' (Rob Hooft):
+ *
+ * The problem with my machine is that its CMOS clock is 10 seconds
+ * per day slow. With this version of clock.c, and my '/etc/rc.local'
+ * reading '/etc/clock -au' instead of '/etc/clock -u -s', this error
+ * is automatically corrected at every boot.
+ *
+ * To do this job, the program reads and writes the file '/etc/adjtime'
+ * to determine the correction, and to save its data. In this file are
+ * three numbers:
+ *
+ * 1) the correction in seconds per day. (So if your clock runs 5
+ * seconds per day fast, the first number should read -5.0)
+ * 2) the number of seconds since 1/1/1970 the last time the program
+ * was used
+ * 3) the remaining part of a second which was leftover after the last
+ * adjustment
+ *
+ * Installation and use of this program:
+ *
+ * a) create a file '/etc/adjtime' containing as the first and only line:
+ * '0.0 0 0.0'
+ * b) run 'clock -au' or 'clock -a', depending on whether your cmos is in
+ * universal or local time. This updates the second number.
+ * c) set your system time using the 'date' command.
+ * d) update your cmos time using 'clock -wu' or 'clock -w'
+ * e) replace the first number in /etc/adjtime by your correction.
+ * f) put the command 'clock -au' or 'clock -a' in your '/etc/rc.local'
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <getopt.h>
+#include <sysexits.h>
+
+#include "clock.h"
+#include "nls.h"
+
+#ifdef HAVE_LIBAUDIT
+#include <libaudit.h>
+static int hwaudit_fd = -1;
+static int hwaudit_on;
+#endif
+
+#define MYNAME "hwclock"
+
+char *progname = MYNAME;
+
+/* The struct that holds our hardware access routines */
+struct clock_ops *ur;
+
+#define FLOOR(arg) ((arg >= 0 ? (int) arg : ((int) arg) - 1));
+
+/* Here the information for time adjustments is kept. */
+#define ADJPATH "/etc/adjtime"
+
+const char *adj_file_name = NULL;
+
+/* Store the date here when "badyear" flag is set. */
+#define LASTDATE "/var/lib/lastdate"
+
+struct adjtime {
+ /* This is information we keep in the adjtime file that tells us how
+ to do drift corrections. Elements are all straight from the
+ adjtime file, so see documentation of that file for details.
+ Exception is <dirty>, which is an indication that what's in this
+ structure is not what's in the disk file (because it has been
+ updated since read from the disk file).
+ */
+ bool dirty;
+
+ /* line 1 */
+ double drift_factor;
+ time_t last_adj_time;
+ double not_adjusted;
+
+ /* line 2 */
+ time_t last_calib_time;
+ /* The most recent time that we set the clock from an external
+ authority (as opposed to just doing a drift adjustment) */
+
+ /* line 3 */
+ enum a_local_utc {LOCAL, UTC, UNKNOWN} local_utc;
+ /* To which time zone, local or UTC, we most recently set the
+ hardware clock. */
+};
+
+bool debug;
+ /* We are running in debug mode, wherein we put a lot of information about
+ what we're doing to standard output. */
+
+bool badyear;
+ /* Workaround for Award 4.50g BIOS bug: keep the year in a file. */
+
+int epoch_option = -1;
+ /* User-specified epoch, used when rtc fails to return epoch. */
+
+/*
+ * Almost all Award BIOS's made between 04/26/94 and 05/31/95
+ * have a nasty bug limiting the RTC year byte to the range 94-99.
+ * Any year between 2000 and 2093 gets changed to 2094, every time
+ * you start the system.
+ * With the --badyear option, we write the date to file and hope
+ * that the file is updated at least once a year.
+ * I recommend putting this command "hwclock --badyear" in the monthly
+ * crontab, just to be safe. -- Dave Coffin 11/12/98
+ */
+static void
+write_date_to_file (struct tm *tm) {
+ FILE *fp;
+
+ if ((fp = fopen(LASTDATE,"w"))) {
+ fprintf(fp,"%02d.%02d.%04d\n", tm->tm_mday, tm->tm_mon+1,
+ tm->tm_year+1900);
+ fclose(fp);
+ } else
+ perror(LASTDATE);
+}
+
+static void
+read_date_from_file (struct tm *tm) {
+ int last_mday, last_mon, last_year;
+ FILE *fp;
+
+ if ((fp = fopen(LASTDATE,"r"))) {
+ if (fscanf (fp,"%d.%d.%d\n", &last_mday, &last_mon, &last_year) == 3) {
+ tm->tm_year = last_year-1900;
+ if ((tm->tm_mon << 5) + tm->tm_mday < ((last_mon-1) << 5) + last_mday)
+ tm->tm_year ++;
+ }
+ fclose(fp);
+ }
+ write_date_to_file (tm);
+}
+
+double
+time_diff(struct timeval subtrahend, struct timeval subtractor) {
+/*---------------------------------------------------------------------------
+ The difference in seconds between two times in "timeval" format.
+----------------------------------------------------------------------------*/
+ return (subtrahend.tv_sec - subtractor.tv_sec)
+ + (subtrahend.tv_usec - subtractor.tv_usec) / 1E6;
+}
+
+
+static struct timeval
+time_inc(struct timeval addend, double increment) {
+/*----------------------------------------------------------------------------
+ The time, in "timeval" format, which is <increment> seconds after
+ the time <addend>. Of course, <increment> may be negative.
+-----------------------------------------------------------------------------*/
+ struct timeval newtime;
+
+ newtime.tv_sec = addend.tv_sec + (int) increment;
+ newtime.tv_usec = addend.tv_usec + (increment - (int) increment) * 1E6;
+
+ /* Now adjust it so that the microsecond value is between 0 and 1 million */
+ if (newtime.tv_usec < 0) {
+ newtime.tv_usec += 1E6;
+ newtime.tv_sec -= 1;
+ } else if (newtime.tv_usec >= 1E6) {
+ newtime.tv_usec -= 1E6;
+ newtime.tv_sec += 1;
+ }
+ return newtime;
+}
+
+
+static bool
+hw_clock_is_utc(const bool utc, const bool local_opt,
+ const struct adjtime adjtime) {
+ bool ret;
+
+ if (utc)
+ ret = TRUE; /* --utc explicitly given on command line */
+ else if (local_opt)
+ ret = FALSE; /* --localtime explicitly given */
+ else
+ /* get info from adjtime file - default is local */
+ ret = (adjtime.local_utc == UTC);
+ if (debug)
+ printf(_("Assuming hardware clock is kept in %s time.\n"),
+ ret ? _("UTC") : _("local"));
+ return ret;
+}
+
+
+
+static int
+read_adjtime(struct adjtime *adjtime_p) {
+/*----------------------------------------------------------------------------
+ Read the adjustment parameters out of the /etc/adjtime file.
+
+ Return them as the adjtime structure <*adjtime_p>.
+ If there is no /etc/adjtime file, return defaults.
+ If values are missing from the file, return defaults for them.
+
+ return value 0 if all OK, !=0 otherwise.
+
+-----------------------------------------------------------------------------*/
+ FILE *adjfile;
+ int rc; /* local return code */
+ struct stat statbuf; /* We don't even use the contents of this. */
+
+ rc = stat(adj_file_name, &statbuf);
+ if (rc < 0 && errno == ENOENT) {
+ /* He doesn't have a adjtime file, so we'll use defaults. */
+ adjtime_p->drift_factor = 0;
+ adjtime_p->last_adj_time = 0;
+ adjtime_p->not_adjusted = 0;
+ adjtime_p->last_calib_time = 0;
+ adjtime_p->local_utc = UNKNOWN;
+ adjtime_p->dirty = FALSE; /* don't create a zero adjfile */
+
+ return 0;
+ }
+
+ adjfile = fopen(adj_file_name, "r"); /* open file for reading */
+ if (adjfile == NULL) {
+ outsyserr("cannot open file %s", adj_file_name);
+ return EX_OSFILE;
+ }
+
+ {
+ char line1[81]; /* String: first line of adjtime file */
+ char line2[81]; /* String: second line of adjtime file */
+ char line3[81]; /* String: third line of adjtime file */
+ long timeval;
+
+ if (!fgets(line1, sizeof(line1), adjfile))
+ line1[0] = '\0'; /* In case fgets fails */
+ if (!fgets(line2, sizeof(line2), adjfile))
+ line2[0] = '\0'; /* In case fgets fails */
+ if (!fgets(line3, sizeof(line3), adjfile))
+ line3[0] = '\0'; /* In case fgets fails */
+
+ fclose(adjfile);
+
+ /* Set defaults in case values are missing from file */
+ adjtime_p->drift_factor = 0;
+ adjtime_p->last_adj_time = 0;
+ adjtime_p->not_adjusted = 0;
+ adjtime_p->last_calib_time = 0;
+ timeval = 0;
+
+ sscanf(line1, "%lf %ld %lf",
+ &adjtime_p->drift_factor,
+ &timeval,
+ &adjtime_p->not_adjusted);
+ adjtime_p->last_adj_time = timeval;
+
+ sscanf(line2, "%ld", &timeval);
+ adjtime_p->last_calib_time = timeval;
+
+ if (!strcmp(line3, "UTC\n"))
+ adjtime_p->local_utc = UTC;
+ else if (!strcmp(line3, "LOCAL\n"))
+ adjtime_p->local_utc = LOCAL;
+ else {
+ adjtime_p->local_utc = UNKNOWN;
+ if (line3[0]) {
+ fprintf(stderr,
+ _("%s: Warning: unrecognized third line in adjtime file\n"),
+ MYNAME);
+ fprintf(stderr, _("(Expected: `UTC' or `LOCAL' or nothing.)\n"));
+ }
+ }
+ }
+ adjtime_p->dirty = FALSE;
+
+ if (debug) {
+ printf(_("Last drift adjustment done at %ld seconds after 1969\n"),
+ (long) adjtime_p->last_adj_time);
+ printf(_("Last calibration done at %ld seconds after 1969\n"),
+ (long) adjtime_p->last_calib_time);
+ printf(_("Hardware clock is on %s time\n"),
+ (adjtime_p->local_utc == LOCAL) ? _("local") :
+ (adjtime_p->local_utc == UTC) ? _("UTC") : _("unknown"));
+ }
+
+ return 0;
+}
+
+
+static int
+synchronize_to_clock_tick(void) {
+/*-----------------------------------------------------------------------------
+ Wait until the falling edge of the Hardware Clock's update flag so
+ that any time that is read from the clock immediately after we
+ return will be exact.
+
+ The clock only has 1 second precision, so it gives the exact time only
+ once per second, right on the falling edge of the update flag.
+
+ We wait (up to one second) either blocked waiting for an rtc device
+ or in a CPU spin loop. The former is probably not very accurate.
+
+ Return 0 if it worked, nonzero if it didn't.
+-----------------------------------------------------------------------------*/
+ int rc;
+
+ if (debug) printf(_("Waiting for clock tick...\n"));
+
+ rc = ur->synchronize_to_clock_tick();
+
+ if (debug) {
+ if (rc)
+ printf(_("...synchronization failed\n"));
+ else
+ printf(_("...got clock tick\n"));
+ }
+
+ return rc;
+}
+
+
+
+static void
+mktime_tz(struct tm tm, const bool universal,
+ bool *valid_p, time_t *systime_p) {
+/*-----------------------------------------------------------------------------
+ Convert a time in broken down format (hours, minutes, etc.) into standard
+ unix time (seconds into epoch). Return it as *systime_p.
+
+ The broken down time is argument <tm>. This broken down time is either in
+ local time zone or UTC, depending on value of logical argument "universal".
+ True means it is in UTC.
+
+ If the argument contains values that do not constitute a valid time,
+ and mktime() recognizes this, return *valid_p == false and
+ *systime_p undefined. However, mktime() sometimes goes ahead and
+ computes a fictional time "as if" the input values were valid,
+ e.g. if they indicate the 31st day of April, mktime() may compute
+ the time of May 1. In such a case, we return the same fictional
+ value mktime() does as *systime_p and return *valid_p == true.
+
+-----------------------------------------------------------------------------*/
+ time_t mktime_result; /* The value returned by our mktime() call */
+ char *zone; /* Local time zone name */
+
+ /* We use the C library function mktime(), but since it only works on
+ local time zone input, we may have to fake it out by temporarily
+ changing the local time zone to UTC.
+ */
+ zone = getenv("TZ"); /* remember original time zone */
+ if (universal) {
+ /* Set timezone to UTC */
+ setenv("TZ", "", TRUE);
+ /* Note: tzset() gets called implicitly by the time code, but only the
+ first time. When changing the environment variable, better call
+ tzset() explicitly.
+ */
+ tzset();
+ }
+ mktime_result = mktime(&tm);
+ if (mktime_result == -1) {
+ /* This apparently (not specified in mktime() documentation) means
+ the 'tm' structure does not contain valid values (however, not
+ containing valid values does _not_ imply mktime() returns -1).
+ */
+ *valid_p = FALSE;
+ *systime_p = 0;
+ if (debug)
+ printf(_("Invalid values in hardware clock: "
+ "%4d/%.2d/%.2d %.2d:%.2d:%.2d\n"),
+ tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ } else {
+ *valid_p = TRUE;
+ *systime_p = mktime_result;
+ if (debug)
+ printf(_("Hw clock time : %4d/%.2d/%.2d %.2d:%.2d:%.2d = "
+ "%ld seconds since 1969\n"),
+ tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, (long) *systime_p);
+ }
+ /* now put back the original zone. */
+ if (zone) setenv("TZ", zone, TRUE);
+ else unsetenv("TZ");
+ tzset();
+}
+
+
+static int
+read_hardware_clock(const bool universal, bool *valid_p, time_t *systime_p){
+/*----------------------------------------------------------------------------
+ Read the hardware clock and return the current time via <tm> argument.
+
+ Use the method indicated by <method> argument to access the hardware clock.
+-----------------------------------------------------------------------------*/
+ struct tm tm;
+ int err;
+
+ err = ur->read_hardware_clock(&tm);
+ if (err)
+ return err;
+
+ if (badyear)
+ read_date_from_file(&tm);
+
+ if (debug)
+ printf (_("Time read from Hardware Clock: %4d/%.2d/%.2d %02d:%02d:%02d\n"),
+ tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ mktime_tz(tm, universal, valid_p, systime_p);
+
+ return 0;
+}
+
+
+static void
+set_hardware_clock(const time_t newtime,
+ const bool universal,
+ const bool testing) {
+/*----------------------------------------------------------------------------
+ Set the Hardware Clock to the time <newtime>, in local time zone or UTC,
+ according to <universal>.
+----------------------------------------------------------------------------*/
+ int err;
+ struct tm new_broken_time;
+ /* Time to which we will set Hardware Clock, in broken down format, in
+ the time zone of caller's choice
+ */
+
+ if (universal)
+ new_broken_time = *gmtime(&newtime);
+ else
+ new_broken_time = *localtime(&newtime);
+
+ if (debug)
+ printf(_("Setting Hardware Clock to %.2d:%.2d:%.2d "
+ "= %ld seconds since 1969\n"),
+ new_broken_time.tm_hour, new_broken_time.tm_min,
+ new_broken_time.tm_sec, (long) newtime);
+
+ if (testing)
+ printf(_("Clock not changed - testing only.\n"));
+ else {
+ if (badyear) {
+ /*
+ * Write the real year to a file, then write a fake year
+ * between 1995 and 1998 to the RTC. This way, Award BIOS boots
+ * on 29 Feb 2000 thinking that it's 29 Feb 1996.
+ */
+ write_date_to_file (&new_broken_time);
+ new_broken_time.tm_year = 95 + ((new_broken_time.tm_year+1) & 3);
+ }
+ err = ur->set_hardware_clock(&new_broken_time);
+ }
+}
+
+
+
+static void
+set_hardware_clock_exact(const time_t sethwtime,
+ const struct timeval refsystime,
+ const bool universal,
+ const bool testing) {
+/*----------------------------------------------------------------------------
+ Set the Hardware Clock to the time "sethwtime", in local time zone or UTC,
+ according to "universal".
+
+ Wait for a fraction of a second so that "sethwtime" is the value of
+ the Hardware Clock as of system time "refsystime", which is in the past.
+ For example, if "sethwtime" is 14:03:05 and "refsystime" is 12:10:04.5
+ and the current system time is 12:10:06.0: Wait .5 seconds (to make
+ exactly 2 seconds since "refsystime") and then set the Hardware Clock
+ to 14:03:07, thus getting a precise and retroactive setting of the clock.
+
+ (Don't be confused by the fact that the system clock and the Hardware
+ Clock differ by two hours in the above example. That's just to remind
+ you that there are two independent time scales here).
+
+ This function ought to be able to accept set times as fractional times.
+ Idea for future enhancement.
+-----------------------------------------------------------------------------*/
+
+ time_t newhwtime;
+ struct timeval beginsystime, nowsystime;
+ double tdiff;
+
+ time_resync:
+ gettimeofday(&beginsystime, NULL);
+ tdiff = time_diff(beginsystime, refsystime);
+ newhwtime = sethwtime + (int) (tdiff + 0.5);
+ if (debug)
+ printf(_("Time elapsed since reference time has been %.6f seconds.\n"
+ "Delaying further to reach the new time.\n"), tdiff);
+
+ /*
+ * Now delay some more until Hardware Clock time newhwtime arrives. The 0.5 s
+ * is because the Hardware Clock always sets to your set time plus 500 ms
+ * (because it is designed to update to the next second precisely 500 ms
+ * after you finish the setting).
+ */
+ do {
+ gettimeofday(&nowsystime, NULL);
+ tdiff = time_diff(nowsystime, beginsystime);
+ if (tdiff < 0)
+ goto time_resync; /* probably backward time reset */
+ if (tdiff > 0.1)
+ goto time_resync; /* probably forward time reset */
+ beginsystime = nowsystime;
+ tdiff = time_diff(nowsystime, refsystime);
+ } while (newhwtime == sethwtime + (int) (tdiff + 0.5));
+
+ set_hardware_clock(newhwtime, universal, testing);
+}
+
+
+
+static void
+display_time(const bool hclock_valid, const time_t systime,
+ const double sync_duration) {
+/*----------------------------------------------------------------------------
+ Put the time "systime" on standard output in display format.
+ Except if hclock_valid == false, just tell standard output that we don't
+ know what time it is.
+
+ Include in the output the adjustment "sync_duration".
+-----------------------------------------------------------------------------*/
+ if (!hclock_valid)
+ fprintf(stderr, _("The Hardware Clock registers contain values that are "
+ "either invalid (e.g. 50th day of month) or beyond the range "
+ "we can handle (e.g. Year 2095).\n"));
+ else {
+ struct tm *lt;
+ char *format = "%c";
+ char ctime_now[200];
+
+ lt = localtime(&systime);
+ strftime(ctime_now, sizeof(ctime_now), format, lt);
+ printf(_("%s %.6f seconds\n"), ctime_now, -(sync_duration));
+ }
+}
+
+
+
+static int
+interpret_date_string(const char *date_opt, time_t * const time_p) {
+/*----------------------------------------------------------------------------
+ Interpret the value of the --date option, which is something like
+ "13:05:01". In fact, it can be any of the myriad ASCII strings that specify
+ a time which the "date" program can understand. The date option value in
+ question is our "dateopt" argument.
+
+ The specified time is in the local time zone.
+
+ Our output, "*time_p", is a seconds-into-epoch time.
+
+ We use the "date" program to interpret the date string. "date" must be
+ runnable by issuing the command "date" to the /bin/sh shell. That means
+ in must be in the current PATH.
+
+ If anything goes wrong (and many things can), we return return code
+ 10 and arbitrary *time_p. Otherwise, return code is 0 and *time_p
+ is valid.
+----------------------------------------------------------------------------*/
+ FILE *date_child_fp;
+ char date_resp[100];
+ const char magic[]="seconds-into-epoch=";
+ char date_command[100];
+ int retcode; /* our eventual return code */
+ int rc; /* local return code */
+
+ if (date_opt == NULL) {
+ fprintf(stderr, _("No --date option specified.\n"));
+ return 14;
+ }
+
+ /* prevent overflow - a security risk */
+ if (strlen(date_opt) > sizeof(date_command) - 50) {
+ fprintf(stderr, _("--date argument too long\n"));
+ return 13;
+ }
+
+ /* Quotes in date_opt would ruin the date command we construct. */
+ if (strchr(date_opt, '"') != NULL) {
+ fprintf(stderr,
+ _("The value of the --date option is not a valid date.\n"
+ "In particular, it contains quotation marks.\n"));
+ return 12;
+ }
+
+ sprintf(date_command, "date --date=\"%s\" +seconds-into-epoch=%%s",
+ date_opt);
+ if (debug)
+ printf(_("Issuing date command: %s\n"), date_command);
+
+ date_child_fp = popen(date_command, "r");
+ if (date_child_fp == NULL) {
+ outsyserr(_("Unable to run 'date' program in /bin/sh shell. "
+ "popen() failed"));
+ return 10;
+ }
+
+ if (!fgets(date_resp, sizeof(date_resp), date_child_fp))
+ date_resp[0] = '\0'; /* in case fgets fails */
+ if (debug)
+ printf(_("response from date command = %s\n"), date_resp);
+ if (strncmp(date_resp, magic, sizeof(magic)-1) != 0) {
+ fprintf(stderr, _("The date command issued by %s returned "
+ "unexpected results.\n"
+ "The command was:\n %s\n"
+ "The response was:\n %s\n"),
+ MYNAME, date_command, date_resp);
+ retcode = 8;
+ } else {
+ long seconds_since_epoch;
+ rc = sscanf(date_resp + sizeof(magic)-1, "%ld",
+ &seconds_since_epoch);
+ if (rc < 1) {
+ fprintf(stderr,
+ _("The date command issued by %s returned "
+ "something other than an integer where the "
+ "converted time value was expected.\n"
+ "The command was:\n %s\n"
+ "The response was:\n %s\n"),
+ MYNAME, date_command, date_resp);
+ retcode = 6;
+ } else {
+ retcode = 0;
+ *time_p = seconds_since_epoch;
+ if (debug)
+ printf(_("date string %s equates to "
+ "%ld seconds since 1969.\n"),
+ date_opt, (long) *time_p);
+ }
+ }
+ pclose(date_child_fp);
+
+ return retcode;
+}
+
+
+
+static int
+set_system_clock(const bool hclock_valid, const time_t newtime,
+ const bool testing) {
+/*----------------------------------------------------------------------------
+ Set the System Clock to time 'newtime'.
+
+ 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.
+
+ EXCEPT: if hclock_valid is false, just issue an error message
+ saying there is no valid time in the Hardware Clock to which to set
+ the system time.
+
+ If 'testing' is true, don't actually update anything -- just say we
+ would have.
+-----------------------------------------------------------------------------*/
+ int retcode;
+
+ if (!hclock_valid) {
+ fprintf(stderr, _("The Hardware Clock does not contain a valid time, so "
+ "we cannot set the System Time from it.\n"));
+ retcode = 1;
+ } else {
+ struct timeval tv;
+ struct tm *broken;
+ int minuteswest;
+ int rc;
+
+ tv.tv_sec = newtime;
+ tv.tv_usec = 0;
+
+ broken = localtime(&newtime);
+#ifdef HAVE_TM_GMTOFF
+ minuteswest = -broken->tm_gmtoff/60; /* GNU extension */
+#else
+ minuteswest = timezone/60;
+ if (broken->tm_isdst)
+ minuteswest -= 60;
+#endif
+
+ if (debug) {
+ printf(_("Calling settimeofday:\n"));
+ 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 int
+set_system_clock_timezone(const bool universal, const bool testing) {
+/*----------------------------------------------------------------------------
+ Reset the System Clock from local time to UTC, based on its current
+ value and the timezone unless universal is TRUE.
+
+ 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);
+ if (!universal)
+ 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,
+ const bool hclock_valid,
+ const time_t hclocktime,
+ const double sync_delay) {
+/*------------------------------------------------------------------------
+ Update the drift factor in <*adjtime_p> to reflect the fact that the
+ Hardware Clock was calibrated to <nowtime> and before that was set
+ to <hclocktime>.
+
+ We record in the adjtime file the time at which we last calibrated
+ the clock so we can compute the drift rate each time we calibrate.
+
+ EXCEPT: if <hclock_valid> is false, assume Hardware Clock was not set
+ before to anything meaningful and regular adjustments have not been
+ done, so don't adjust the drift factor.
+ ------------------------------------------------------------------------*/
+ if (!hclock_valid) {
+ if (debug)
+ printf(_("Not adjusting drift factor because the "
+ "Hardware Clock previously contained "
+ "garbage.\n"));
+ } else if (adjtime_p->last_calib_time == 0) {
+ if (debug)
+ printf(_("Not adjusting drift factor because last "
+ "calibration time is zero,\n"
+ "so history is bad and calibration startover "
+ "is necessary.\n"));
+ } else if ((hclocktime - adjtime_p->last_calib_time) < 23 * 60 * 60) {
+ if (debug)
+ printf(_("Not adjusting drift factor because it has "
+ "been less than a day since the last "
+ "calibration.\n"));
+ } else if (adjtime_p->last_calib_time != 0) {
+ /*
+ * At adjustment time we adjust the hardware clock according
+ * to the contents of /etc/adjtime.
+ *
+ * At calibration time we set the hardware clock and
+ * update /etc/adjtime, that is, for each calibration
+ * (except the first) we also do an adjustment.
+ *
+ * We are now at calibration time.
+ *
+ * Let us do computation in doubles. (Floats almost suffice,
+ * but 195 days + 1 second equals 195 days in floats.)
+ */
+ const double sec_per_day = 24.0 * 60.0 * 60.0;
+ double atime_per_htime;
+ double adj_days, cal_days;
+ double exp_drift, unc_drift;
+ double factor_adjust;
+
+ /* Adjusted time units per hardware time unit */
+ atime_per_htime = 1.0 + adjtime_p->drift_factor / sec_per_day;
+
+ /* Days since last adjustment (in hardware clock time) */
+ adj_days = (double)(hclocktime - adjtime_p->last_adj_time)
+ / sec_per_day;
+
+ /* Expected drift (sec) since last adjustment */
+ exp_drift = adj_days * adjtime_p->drift_factor
+ + adjtime_p->not_adjusted;
+
+ /* Uncorrected drift (sec) since last calibration */
+ unc_drift = (double)(nowtime - hclocktime)
+ + sync_delay - exp_drift;
+
+ /* Days since last calibration (in hardware clock time) */
+ cal_days = ((double)(adjtime_p->last_adj_time
+ - adjtime_p->last_calib_time)
+ + adjtime_p->not_adjusted)
+ / (sec_per_day * atime_per_htime) + adj_days;
+
+ /* Amount to add to previous drift factor */
+ factor_adjust = unc_drift / cal_days;
+
+ if (debug)
+ printf(_("Clock drifted %.1f seconds in the past "
+ "%d seconds in spite of a drift factor of "
+ "%f seconds/day.\n"
+ "Adjusting drift factor by %f seconds/day\n"),
+ unc_drift,
+ (int) (nowtime - adjtime_p->last_calib_time),
+ adjtime_p->drift_factor,
+ factor_adjust);
+
+ adjtime_p->drift_factor += factor_adjust;
+ }
+ adjtime_p->last_calib_time = nowtime;
+
+ adjtime_p->last_adj_time = nowtime;
+
+ adjtime_p->not_adjusted = 0;
+
+ adjtime_p->dirty = TRUE;
+}
+
+
+
+static void
+calculate_adjustment(const double factor,
+ const time_t last_time,
+ const double not_adjusted,
+ const time_t systime,
+ int *adjustment_p,
+ double *retro_p) {
+/*----------------------------------------------------------------------------
+ Do the drift adjustment calculation.
+
+ The way we have to set the clock, we need the adjustment in two parts:
+
+ 1) an integer number of seconds (return as *adjustment_p)
+
+ 2) a positive fraction of a second (less than 1) (return as *retro_p)
+
+ The sum of these two values is the adjustment needed. Positive means to
+ advance the clock or insert seconds. Negative means to retard the clock
+ or remove seconds.
+----------------------------------------------------------------------------*/
+ double exact_adjustment;
+
+ exact_adjustment = ((double) (systime - last_time)) * factor / (24 * 60 * 60)
+ + not_adjusted;
+ *adjustment_p = FLOOR(exact_adjustment);
+
+ *retro_p = exact_adjustment - (double) *adjustment_p;
+ if (debug) {
+ printf (_("Time since last adjustment is %d seconds\n"),
+ (int) (systime - last_time));
+ printf (_("Need to insert %d seconds and refer time back "
+ "%.6f seconds ago\n"),
+ *adjustment_p, *retro_p);
+ }
+}
+
+
+
+static void
+save_adjtime(const struct adjtime adjtime, const bool testing) {
+/*-----------------------------------------------------------------------------
+ Write the contents of the <adjtime> structure to its disk file.
+
+ But if the contents are clean (unchanged since read from disk), don't
+ bother.
+-----------------------------------------------------------------------------*/
+ char newfile[412]; /* Stuff to write to disk file */
+
+ if (adjtime.dirty) {
+ /* snprintf is not always available, but this is safe
+ as long as libc does not use more than 100 positions for %ld or %f */
+ sprintf(newfile, "%f %ld %f\n%ld\n%s\n",
+ adjtime.drift_factor,
+ (long) adjtime.last_adj_time,
+ adjtime.not_adjusted,
+ (long) adjtime.last_calib_time,
+ (adjtime.local_utc == UTC) ? "UTC" : "LOCAL");
+
+ if (testing) {
+ printf(_("Not updating adjtime file because of testing mode.\n"));
+ printf(_("Would have written the following to %s:\n%s"),
+ adj_file_name, newfile);
+ } else {
+ FILE *adjfile;
+ int err = 0;
+
+ adjfile = fopen(adj_file_name, "w");
+ if (adjfile == NULL) {
+ outsyserr(_("Could not open file with the clock adjustment parameters "
+ "in it (%s) for writing"), adj_file_name);
+ err = 1;
+ } else {
+ if (fputs(newfile, adjfile) < 0) {
+ outsyserr(_("Could not update file with the clock adjustment "
+ "parameters (%s) in it"), adj_file_name);
+ err = 1;
+ }
+ if (fclose(adjfile) < 0) {
+ outsyserr(_("Could not update file with the clock adjustment "
+ "parameters (%s) in it"), adj_file_name);
+ err = 1;
+ }
+ }
+ if (err)
+ fprintf(stderr, _("Drift adjustment parameters not updated.\n"));
+ }
+ }
+}
+
+
+
+static void
+do_adjustment(struct adjtime *adjtime_p,
+ const bool hclock_valid, const time_t hclocktime,
+ const struct timeval read_time,
+ const bool universal, const bool testing) {
+/*---------------------------------------------------------------------------
+ Do the adjustment requested, by 1) setting the Hardware Clock (if
+ necessary), and 2) updating the last-adjusted time in the adjtime
+ structure.
+
+ Do not update anything if the Hardware Clock does not currently present
+ a valid time.
+
+ arguments <factor> and <last_time> are current values from the adjtime
+ file.
+
+ <hclock_valid> means the Hardware Clock contains a valid time, and that
+ time is <hclocktime>.
+
+ <read_time> is the current system time (to be precise, it is the system
+ time at the time <hclocktime> was read, which due to computational delay
+ could be a short time ago).
+
+ <universal>: the Hardware Clock is kept in UTC.
+
+ <testing>: We are running in test mode (no updating of clock).
+
+ We do not bother to update the clock if the adjustment would be less than
+ one second. This is to avoid cumulative error and needless CPU hogging
+ (remember we use an infinite loop for some timing) if the user runs us
+ frequently.
+
+----------------------------------------------------------------------------*/
+ if (!hclock_valid) {
+ fprintf(stderr, _("The Hardware Clock does not contain a valid time, "
+ "so we cannot adjust it.\n"));
+ adjtime_p->last_calib_time = 0; /* calibration startover is required */
+ adjtime_p->last_adj_time = 0;
+ adjtime_p->not_adjusted = 0;
+ adjtime_p->dirty = TRUE;
+ } else if (adjtime_p->last_adj_time == 0) {
+ if (debug)
+ printf(_("Not setting clock because last adjustment time is zero, "
+ "so history is bad."));
+ } else {
+ int adjustment;
+ /* Number of seconds we must insert in the Hardware Clock */
+ double retro;
+ /* Fraction of second we have to remove from clock after inserting
+ <adjustment> whole seconds.
+ */
+ calculate_adjustment(adjtime_p->drift_factor,
+ adjtime_p->last_adj_time,
+ adjtime_p->not_adjusted,
+ hclocktime,
+ &adjustment, &retro);
+ if (adjustment > 0 || adjustment < -1) {
+ set_hardware_clock_exact(hclocktime + adjustment,
+ time_inc(read_time, -retro),
+ universal, testing);
+ adjtime_p->last_adj_time = hclocktime + adjustment;
+ adjtime_p->not_adjusted = 0;
+ adjtime_p->dirty = TRUE;
+ } else
+ if (debug)
+ printf(_("Needed adjustment is less than one second, "
+ "so not setting clock.\n"));
+ }
+}
+
+
+
+static void
+determine_clock_access_method(const bool user_requests_ISA) {
+
+ ur = NULL;
+
+ if (user_requests_ISA)
+ ur = probe_for_cmos_clock();
+
+ if (!ur)
+ ur = probe_for_rtc_clock();
+
+ if (!ur)
+ ur = probe_for_kd_clock();
+
+ /*
+ * This final clause is a really bad idea on x86/AT PCs. You run the
+ * risk of a race condition with another copy of hwclock
+ * that already has /dev/rtc open. The fallback case on
+ * x86 is to then raise I/O priviledge level and access
+ * the RTC CMOS directly using I/O instructions. Simultaneous
+ * access like that can really hose the RTC.
+ */
+#if !defined(__i386__)
+ if (!ur && !user_requests_ISA)
+ ur = probe_for_cmos_clock();
+#endif
+
+ if (debug) {
+ if (ur)
+ printf(_("Using %s.\n"), ur->interface_name);
+ else
+ printf(_("No usable clock interface found.\n"));
+ }
+}
+
+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 systz,
+ const struct timeval startup_time,
+ const bool utc, const bool local_opt,
+ const bool testing) {
+/*---------------------------------------------------------------------------
+ Do all the normal work of hwclock - read, set clock, etc.
+
+ Issue output to stdout and error message to stderr where appropriate.
+
+ Return rc == 0 if everything went OK, rc != 0 if not.
+----------------------------------------------------------------------------*/
+ struct adjtime adjtime;
+ /* Contents of the adjtime file, or what they should be. */
+ int rc; /* local return code */
+ bool no_auth; /* User lacks necessary authorization to access the clock */
+
+ if (!systz) {
+ no_auth = ur->get_permissions();
+ if (no_auth)
+ return EX_NOPERM;
+ }
+
+ if (!noadjfile && (adjust || set || systohc || (!utc && !local_opt))) {
+ rc = read_adjtime(&adjtime);
+ if (rc)
+ return rc;
+ } else {
+ /* A little trick to avoid reading the file if we don't have to */
+ adjtime.dirty = FALSE;
+ rc = 0;
+ }
+
+ {
+ const bool universal = hw_clock_is_utc(utc, local_opt, adjtime);
+
+ if ((set || systohc || adjust) &&
+ (adjtime.local_utc == UTC) != universal) {
+ adjtime.local_utc = universal ? UTC : LOCAL;
+ adjtime.dirty = TRUE;
+ }
+
+ {
+ struct timeval read_time;
+ /* The time at which we read the Hardware Clock */
+
+ bool hclock_valid = FALSE;
+ /* The Hardware Clock gives us a valid time, or at least something
+ close enough to fool mktime().
+ */
+
+ time_t hclocktime = 0;
+ /* The time the hardware clock had just after we
+ synchronized to its next clock tick when we started up.
+ Defined only if hclock_valid is true.
+ */
+
+ if (show || adjust || hctosys || (!noadjfile && !systz)) {
+ /* data from HW-clock are required */
+ rc = synchronize_to_clock_tick();
+ if (rc && rc != 2) /* 2= synchronization timeout */
+ return EX_IOERR;
+ gettimeofday(&read_time, NULL);
+ rc = read_hardware_clock(universal, &hclock_valid, &hclocktime);
+ if (rc)
+ return EX_IOERR;
+ }
+
+ if (show) {
+ display_time(hclock_valid, hclocktime,
+ time_diff(read_time, startup_time));
+ } else if (set) {
+ set_hardware_clock_exact(set_time, startup_time,
+ universal, testing);
+ if (!noadjfile)
+ adjust_drift_factor(&adjtime, set_time, hclock_valid, hclocktime,
+ time_diff(read_time, startup_time));
+ } else if (adjust) {
+ do_adjustment(&adjtime, hclock_valid, hclocktime,
+ read_time, universal, testing);
+ } else if (systohc) {
+ struct timeval nowtime, reftime;
+ /* We can only set_hardware_clock_exact to a whole seconds
+ time, so we set it with reference to the most recent
+ whole seconds time.
+ */
+ gettimeofday(&nowtime, NULL);
+ reftime.tv_sec = nowtime.tv_sec;
+ reftime.tv_usec = 0;
+
+ set_hardware_clock_exact((time_t) reftime.tv_sec, reftime,
+ universal, testing);
+ if (!noadjfile)
+ adjust_drift_factor(&adjtime, (time_t) reftime.tv_sec, hclock_valid,
+ hclocktime, (double) read_time.tv_usec / 1E6);
+ } else if (hctosys) {
+ rc = set_system_clock(hclock_valid, hclocktime, testing);
+ if (rc) {
+ printf(_("Unable to set system clock.\n"));
+ return rc;
+ }
+ } else if (systz) {
+ rc = set_system_clock_timezone(universal, testing);
+ if (rc) {
+ printf(_("Unable to set system clock.\n"));
+ return rc;
+ }
+ }
+ if (!noadjfile)
+ save_adjtime(adjtime, testing);
+ }
+ }
+ return 0;
+}
+
+
+static void
+manipulate_epoch(const bool getepoch, const bool setepoch,
+ const int epoch_opt, const bool testing) {
+/*----------------------------------------------------------------------------
+ Get or set the Hardware Clock epoch value in the kernel, as appropriate.
+ <getepoch>, <setepoch>, and <epoch> are hwclock invocation options.
+
+ <epoch> == -1 if the user did not specify an "epoch" option.
+
+-----------------------------------------------------------------------------*/
+ /*
+ Maintenance note: This should work on non-Alpha machines, but the
+ evidence today (98.03.04) indicates that the kernel only keeps the
+ epoch value on Alphas. If that is ever fixed, this function should be
+ changed.
+ */
+
+#ifndef __alpha__
+ fprintf(stderr, _("The kernel keeps an epoch value for the Hardware Clock "
+ "only on an Alpha machine.\nThis copy of hwclock was built for "
+ "a machine other than Alpha\n(and thus is presumably not running "
+ "on an Alpha now). No action taken.\n"));
+#else
+ if (getepoch) {
+ unsigned long epoch;
+
+ if (get_epoch_rtc(&epoch, 0))
+ fprintf(stderr, _("Unable to get the epoch value from the kernel.\n"));
+ else
+ printf(_("Kernel is assuming an epoch value of %lu\n"), epoch);
+ } else if (setepoch) {
+ if (epoch_opt == -1)
+ fprintf(stderr, _("To set the epoch value, you must use the 'epoch' "
+ "option to tell to what value to set it.\n"));
+ else if (testing)
+ printf(_("Not setting the epoch to %d - testing only.\n"),
+ epoch_opt);
+ else if (set_epoch_rtc(epoch_opt))
+ printf(_("Unable to set the epoch value in the kernel.\n"));
+ }
+#endif
+}
+
+#ifdef __ia64__
+#define RTC_DEV "/dev/efirtc"
+#else
+#define RTC_DEV "/dev/rtc"
+#endif
+
+static void
+out_version(void) {
+ printf(_("%s from %s\n"), MYNAME, PACKAGE_STRING);
+}
+
+/*
+ usage - Output (error and) usage information
+
+ This function is called both directly from main to show usage
+ information and as fatal function from shhopt if some argument is
+ not understood. In case of normal usage info FMT should be NULL.
+ In that case the info is printed to stdout. If FMT is given
+ usage will act like fprintf( stderr, fmt, ... ), show a usage
+ information and terminate the program afterwards.
+*/
+static void
+usage( const char *fmt, ... ) {
+ FILE *usageto;
+ va_list ap;
+
+ usageto = fmt ? stderr : stdout;
+
+ fprintf( usageto, _(
+ "hwclock - query and set the hardware clock (RTC)\n\n"
+ "Usage: hwclock [function] [options...]\n\n"
+ "Functions:\n"
+ " -h | --help show this help\n"
+ " -r | --show read hardware clock and print result\n"
+ " --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"
+ " --setepoch set the kernel's hardware clock epoch value to the \n"
+ " value given with --epoch\n"
+ " -v | --version print out the version of hwclock to stdout\n"
+ "\nOptions: \n"
+ " -u | --utc the hardware clock is kept in UTC\n"
+ " --localtime the hardware clock is kept in local time\n"
+ " -f | --rtc=path special /dev/... file to use instead of default\n"
+ " --directisa access the ISA bus directly instead of %s\n"
+ " --badyear ignore rtc's year because the bios is broken\n"
+ " --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"
+ " --adjfile=path specifies the path to the adjust file (default is\n"
+ " /etc/adjtime)\n"
+ " --test do everything except actually updating the hardware\n"
+ " clock or anything else\n"
+ " -D | --debug debug mode\n"
+ "\n"
+ ),RTC_DEV);
+#ifdef __alpha__
+ fprintf(usageto, _(
+ " -J|--jensen, -A|--arc, -S|--srm, -F|--funky-toy\n"
+ " tell hwclock the type of alpha you have (see hwclock(8))\n"
+ "\n"
+ ) );
+#endif
+
+ fflush(stdout);
+ if (fmt) {
+ usageto = stderr;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ hwclock_exit(fmt ? EX_USAGE : 0);
+}
+
+static const struct option longopts[] = {
+ { "adjust", 0, 0, 'a' },
+ { "help", 0, 0, 'h' },
+ { "show", 0, 0, 'r' },
+ { "hctosys", 0, 0, 's' },
+ { "utc", 0, 0, 'u' },
+ { "version", 0, 0, 'v' },
+ { "systohc", 0, 0, 'w' },
+ { "debug", 0, 0, 'D' },
+#ifdef __alpha__
+ { "ARC", 0, 0, 'A' },
+ { "arc", 0, 0, 'A' },
+ { "Jensen", 0, 0, 'J' },
+ { "jensen", 0, 0, 'J' },
+ { "SRM", 0, 0, 'S' },
+ { "srm", 0, 0, 'S' },
+ { "funky-toy", 0, 0, 'F'},
+#endif
+ { "set", 0, 0, 128 },
+ { "getepoch", 0, 0, 129 },
+ { "setepoch", 0, 0, 130 },
+ { "noadjfile", 0, 0, 131 },
+ { "localtime", 0, 0, 132 },
+ { "badyear", 0, 0, 133 },
+ { "directisa", 0, 0, 134 },
+ { "test", 0, 0, 135 },
+ { "date", 1, 0, 136 },
+ { "epoch", 1, 0, 137 },
+ { "rtc", 1, 0, 'f' },
+ { "adjfile", 1, 0, 138 },
+ { "systz", 0, 0, 139 },
+ { NULL, 0, 0, 0 }
+};
+
+/*
+ * Returns:
+ * EX_USAGE: bad invocation
+ * EX_NOPERM: no permission
+ * EX_OSFILE: cannot open /dev/rtc or /etc/adjtime
+ * EX_IOERR: ioctl error getting or setting the time
+ * 0: OK (or not)
+ * 1: failure
+ */
+int
+main(int argc, char **argv) {
+
+ struct timeval startup_time;
+ /* The time we started up, in seconds into the epoch, including
+ fractions. */
+ time_t set_time = 0; /* Time to which user said to set Hardware Clock */
+
+ bool permitted; /* User is permitted to do the function */
+ int rc, c;
+
+ /* 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, systz, adjust, getepoch, setepoch;
+ bool utc, testing, local_opt, noadjfile, directisa;
+ bool ARCconsole, Jensen, SRM, funky_toy;
+ char *date_opt;
+
+ /* Remember what time we were invoked */
+ gettimeofday(&startup_time, NULL);
+
+#ifdef HAVE_LIBAUDIT
+ hwaudit_fd = audit_open();
+ if (hwaudit_fd < 0 && !(errno == EINVAL || errno == EPROTONOSUPPORT ||
+ errno == EAFNOSUPPORT)) {
+ /* You get these error codes only when the kernel doesn't have
+ * audit compiled in. */
+ fprintf(stderr, _("%s: Unable to connect to audit system\n"),
+ MYNAME);
+ return EX_NOPERM;
+ }
+#endif
+ setlocale(LC_ALL, "");
+#ifdef LC_NUMERIC
+ /* We need LC_CTYPE and LC_TIME and LC_MESSAGES, but must avoid
+ LC_NUMERIC since it gives problems when we write to /etc/adjtime.
+ - gqueri@mail.dotcom.fr */
+ setlocale(LC_NUMERIC, "C");
+#endif
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ /* Set option defaults */
+ 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;
+
+ while ((c = getopt_long (argc, argv, "?hvVDarsuwAJSFf:", longopts, NULL))
+ != -1) {
+ switch (c) {
+ case 'D':
+ debug = TRUE;
+ break;
+ case 'a':
+ adjust = TRUE;
+ break;
+ case 'r':
+ show = TRUE;
+ break;
+ case 's':
+ hctosys = TRUE;
+ break;
+ case 'u':
+ utc = TRUE;
+ break;
+ case 'w':
+ systohc = TRUE;
+ break;
+#ifdef __alpha__
+ case 'A':
+ ARCconsole = TRUE;
+ break;
+ case 'J':
+ Jensen = TRUE;
+ break;
+ case 'S':
+ SRM = TRUE;
+ break;
+ case 'F':
+ funky_toy = TRUE;
+ break;
+#endif
+ case 128:
+ set = TRUE;
+ break;
+ case 129:
+ getepoch = TRUE;
+ break;
+ case 130:
+ setepoch = TRUE;
+ break;
+ case 131:
+ noadjfile = TRUE;
+ break;
+ case 132:
+ local_opt = TRUE; /* --localtime */
+ break;
+ case 133:
+ badyear = TRUE;
+ break;
+ case 134:
+ directisa = TRUE;
+ break;
+ case 135:
+ testing = TRUE; /* --test */
+ break;
+ case 136:
+ date_opt = optarg; /* --date */
+ break;
+ case 137:
+ epoch_option = atoi(optarg); /* --epoch */
+ break;
+ case 138:
+ adj_file_name = optarg; /* --adjfile */
+ break;
+ case 139:
+ systz = TRUE; /* --systz */
+ break;
+ case 'f':
+ rtc_dev_name = optarg; /* --rtc */
+ break;
+ case 'v': /* --version */
+ case 'V':
+ out_version();
+ return 0;
+ case 'h': /* --help */
+ case '?':
+ default:
+ usage(NULL);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+#ifdef HAVE_LIBAUDIT
+ if (testing != TRUE) {
+ if (adjust == TRUE || hctosys == TRUE || systohc == TRUE ||
+ set == TRUE || setepoch == TRUE) {
+ hwaudit_on = TRUE;
+ }
+ }
+#endif
+ if (argc > 0) {
+ usage(_("%s takes no non-option arguments. "
+ "You supplied %d.\n"),
+ MYNAME, argc);
+ }
+
+ 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"));
+ hwclock_exit(EX_USAGE);
+ }
+
+ if (utc && local_opt) {
+ fprintf(stderr, _("%s: The --utc and --localtime options "
+ "are mutually exclusive. You specified "
+ "both.\n"), MYNAME);
+ hwclock_exit(EX_USAGE);
+ }
+
+ if (adjust && noadjfile) {
+ fprintf(stderr, _("%s: The --adjust and --noadjfile options "
+ "are mutually exclusive. You specified "
+ "both.\n"), MYNAME);
+ hwclock_exit(EX_USAGE);
+ }
+
+ if (adj_file_name && noadjfile) {
+ fprintf(stderr, _("%s: The --adjfile and --noadjfile options "
+ "are mutually exclusive. You specified "
+ "both.\n"), MYNAME);
+ hwclock_exit(EX_USAGE);
+ }
+ if (!adj_file_name)
+ adj_file_name = ADJPATH;
+
+ if (noadjfile && !(utc || local_opt)) {
+ fprintf(stderr, _("%s: With --noadjfile, you must specify "
+ "either --utc or --localtime\n"), MYNAME);
+ hwclock_exit(EX_USAGE);
+ }
+
+#ifdef __alpha__
+ set_cmos_epoch(ARCconsole, SRM);
+ set_cmos_access(Jensen, funky_toy);
+#endif
+
+ if (set) {
+ rc = interpret_date_string(date_opt, &set_time);
+ /* (time-consuming) */
+ if (rc != 0) {
+ fprintf(stderr, _("No usable set-to time. "
+ "Cannot set clock.\n"));
+ hwclock_exit(EX_USAGE);
+ }
+ }
+
+ if (!(show | set | systohc | hctosys | systz | adjust | getepoch
+ | setepoch))
+ show = 1; /* default to show */
+
+
+ if (getuid() == 0)
+ permitted = TRUE;
+ else {
+ /* program is designed to run setuid (in some situations) */
+ if (set || systohc || adjust) {
+ fprintf(stderr,
+ _("Sorry, only the superuser can change "
+ "the Hardware Clock.\n"));
+ permitted = FALSE;
+ } else if (systz || hctosys) {
+ fprintf(stderr,
+ _("Sorry, only the superuser can change "
+ "the System Clock.\n"));
+ permitted = FALSE;
+ } else if (setepoch) {
+ fprintf(stderr,
+ _("Sorry, only the superuser can change the "
+ "Hardware Clock epoch in the kernel.\n"));
+ permitted = FALSE;
+ } else
+ permitted = TRUE;
+ }
+
+ if (!permitted)
+ hwclock_exit(EX_NOPERM);
+
+ if (getepoch || setepoch) {
+ manipulate_epoch(getepoch, setepoch, epoch_option, testing);
+ hwclock_exit(0);
+ }
+
+ if (debug)
+ out_version();
+ if (!systz) {
+ determine_clock_access_method(directisa);
+ if (!ur) {
+ fprintf(stderr,
+ _("Cannot access the Hardware Clock via "
+ "any known method.\n"));
+ if (!debug)
+ fprintf(stderr,
+ _("Use the --debug option to see the "
+ "details of our search for an access "
+ "method.\n"));
+ hwclock_exit(1);
+ }
+ }
+
+ rc = manipulate_clock(show, adjust, noadjfile, set, set_time,
+ hctosys, systohc, systz, startup_time, utc,
+ local_opt, testing);
+ hwclock_exit(rc);
+ return rc; /* Not reached */
+}
+
+/* A single routine for greater uniformity */
+void
+outsyserr(char *msg, ...) {
+ va_list args;
+ int errsv = errno;
+
+ fprintf(stderr, "%s: ", progname);
+ va_start(args, msg);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fprintf(stderr, ", errno=%d: %s.\n",
+ errsv, strerror(errsv));
+}
+
+
+#ifdef HAVE_LIBAUDIT
+void
+hwaudit_exit(int status)
+{
+ if (hwaudit_on) {
+ audit_log_user_message(hwaudit_fd, AUDIT_USYS_CONFIG,
+ "changing system time", NULL, NULL, NULL, status ? 0 : 1);
+ close(hwaudit_fd);
+ }
+ exit(status);
+}
+#endif
+
+/****************************************************************************
+
+ History of this program:
+
+ 98.08.12 BJH Version 2.4
+
+ Don't use century byte from Hardware Clock. Add comments telling why.
+
+
+ 98.06.20 BJH Version 2.3.
+
+ Make --hctosys set the kernel timezone from TZ environment variable
+ and/or /usr/lib/zoneinfo. From Klaus Ripke (klaus@ripke.com).
+
+ 98.03.05 BJH. Version 2.2.
+
+ Add --getepoch and --setepoch.
+
+ Fix some word length things so it works on Alpha.
+
+ Make it work when /dev/rtc doesn't have the interrupt functions.
+ In this case, busywait for the top of a second instead of blocking and
+ waiting for the update complete interrupt.
+
+ Fix a bunch of bugs too numerous to mention.
+
+ 97.06.01: BJH. Version 2.1. Read and write the century byte (Byte
+ 50) of the ISA Hardware Clock when using direct ISA I/O. Problem
+ discovered by job (jei@iclnl.icl.nl).
+
+ Use the rtc clock access method in preference to the KDGHWCLK method.
+ Problem discovered by Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>.
+
+ November 1996: Version 2.0.1. Modifications by Nicolai Langfeldt
+ (janl@math.uio.no) to make it compile on linux 1.2 machines as well
+ as more recent versions of the kernel. Introduced the NO_CLOCK
+ access method and wrote feature test code to detect absense of rtc
+ headers.
+
+
+**************************************************************************
+ Maintenance notes
+
+ To compile this, you must use GNU compiler optimization (-O option)
+ in order to make the "extern inline" functions from asm/io.h (inb(),
+ etc.) compile. If you don't optimize, which means the compiler
+ will generate no inline functions, the references to these functions
+ in this program will be compiled as external references. Since you
+ probably won't be linking with any functions by these names, you will
+ have unresolved external references when you link.
+
+ The program is designed to run setuid superuser, since we need to be
+ able to do direct I/O. (More to the point: we need permission to
+ execute the iopl() system call). (However, if you use one of the
+ methods other than direct ISA I/O to access the clock, no setuid is
+ required).
+
+ Here's some info on how we must deal with the time that elapses while
+ this program runs: There are two major delays as we run:
+
+ 1) Waiting up to 1 second for a transition of the Hardware Clock so
+ we are synchronized to the Hardware Clock.
+
+ 2) Running the "date" program to interpret the value of our --date
+ option.
+
+ Reading the /etc/adjtime file is the next biggest source of delay and
+ uncertainty.
+
+ The user wants to know what time it was at the moment he invoked us,
+ not some arbitrary time later. And in setting the clock, he is
+ giving us the time at the moment we are invoked, so if we set the
+ clock some time later, we have to add some time to that.
+
+ So we check the system time as soon as we start up, then run "date"
+ and do file I/O if necessary, then wait to synchronize with a
+ Hardware Clock edge, then check the system time again to see how
+ much time we spent. We immediately read the clock then and (if
+ appropriate) report that time, and additionally, the delay we measured.
+
+ If we're setting the clock to a time given by the user, we wait some
+ more so that the total delay is an integral number of seconds, then
+ set the Hardware Clock to the time the user requested plus that
+ integral number of seconds. N.B. The Hardware Clock can only be set
+ in integral seconds.
+
+ If we're setting the clock to the system clock value, we wait for
+ the system clock to reach the top of a second, and then set the
+ Hardware Clock to the system clock's value.
+
+ Here's an interesting point about setting the Hardware Clock: On my
+ machine, when you set it, it sets to that precise time. But one can
+ imagine another clock whose update oscillator marches on a steady one
+ second period, so updating the clock between any two oscillator ticks
+ is the same as updating it right at the earlier tick. To avoid any
+ complications that might cause, we set the clock as soon as possible
+ after an oscillator tick.
+
+
+ About synchronizing to the Hardware Clock when reading the time: The
+ precision of the Hardware Clock counters themselves is one second.
+ You can't read the counters and find out that is 12:01:02.5. But if
+ you consider the location in time of the counter's ticks as part of
+ its value, then its precision is as infinite as time is continuous!
+ What I'm saying is this: To find out the _exact_ time in the
+ hardware clock, we wait until the next clock tick (the next time the
+ second counter changes) and measure how long we had to wait. We
+ then read the value of the clock counters and subtract the wait time
+ and we know precisely what time it was when we set out to query the
+ time.
+
+ hwclock uses this method, and considers the Hardware Clock to have
+ infinite precision.
+
+
+ Enhancements needed:
+
+ - When waiting for whole second boundary in set_hardware_clock_exact,
+ fail if we miss the goal by more than .1 second, as could happen if
+ we get pre-empted (by the kernel dispatcher).
+
+****************************************************************************/
+
diff --git a/hwclock/kd.c b/hwclock/kd.c
new file mode 100644
index 0000000..3da87ca
--- /dev/null
+++ b/hwclock/kd.c
@@ -0,0 +1,186 @@
+/* kd.c - KDGHWCLK stuff, possibly m68k only - deprecated */
+
+#include "clock.h"
+
+#ifndef __m68k__
+
+struct clock_ops *
+probe_for_kd_clock() {
+ return NULL;
+}
+
+#else /* __m68k__ */
+
+#include <unistd.h> /* for close() */
+#include <fcntl.h> /* for O_RDONLY */
+#include <sysexits.h>
+#include <sys/ioctl.h>
+
+#include "nls.h"
+#include "usleep.h"
+
+static int con_fd = -1; /* opened by probe_for_kd_clock() */
+ /* never closed */
+static char *con_fd_filename; /* usually "/dev/tty1" */
+
+/* Get defines for KDGHWCLK and KDSHWCLK (m68k) */
+#include <linux/kd.h>
+#ifndef KDGHWCLK
+#define KDGHWCLK 0x4B50 /* get hardware clock */
+#define KDSHWCLK 0x4B51 /* set hardware clock */
+struct hwclk_time {
+ unsigned sec; /* 0..59 */
+ unsigned min; /* 0..59 */
+ unsigned hour; /* 0..23 */
+ unsigned day; /* 1..31 */
+ unsigned mon; /* 0..11 */
+ unsigned year; /* 70... */
+ int wday; /* 0..6, 0 is Sunday, -1 means unknown/don't set */
+};
+#endif
+
+static int
+synchronize_to_clock_tick_kd(void) {
+/*----------------------------------------------------------------------------
+ Wait for the top of a clock tick by calling KDGHWCLK in a busy loop until
+ we see it.
+-----------------------------------------------------------------------------*/
+
+ /* The time when we were called (and started waiting) */
+ struct hwclk_time start_time, nowtime;
+ struct timeval begin, now;
+
+ if (debug)
+ printf(_("Waiting in loop for time from KDGHWCLK to change\n"));
+
+ if (ioctl(con_fd, KDGHWCLK, &start_time) == -1) {
+ outsyserr(_("KDGHWCLK ioctl to read time failed"));
+ return 3;
+ }
+
+ /* Wait for change. Should be within a second, but in case something
+ * weird happens, we have a time limit (1.5s) on this loop to reduce the
+ * impact of this failure.
+ */
+ gettimeofday(&begin, NULL);
+ do {
+ /* Added by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ * "The culprit is the fast loop with KDGHWCLK ioctls. It seems
+ * the kernel gets confused by those on Amigas with A2000 RTCs
+ * and simply hangs after some time. Inserting a sleep helps."
+ */
+ usleep(1);
+
+ if (ioctl(con_fd, KDGHWCLK, &nowtime) == -1) {
+ outsyserr(_("KDGHWCLK ioctl to read time failed in loop"));
+ return 3;
+ }
+ if (start_time.tm_sec != nowtime.tm_sec)
+ break;
+ gettimeofday(&now, NULL);
+ if (time_diff(now, begin) > 1.5) {
+ fprintf(stderr, _("Timed out waiting for time change.\n"));
+ return 2;
+ }
+ } while(1);
+
+ return 0;
+}
+
+
+static int
+read_hardware_clock_kd(struct tm *tm) {
+/*----------------------------------------------------------------------------
+ Read the hardware clock and return the current time via <tm>
+ argument. Use ioctls to /dev/tty1 on what we assume is an m68k
+ machine.
+
+ Note that we don't use /dev/console here. That might be a serial
+ console.
+-----------------------------------------------------------------------------*/
+ struct hwclk_time t;
+
+ if (ioctl(con_fd, KDGHWCLK, &t) == -1) {
+ outsyserr(_("ioctl() failed to read time from %s"), con_fd_filename);
+ hwclock_exit(EX_IOERR);
+ }
+
+ tm->tm_sec = t.sec;
+ tm->tm_min = t.min;
+ tm->tm_hour = t.hour;
+ tm->tm_mday = t.day;
+ tm->tm_mon = t.mon;
+ tm->tm_year = t.year;
+ tm->tm_wday = t.wday;
+ tm->tm_isdst = -1; /* Don't know if it's Daylight Savings Time */
+
+ return 0;
+}
+
+
+static int
+set_hardware_clock_kd(const struct tm *new_broken_time) {
+/*----------------------------------------------------------------------------
+ Set the Hardware Clock to the time <new_broken_time>. Use ioctls to
+ /dev/tty1 on what we assume is an m68k machine.
+
+ Note that we don't use /dev/console here. That might be a serial console.
+----------------------------------------------------------------------------*/
+ struct hwclk_time t;
+
+ t.sec = new_broken_time->tm_sec;
+ t.min = new_broken_time->tm_min;
+ t.hour = new_broken_time->tm_hour;
+ t.day = new_broken_time->tm_mday;
+ t.mon = new_broken_time->tm_mon;
+ t.year = new_broken_time->tm_year;
+ t.wday = new_broken_time->tm_wday;
+
+ if (ioctl(con_fd, KDSHWCLK, &t ) == -1) {
+ outsyserr(_("ioctl KDSHWCLK failed"));
+ hwclock_exit(1);
+ }
+ return 0;
+}
+
+static int
+get_permissions_kd(void) {
+ return 0;
+}
+
+static struct clock_ops kd = {
+ "KDGHWCLK interface to m68k clock",
+ get_permissions_kd,
+ read_hardware_clock_kd,
+ set_hardware_clock_kd,
+ synchronize_to_clock_tick_kd,
+};
+
+/* return &kd if KDGHWCLK works, NULL otherwise */
+struct clock_ops *
+probe_for_kd_clock() {
+ struct clock_ops *ret = NULL;
+ struct hwclk_time t;
+
+ if (con_fd < 0) { /* first time here */
+ con_fd_filename = "/dev/tty1";
+ con_fd = open(con_fd_filename, O_RDONLY);
+ }
+ if (con_fd < 0) {
+ /* perhaps they are using devfs? */
+ con_fd_filename = "/dev/vc/1";
+ con_fd = open(con_fd_filename, O_RDONLY);
+ }
+ if (con_fd < 0) {
+ /* probably KDGHWCLK exists on m68k only */
+ outsyserr(_("Can't open /dev/tty1 or /dev/vc/1"));
+ } else {
+ if (ioctl(con_fd, KDGHWCLK, &t) == -1) {
+ if (errno != EINVAL)
+ outsyserr(_("KDGHWCLK ioctl failed"));
+ } else
+ ret = &kd;
+ }
+ return ret;
+}
+#endif /* __m68k__ */
diff --git a/hwclock/rtc.c b/hwclock/rtc.c
new file mode 100644
index 0000000..2e05385
--- /dev/null
+++ b/hwclock/rtc.c
@@ -0,0 +1,477 @@
+/* rtc.c - Use /dev/rtc for clock access */
+#include <unistd.h> /* for close() */
+#include <fcntl.h> /* for O_RDONLY */
+#include <errno.h>
+#include <sysexits.h>
+#include <sys/ioctl.h>
+#include <sys/time.h> /* for struct timeval */
+
+#include "clock.h"
+#include "nls.h"
+
+/*
+ * Get defines for rtc stuff.
+ *
+ * Getting the rtc defines is nontrivial.
+ * The obvious way is by including <linux/mc146818rtc.h>
+ * but that again includes <asm/io.h> which again includes ...
+ * and on sparc and alpha this gives compilation errors for
+ * many kernel versions. So, we give the defines ourselves here.
+ * Moreover, some Sparc person decided to be incompatible, and
+ * used a struct rtc_time different from that used in mc146818rtc.h.
+ */
+
+/* On Sparcs, there is a <asm/rtc.h> that defines different ioctls
+ (that are required on my machine). However, this include file
+ does not exist on other architectures. */
+/* One might do:
+#ifdef __sparc__
+#include <asm/rtc.h>
+#endif
+ */
+/* The following is roughly equivalent */
+struct sparc_rtc_time
+{
+ int sec; /* Seconds (0-59) */
+ int min; /* Minutes (0-59) */
+ int hour; /* Hour (0-23) */
+ int dow; /* Day of the week (1-7) */
+ int dom; /* Day of the month (1-31) */
+ int month; /* Month of year (1-12) */
+ int year; /* Year (0-99) */
+};
+
+#define RTCGET _IOR('p', 20, struct sparc_rtc_time)
+#define RTCSET _IOW('p', 21, struct sparc_rtc_time)
+
+
+/* non-sparc stuff */
+#if 0
+#include <linux/version.h>
+/* Check if the /dev/rtc interface is available in this version of
+ the system headers. 131072 is linux 2.0.0. */
+#if LINUX_VERSION_CODE >= 131072
+#include <linux/mc146818rtc.h>
+#endif
+#endif
+
+/* struct rtc_time is present since 1.3.99 */
+/* Earlier (since 1.3.89), a struct tm was used. */
+struct linux_rtc_time {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+};
+
+/* RTC_RD_TIME etc have this definition since 1.99.9 (pre2.0-9) */
+#ifndef RTC_RD_TIME
+#define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time)
+#define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time)
+#define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */
+#define RTC_UIE_OFF _IO('p', 0x04) /* Update int. enable off */
+#endif
+/* RTC_EPOCH_READ and RTC_EPOCH_SET are present since 2.0.34 and 2.1.89 */
+#ifndef RTC_EPOCH_READ
+#define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */
+#define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */
+#endif
+
+/* /dev/rtc is conventionally chardev 10/135
+ * ia64 uses /dev/efirtc, chardev 10/136
+ * devfs (obsolete) used /dev/misc/... for miscdev
+ * new RTC framework + udev uses dynamic major and /dev/rtc0.../dev/rtcN
+ * ... so we need an overridable default
+ */
+
+/* default or user defined dev (by hwclock --rtc=<path>) */
+char *rtc_dev_name;
+
+static int rtc_dev_fd = -1;
+
+static void
+close_rtc(void) {
+ if (rtc_dev_fd != -1)
+ close(rtc_dev_fd);
+ rtc_dev_fd = -1;
+}
+
+static int
+open_rtc(void) {
+ char *fls[] = {
+#ifdef __ia64__
+ "/dev/efirtc",
+ "/dev/misc/efirtc",
+#endif
+ "/dev/rtc",
+ "/dev/rtc0",
+ "/dev/misc/rtc",
+ NULL
+ };
+ char **p;
+
+ if (rtc_dev_fd != -1)
+ return rtc_dev_fd;
+
+ /* --rtc option has been given */
+ if (rtc_dev_name)
+ rtc_dev_fd = open(rtc_dev_name, O_RDONLY);
+ else {
+ for (p=fls; *p; ++p) {
+ rtc_dev_fd = open(*p, O_RDONLY);
+
+ if (rtc_dev_fd < 0 && (errno == ENOENT || errno == ENODEV))
+ continue;
+ rtc_dev_name = *p;
+ break;
+ }
+ if (rtc_dev_fd < 0)
+ rtc_dev_name = *fls; /* default for error messages */
+ }
+
+ if (rtc_dev_fd != 1)
+ atexit(close_rtc);
+ return rtc_dev_fd;
+}
+
+static int
+open_rtc_or_exit(void) {
+ int rtc_fd = open_rtc();
+
+ if (rtc_fd < 0) {
+ outsyserr(_("open() of %s failed"), rtc_dev_name);
+ hwclock_exit(EX_OSFILE);
+ }
+ return rtc_fd;
+}
+
+static int
+do_rtc_read_ioctl(int rtc_fd, struct tm *tm) {
+ int rc = -1;
+ char *ioctlname;
+
+#ifdef __sparc__
+ /* some but not all sparcs use a different ioctl and struct */
+ struct sparc_rtc_time stm;
+
+ ioctlname = "RTCGET";
+ rc = ioctl(rtc_fd, RTCGET, &stm);
+ if (rc == 0) {
+ tm->tm_sec = stm.sec;
+ tm->tm_min = stm.min;
+ tm->tm_hour = stm.hour;
+ tm->tm_mday = stm.dom;
+ tm->tm_mon = stm.month - 1;
+ tm->tm_year = stm.year - 1900;
+ tm->tm_wday = stm.dow - 1;
+ tm->tm_yday = -1; /* day in the year */
+ }
+#endif
+ if (rc == -1) { /* no sparc, or RTCGET failed */
+ ioctlname = "RTC_RD_TIME";
+ rc = ioctl(rtc_fd, RTC_RD_TIME, tm);
+ }
+ if (rc == -1) {
+ perror(ioctlname);
+ fprintf(stderr, _("ioctl() to %s to read the time failed.\n"),
+ rtc_dev_name);
+ return -1;
+ }
+
+ tm->tm_isdst = -1; /* don't know whether it's dst */
+ return 0;
+}
+
+static int
+busywait_for_rtc_clock_tick(const int rtc_fd) {
+/*----------------------------------------------------------------------------
+ Wait for the top of a clock tick by reading /dev/rtc in a busy loop until
+ we see it.
+-----------------------------------------------------------------------------*/
+ struct tm start_time;
+ /* The time when we were called (and started waiting) */
+ struct tm nowtime;
+ int rc;
+ struct timeval begin, now;
+
+ if (debug)
+ printf(_("Waiting in loop for time from %s to change\n"),
+ rtc_dev_name);
+
+ rc = do_rtc_read_ioctl(rtc_fd, &start_time);
+ if (rc)
+ return 1;
+
+ /* Wait for change. Should be within a second, but in case something
+ * weird happens, we have a time limit (1.5s) on this loop to reduce the
+ * impact of this failure.
+ */
+ gettimeofday(&begin, NULL);
+ do {
+ rc = do_rtc_read_ioctl(rtc_fd, &nowtime);
+ if (rc || start_time.tm_sec != nowtime.tm_sec)
+ break;
+ gettimeofday(&now, NULL);
+ if (time_diff(now, begin) > 1.5) {
+ fprintf(stderr, _("Timed out waiting for time change.\n"));
+ return 2;
+ }
+ } while(1);
+
+ if (rc)
+ return 3;
+ return 0;
+}
+
+static int
+synchronize_to_clock_tick_rtc(void) {
+/*----------------------------------------------------------------------------
+ Same as synchronize_to_clock_tick(), but just for /dev/rtc.
+-----------------------------------------------------------------------------*/
+int rtc_fd; /* File descriptor of /dev/rtc */
+int ret;
+
+ rtc_fd = open_rtc();
+ if (rtc_fd == -1) {
+ outsyserr(_("open() of %s failed"), rtc_dev_name);
+ ret = 1;
+ } else {
+ int rc; /* Return code from ioctl */
+ /* Turn on update interrupts (one per second) */
+#if defined(__alpha__) || defined(__sparc__)
+ /* Not all alpha kernels reject RTC_UIE_ON, but probably they should. */
+ rc = -1;
+ errno = EINVAL;
+#else
+ rc = ioctl(rtc_fd, RTC_UIE_ON, 0);
+#endif
+ if (rc == -1 && (errno == ENOTTY || errno == EINVAL)) {
+ /* This rtc device doesn't have interrupt functions. This is typical
+ on an Alpha, where the Hardware Clock interrupts are used by the
+ kernel for the system clock, so aren't at the user's disposal.
+ */
+ if (debug)
+ printf(_("%s does not have interrupt functions. "),
+ rtc_dev_name);
+ ret = busywait_for_rtc_clock_tick(rtc_fd);
+ } else if (rc == 0) {
+#ifdef Wait_until_update_interrupt
+ unsigned long dummy;
+
+ /* this blocks until the next update interrupt */
+ rc = read(rtc_fd, &dummy, sizeof(dummy));
+ ret = 1;
+ if (rc == -1)
+ outsyserr(_("read() to %s to wait for clock tick failed"),
+ rtc_dev_name);
+ else
+ ret = 0;
+#else
+ /* Just reading rtc_fd fails on broken hardware: no update
+ interrupt comes and a bootscript with a hwclock call hangs */
+ fd_set rfds;
+ struct timeval tv;
+
+ /* Wait up to five seconds for the next update interrupt */
+ FD_ZERO(&rfds);
+ FD_SET(rtc_fd, &rfds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ rc = select(rtc_fd + 1, &rfds, NULL, NULL, &tv);
+ ret = 1;
+ if (rc == -1)
+ outsyserr(_("select() to %s to wait for clock tick failed"),
+ rtc_dev_name);
+ else if (rc == 0)
+ fprintf(stderr, _("select() to %s to wait for clock tick timed out\n"),
+ rtc_dev_name);
+ else
+ ret = 0;
+#endif
+
+ /* Turn off update interrupts */
+ rc = ioctl(rtc_fd, RTC_UIE_OFF, 0);
+ if (rc == -1)
+ outsyserr(_("ioctl() to %s to turn off update interrupts failed"),
+ rtc_dev_name);
+ } else {
+ outsyserr(_("ioctl() to %s to turn on update interrupts "
+ "failed unexpectedly"), rtc_dev_name);
+ ret = 1;
+ }
+ }
+ return ret;
+}
+
+
+static int
+read_hardware_clock_rtc(struct tm *tm) {
+ int rtc_fd, rc;
+
+ rtc_fd = open_rtc_or_exit();
+
+ /* Read the RTC time/date, return answer via tm */
+ rc = do_rtc_read_ioctl(rtc_fd, tm);
+
+ return rc;
+}
+
+
+static int
+set_hardware_clock_rtc(const struct tm *new_broken_time) {
+/*-------------------------------------------------------------------------
+ Set the Hardware Clock to the broken down time <new_broken_time>.
+ Use ioctls to "rtc" device /dev/rtc.
+ -------------------------------------------------------------------------*/
+ int rc = -1;
+ int rtc_fd;
+ char *ioctlname;
+
+ rtc_fd = open_rtc_or_exit();
+
+#ifdef __sparc__
+ {
+ struct sparc_rtc_time stm;
+
+ stm.sec = new_broken_time->tm_sec;
+ stm.min = new_broken_time->tm_min;
+ stm.hour = new_broken_time->tm_hour;
+ stm.dom = new_broken_time->tm_mday;
+ stm.month = new_broken_time->tm_mon + 1;
+ stm.year = new_broken_time->tm_year + 1900;
+ stm.dow = new_broken_time->tm_wday + 1;
+
+ ioctlname = "RTCSET";
+ rc = ioctl(rtc_fd, RTCSET, &stm);
+ }
+#endif
+ if (rc == -1) { /* no sparc, or RTCSET failed */
+ ioctlname = "RTC_SET_TIME";
+ rc = ioctl(rtc_fd, RTC_SET_TIME, new_broken_time);
+ }
+
+ if (rc == -1) {
+ perror(ioctlname);
+ fprintf(stderr, _("ioctl() to %s to set the time failed.\n"),
+ rtc_dev_name);
+ hwclock_exit(EX_IOERR);
+ }
+
+ if (debug)
+ printf(_("ioctl(%s) was successful.\n"), ioctlname);
+
+ return 0;
+}
+
+
+static int
+get_permissions_rtc(void) {
+ return 0;
+}
+
+static struct clock_ops rtc = {
+ "/dev interface to clock",
+ get_permissions_rtc,
+ read_hardware_clock_rtc,
+ set_hardware_clock_rtc,
+ synchronize_to_clock_tick_rtc,
+};
+
+/* return &rtc if /dev/rtc can be opened, NULL otherwise */
+struct clock_ops *
+probe_for_rtc_clock(){
+ int rtc_fd = open_rtc();
+ if (rtc_fd >= 0)
+ return &rtc;
+ if (debug)
+ outsyserr(_("Open of %s failed"), rtc_dev_name);
+ return NULL;
+}
+
+
+
+int
+get_epoch_rtc(unsigned long *epoch_p, int silent) {
+/*----------------------------------------------------------------------------
+ Get the Hardware Clock epoch setting from the kernel.
+----------------------------------------------------------------------------*/
+ int rtc_fd;
+
+ rtc_fd = open_rtc();
+ if (rtc_fd < 0) {
+ if (!silent) {
+ if (errno == ENOENT)
+ fprintf(stderr, _(
+ "To manipulate the epoch value in the kernel, we must "
+ "access the Linux 'rtc' device driver via the device special "
+ "file %s. This file does not exist on this system.\n"),
+ rtc_dev_name);
+ else
+ outsyserr(_("Unable to open %s"), rtc_dev_name);
+ }
+ return 1;
+ }
+
+ if (ioctl(rtc_fd, RTC_EPOCH_READ, epoch_p) == -1) {
+ if (!silent)
+ outsyserr(_("ioctl(RTC_EPOCH_READ) to %s failed"), rtc_dev_name);
+ return 1;
+ }
+
+ if (debug)
+ printf(_("we have read epoch %ld from %s "
+ "with RTC_EPOCH_READ ioctl.\n"), *epoch_p, rtc_dev_name);
+
+ return 0;
+}
+
+
+
+int
+set_epoch_rtc(unsigned long epoch) {
+/*----------------------------------------------------------------------------
+ Set the Hardware Clock epoch in the kernel.
+----------------------------------------------------------------------------*/
+ int rtc_fd;
+
+ if (epoch < 1900) {
+ /* kernel would not accept this epoch value */
+ /* Hmm - bad habit, deciding not to do what the user asks
+ just because one believes that the kernel might not like it. */
+ fprintf(stderr, _("The epoch value may not be less than 1900. "
+ "You requested %ld\n"), epoch);
+ return 1;
+ }
+
+ rtc_fd = open_rtc();
+ if (rtc_fd < 0) {
+ if (errno == ENOENT)
+ fprintf(stderr, _("To manipulate the epoch value in the kernel, we must "
+ "access the Linux 'rtc' device driver via the device special "
+ "file %s. This file does not exist on this system.\n"),
+ rtc_dev_name);
+ else
+ outsyserr(_("Unable to open %s"), rtc_dev_name);
+ return 1;
+ }
+
+ if (debug)
+ printf(_("setting epoch to %ld "
+ "with RTC_EPOCH_SET ioctl to %s.\n"), epoch, rtc_dev_name);
+
+ if (ioctl(rtc_fd, RTC_EPOCH_SET, epoch) == -1) {
+ if (errno == EINVAL)
+ fprintf(stderr, _("The kernel device driver for %s "
+ "does not have the RTC_EPOCH_SET ioctl.\n"), rtc_dev_name);
+ else
+ outsyserr(_("ioctl(RTC_EPOCH_SET) to %s failed"), rtc_dev_name);
+ return 1;
+ }
+
+ return 0;
+}