summaryrefslogtreecommitdiffstats
path: root/sys-utils
diff options
context:
space:
mode:
authorKarel Zak2012-05-31 11:15:07 +0200
committerKarel Zak2012-06-26 20:48:23 +0200
commitc7f753901f45bffdf39506e4f19bba4c37659ccc (patch)
tree88fc1be2f3f104144b4a45737a070ebf76801106 /sys-utils
parentbuild-sys: convert sys-utils/ to module (diff)
downloadkernel-qcow2-util-linux-c7f753901f45bffdf39506e4f19bba4c37659ccc.tar.gz
kernel-qcow2-util-linux-c7f753901f45bffdf39506e4f19bba4c37659ccc.tar.xz
kernel-qcow2-util-linux-c7f753901f45bffdf39506e4f19bba4c37659ccc.zip
build-sys: move hwclock to sys-utils/
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'sys-utils')
-rw-r--r--sys-utils/Makemodule.am20
-rw-r--r--sys-utils/hwclock-cmos.c677
-rw-r--r--sys-utils/hwclock-kd.c183
-rw-r--r--sys-utils/hwclock-rtc.c509
-rw-r--r--sys-utils/hwclock.8661
-rw-r--r--sys-utils/hwclock.c1895
-rw-r--r--sys-utils/hwclock.h47
7 files changed, 3992 insertions, 0 deletions
diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am
index b4adf634e..cc1a3f63a 100644
--- a/sys-utils/Makemodule.am
+++ b/sys-utils/Makemodule.am
@@ -314,3 +314,23 @@ bin_PROGRAMS += arch
dist_man_MANS += sys-utils/arch.1
arch_SOURCES = sys-utils/arch.c
endif
+
+if BUILD_HWCLOCK
+sbin_PROGRAMS += hwclock
+dist_man_MANS += sys-utils/hwclock.8
+hwclock_SOURCES = \
+ sys-utils/hwclock.c \
+ sys-utils/hwclock.h \
+ sys-utils/hwclock-cmos.c \
+ sys-utils/hwclock-kd.c \
+ lib/strutils.c
+
+if LINUX
+hwclock_SOURCES += sys-utils/hwclock-rtc.c
+endif
+
+hwclock_LDADD =
+if HAVE_AUDIT
+hwclock_LDADD += -laudit
+endif
+endif # BUILD_HWCLOCK
diff --git a/sys-utils/hwclock-cmos.c b/sys-utils/hwclock-cmos.c
new file mode 100644
index 000000000..366d934fb
--- /dev/null
+++ b/sys-utils/hwclock-cmos.c
@@ -0,0 +1,677 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "nls.h"
+
+#if defined(__i386__)
+# ifdef HAVE_SYS_IO_H
+# include <sys/io.h>
+# elif defined(HAVE_ASM_IO_H)
+# include <asm/io.h> /* for inb, outb */
+# else
+/*
+ * Disable cmos access; we can no longer use asm/io.h, since the kernel does
+ * not export that header.
+ */
+#undef __i386__
+void outb(int a __attribute__ ((__unused__)),
+ int b __attribute__ ((__unused__)))
+{
+}
+
+int inb(int c __attribute__ ((__unused__)))
+{
+ return 0;
+}
+#endif /* __i386__ */
+
+#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 __attribute__ ((__unused__)),
+ int b __attribute__ ((__unused__)))
+{
+}
+
+int inb(int c __attribute__ ((__unused__)))
+{
+ return 0;
+}
+#endif /* __alpha__ */
+
+#include "hwclock.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 between "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;
+
+#ifdef __linux__
+ /*
+ * 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;
+ }
+#endif
+
+ /*
+ * 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.
+ * TODO: 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 /* __alpha */
+
+#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;
+ }
+ }
+ errx(EXIT_FAILURE, _("atomic %s failed for 1000 iterations!"),
+ name);
+}
+#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 __attribute__ ((__unused__)),
+ 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: %m\n"),
+ clock_ctl_addr);
+ 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: %m\n"),
+ clock_data_addr);
+ 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 doesn't 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: %m\n"),
+ clock_ctl_addr);
+ 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: %m\n"),
+ clock_data_addr);
+ } 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;
+}
+
+/*
+ * 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.
+ */
+static int read_hardware_clock_cmos(struct tm *tm)
+{
+ 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;
+}
+
+#if defined(__i386__) || defined(__alpha__)
+# if defined(HAVE_IOPL)
+static int i386_iopl(const int level)
+{
+ extern int iopl(const int lvl);
+ return iopl(level);
+}
+# else
+static int i386_iopl(const int level __attribute__ ((__unused__)))
+{
+ extern int ioperm(unsigned long from, unsigned long num, int turn_on);
+ return ioperm(clock_ctl_addr, 2, 1);
+}
+# endif
+#else
+static int i386_iopl(const int level __attribute__ ((__unused__)))
+{
+ 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) {
+ warn(_("Cannot open /dev/port"));
+ rc = 1;
+ } else
+ rc = 0;
+ } else {
+ rc = i386_iopl(3);
+ if (rc == -2) {
+ warnx(_("I failed to get permission because I didn't try."));
+ } else if (rc != 0) {
+ rc = errno;
+ warn(_("unable to get I/O port access: "
+ "the iopl(3) call failed."));
+ if (rc == EPERM && geteuid())
+ warnx(_("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/sys-utils/hwclock-kd.c b/sys-utils/hwclock-kd.c
new file mode 100644
index 000000000..ec98f45ba
--- /dev/null
+++ b/sys-utils/hwclock-kd.c
@@ -0,0 +1,183 @@
+/*
+ * kd.c - KDGHWCLK stuff, possibly m68k only, likely to be deprecated
+ */
+
+
+#ifdef __m68k__
+
+# include <fcntl.h>
+# include <sysexits.h>
+# include <sys/ioctl.h>
+# include <unistd.h>
+
+# include "nls.h"
+# include "usleep.h"
+
+# include "hwclock.h"
+
+/* Get defines for KDGHWCLK and KDSHWCLK (m68k) */
+# include <linux/kd.h>
+
+/* Even on m68k, if KDGHWCLK (antique) is not defined, don't build this */
+
+#endif
+
+#if !defined(__m68k__) || !defined(KDGHWCLK)
+
+#include <stddef.h>
+struct clock_ops *probe_for_kd_clock(void)
+{
+ return NULL;
+}
+
+#else /* __m68k__ && KDGHWCLK */
+
+/* Opened by probe_for_kd_clock(), and never closed. */
+static int con_fd = -1;
+static char *con_fd_filename; /* usually "/dev/tty1" */
+
+/*
+ * Wait for the top of a clock tick by calling KDGHWCLK in a busy loop until
+ * we see it.
+ */
+static int synchronize_to_clock_tick_kd(void)
+{
+ /* 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) {
+ warn(_("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) {
+ warn(_("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) {
+ warnx(_("Timed out waiting for time change."));
+ return 2;
+ }
+ } while (1);
+
+ return 0;
+}
+
+/*
+ * 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.
+ */
+static int read_hardware_clock_kd(struct tm *tm)
+{
+ struct hwclk_time t;
+
+ if (ioctl(con_fd, KDGHWCLK, &t) == -1) {
+ warn(_("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;
+}
+
+/*
+ * 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.
+ */
+static int set_hardware_clock_kd(const struct tm *new_broken_time)
+{
+ 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) {
+ warn(_("ioctl KDSHWCLK failed"));
+ hwclock_exit(EX_IOERR);
+ }
+ 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 */
+ warn(_("Can't open /dev/tty1 or /dev/vc/1"));
+ } else {
+ if (ioctl(con_fd, KDGHWCLK, &t) == -1) {
+ if (errno != EINVAL)
+ warn(_("KDGHWCLK ioctl failed"));
+ } else
+ ret = &kd;
+ }
+ return ret;
+}
+#endif /* __m68k__ && KDGHWCLK */
diff --git a/sys-utils/hwclock-rtc.c b/sys-utils/hwclock-rtc.c
new file mode 100644
index 000000000..62adc49d7
--- /dev/null
+++ b/sys-utils/hwclock-rtc.c
@@ -0,0 +1,509 @@
+/*
+ * rtc.c - Use /dev/rtc for clock access
+ */
+#include <asm/ioctl.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "nls.h"
+
+#include "hwclock.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) {
+ warn(_("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) {
+ warn(_("ioctl(%s) to %s to read the time failed"),
+ ioctlname, rtc_dev_name);
+ return -1;
+ }
+
+ tm->tm_isdst = -1; /* don't know whether it's dst */
+ return 0;
+}
+
+/*
+ * Wait for the top of a clock tick by reading /dev/rtc in a busy loop until
+ * we see it.
+ */
+static int busywait_for_rtc_clock_tick(const int rtc_fd)
+{
+ 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) {
+ warnx(_("Timed out waiting for time change."));
+ return 2;
+ }
+ } while (1);
+
+ if (rc)
+ return 3;
+ return 0;
+}
+
+/*
+ * Same as synchronize_to_clock_tick(), but just for /dev/rtc.
+ */
+static int synchronize_to_clock_tick_rtc(void)
+{
+ int rtc_fd; /* File descriptor of /dev/rtc */
+ int ret;
+
+ rtc_fd = open_rtc();
+ if (rtc_fd == -1) {
+ warn(_("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)
+ warn(_
+ ("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)
+ warn(_
+ ("select() to %s to wait for clock tick failed"),
+ rtc_dev_name);
+ else if (rc == 0)
+ warn(_
+ ("select() to %s to wait for clock tick timed out"),
+ rtc_dev_name);
+ else
+ ret = 0;
+#endif
+
+ /* Turn off update interrupts */
+ rc = ioctl(rtc_fd, RTC_UIE_OFF, 0);
+ if (rc == -1)
+ warn(_
+ ("ioctl() to %s to turn off update interrupts failed"),
+ rtc_dev_name);
+ } else {
+ warn(_
+ ("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;
+}
+
+/*
+ * Set the Hardware Clock to the broken down time <new_broken_time>. Use
+ * ioctls to "rtc" device /dev/rtc.
+ */
+static int set_hardware_clock_rtc(const struct tm *new_broken_time)
+{
+ 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) {
+ warn(_("ioctl(%s) to %s to set the time failed."),
+ ioctlname, 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(void)
+{
+ int rtc_fd = open_rtc();
+ if (rtc_fd >= 0)
+ return &rtc;
+ if (debug)
+ warn(_("Open of %s failed"), rtc_dev_name);
+ return NULL;
+}
+
+/*
+ * Get the Hardware Clock epoch setting from the kernel.
+ */
+int get_epoch_rtc(unsigned long *epoch_p, int silent)
+{
+ int rtc_fd;
+
+ rtc_fd = open_rtc();
+ if (rtc_fd < 0) {
+ if (!silent) {
+ if (errno == ENOENT)
+ warnx(_
+ ("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."),
+ rtc_dev_name);
+ else
+ warn(_("Unable to open %s"), rtc_dev_name);
+ }
+ return 1;
+ }
+
+ if (ioctl(rtc_fd, RTC_EPOCH_READ, epoch_p) == -1) {
+ if (!silent)
+ warn(_("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;
+}
+
+/*
+ * Set the Hardware Clock epoch in the kernel.
+ */
+int set_epoch_rtc(unsigned long epoch)
+{
+ int rtc_fd;
+
+ if (epoch < 1900) {
+ /* kernel would not accept this epoch value
+ *
+ * Bad habit, deciding not to do what the user asks just
+ * because one believes that the kernel might not like it.
+ */
+ warnx(_("The epoch value may not be less than 1900. "
+ "You requested %ld"), epoch);
+ return 1;
+ }
+
+ rtc_fd = open_rtc();
+ if (rtc_fd < 0) {
+ if (errno == ENOENT)
+ warnx(_
+ ("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."),
+ rtc_dev_name);
+ else
+ warn(_("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)
+ warnx(_("The kernel device driver for %s "
+ "does not have the RTC_EPOCH_SET ioctl."),
+ rtc_dev_name);
+ else
+ warn(_("ioctl(RTC_EPOCH_SET) to %s failed"),
+ rtc_dev_name);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/sys-utils/hwclock.8 b/sys-utils/hwclock.8
new file mode 100644
index 000000000..07d9fc024
--- /dev/null
+++ b/sys-utils/hwclock.8
@@ -0,0 +1,661 @@
+.TH HWCLOCK 8 "August 2011" "util-linux" "System Administration"
+.SH NAME
+hwclock \- query or set the hardware clock (RTC)
+.SH SYNOPSIS
+.B hwclock
+.RI [ function ]
+.RI [ option ...]
+
+.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 from the System Time, or set the System Time from the
+Hardware Clock.
+.PP
+You can also run
+.B hwclock
+periodically to add or subtract time from the Hardware Clock to
+compensate for systematic drift (where the clock consistently loses or
+gains time at a certain rate when 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.
+Showing the Hardware Clock time is the default when no function is specified.
+
+.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 Clock epoch value
+must be 1952.
+
+This epoch value is used whenever
+.B 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
+.BI \-\-predict
+Predict what the RTC will read at time given by the
+.B \-\-date
+option based on the adjtime file. This is useful for example if you
+need to set an RTC wakeup time to distant future and want to account
+for the RTC drift.
+.TP
+.BR \-h , \ \-\-help
+Display a help text and exit.
+.TP
+.BR \-V , \ \-\-version
+Display the version of
+.B hwclock
+and exit.
+
+.SH OPTIONS
+.PP
+The first two options apply to just a few specific functions,
+the others apply to most functions.
+.TP
+.BI \-\-date= date_string
+You need this option if you specify the
+.B \-\-set
+or
+.B \-\-predict
+functions, otherwise it is ignored.
+It specifies the time to which to set the Hardware Clock, or the
+time for which to predict the Hardware Clock reading.
+The value of this option is an argument to the
+.BR date (1)
+program.
+For example:
+.sp
+.B " hwclock" --set --date="2011-08-14 16:45:05"
+.sp
+The argument must be 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, that is 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 \fB\-\-setepoch\fR 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
+.B " hwclock" --setepoch --epoch=1952
+
+.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
+.BR \-\-localtime ,
+the default is whichever was specified the last time
+.B hwclock
+was used to set the clock (i.e.
+.B 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 UTC 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
+This option 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 reading), it will
+use the explicit I/O instructions anyway.
+
+.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
+.B hwclock \-\-set
+or
+.B 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.)
+
+Option
+.B \-\-jensen
+means you are running on a Jensen model. And
+.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 Users access and setuid
+.PP
+Sometimes, you need to install
+.B 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.
+
+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).
+
+.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/
+.RI ( /usr/lib/zoneinfo
+on old systems)
+.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)
+
+.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 package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/hwclock.c b/sys-utils/hwclock.c
new file mode 100644
index 000000000..c0ac67826
--- /dev/null
+++ b/sys-utils/hwclock.c
@@ -0,0 +1,1895 @@
+/*
+ * 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
+ */
+/*
+ * 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 <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#define OPTUTILS_EXIT_CODE EX_USAGE
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "optutils.h"
+#include "pathnames.h"
+#include "strutils.h"
+#include "hwclock.h"
+
+#ifdef HAVE_LIBAUDIT
+#include <libaudit.h>
+static int hwaudit_fd = -1;
+static int hwaudit_on;
+#endif
+
+#define EXCL_ERROR "--{adjust,getepoch,hctosys,predict,set,setepoch,show,systohc,systz}"
+
+/* The struct that holds our hardware access routines */
+struct clock_ops *ur;
+
+#define FLOOR(arg) ((arg >= 0 ? (int) arg : ((int) arg) - 1));
+
+const char *adj_file_name = NULL;
+
+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.
+ */
+};
+
+/* Long only options. */
+enum {
+ OPT_SET = CHAR_MAX + 1,
+ OPT_GETEPOCH,
+ OPT_SETEPOCH,
+ OPT_NOADJFILE,
+ OPT_LOCALTIME,
+ OPT_BADYEAR,
+ OPT_DIRECTISA,
+ OPT_TEST,
+ OPT_DATE,
+ OPT_EPOCH,
+ OPT_ADJFILE,
+ OPT_SYSTZ,
+ OPT_PREDICT_HC
+};
+
+/*
+ * We are running in debug mode, wherein we put a lot of information about
+ * what we're doing to standard output.
+ */
+bool debug;
+
+/* Workaround for Award 4.50g BIOS bug: keep the year in a file. */
+bool badyear;
+
+/* User-specified epoch, used when rtc fails to return epoch. */
+unsigned long epoch_option = -1;
+
+/*
+ * 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(_PATH_LASTDATE, "w"))) {
+ fprintf(fp, "%02d.%02d.%04d\n", tm->tm_mday, tm->tm_mon + 1,
+ tm->tm_year + 1900);
+ if (close_stream(fp) != 0)
+ warn(_("cannot write %s"), _PATH_LASTDATE);
+ } else
+ warn(_("cannot write %s"), _PATH_LASTDATE);
+}
+
+static void read_date_from_file(struct tm *tm)
+{
+ int last_mday, last_mon, last_year;
+ FILE *fp;
+
+ if ((fp = fopen(_PATH_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);
+}
+
+/*
+ * The difference in seconds between two times in "timeval" format.
+ */
+double time_diff(struct timeval subtrahend, struct timeval subtractor)
+{
+ return (subtrahend.tv_sec - subtractor.tv_sec)
+ + (subtrahend.tv_usec - subtractor.tv_usec) / 1E6;
+}
+
+/*
+ * The time, in "timeval" format, which is <increment> seconds after the
+ * time <addend>. Of course, <increment> may be negative.
+ */
+static struct timeval time_inc(struct timeval addend, double increment)
+{
+ 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 UTC */
+ ret = (adjtime.local_utc != LOCAL);
+ if (debug)
+ printf(_("Assuming hardware clock is kept in %s time.\n"),
+ ret ? _("UTC") : _("local"));
+ return ret;
+}
+
+/*
+ * 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.
+ */
+static int read_adjtime(struct adjtime *adjtime_p)
+{
+ FILE *adjfile;
+ int rc; /* local return code */
+ struct stat statbuf; /* We don't even use the contents of this. */
+ 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;
+
+ 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) {
+ warn("cannot open file %s", adj_file_name);
+ return EX_OSFILE;
+ }
+
+
+ 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]) {
+ warnx(_("Warning: unrecognized third line in adjtime file\n"
+ "(Expected: `UTC' or `LOCAL' or nothing.)"));
+ }
+ }
+
+ 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;
+}
+
+/*
+ * 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.
+ */
+static int synchronize_to_clock_tick(void)
+{
+ 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;
+}
+
+/*
+ * 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.
+ */
+static void
+mktime_tz(struct tm tm, const bool universal,
+ bool * valid_p, time_t * systime_p)
+{
+ 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();
+}
+
+/*
+ * Read the hardware clock and return the current time via <tm> argument.
+ *
+ * Use the method indicated by <method> argument to access the hardware
+ * clock.
+ */
+static int
+read_hardware_clock(const bool universal, bool * valid_p, time_t * systime_p)
+{
+ 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;
+}
+
+/*
+ * Set the Hardware Clock to the time <newtime>, in local time zone or UTC,
+ * according to <universal>.
+ */
+static void
+set_hardware_clock(const time_t newtime,
+ const bool universal, const bool testing)
+{
+ 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);
+ }
+ ur->set_hardware_clock(&new_broken_time);
+ }
+}
+
+/*
+ * 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.
+ */
+static void
+set_hardware_clock_exact(const time_t sethwtime,
+ const struct timeval refsystime,
+ const bool universal, const bool testing)
+{
+ time_t newhwtime = sethwtime;
+ struct timeval beginsystime, nowsystime;
+ double tdiff;
+ int time_resync = 1;
+
+ /*
+ * 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 {
+ if (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);
+ time_resync = 0;
+ }
+
+ gettimeofday(&nowsystime, NULL);
+ tdiff = time_diff(nowsystime, beginsystime);
+ if (tdiff < 0) {
+ time_resync = 1; /* probably backward time reset */
+ continue;
+ }
+ if (tdiff > 0.1) {
+ time_resync = 1; /* probably forward time reset */
+ continue;
+ }
+ beginsystime = nowsystime;
+ tdiff = time_diff(nowsystime, refsystime);
+ } while (newhwtime == sethwtime + (int)(tdiff + 0.5));
+
+ set_hardware_clock(newhwtime, universal, testing);
+}
+
+/*
+ * 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".
+ */
+static void
+display_time(const bool hclock_valid, const time_t systime,
+ const double sync_duration)
+{
+ if (!hclock_valid)
+ warnx(_
+ ("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)."));
+ 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));
+ }
+}
+
+/*
+ * 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.
+ */
+static int interpret_date_string(const char *date_opt, time_t * const time_p)
+{
+ 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) {
+ warnx(_("No --date option specified."));
+ return 14;
+ }
+
+ /* prevent overflow - a security risk */
+ if (strlen(date_opt) > sizeof(date_command) - 50) {
+ warnx(_("--date argument too long"));
+ return 13;
+ }
+
+ /* Quotes in date_opt would ruin the date command we construct. */
+ if (strchr(date_opt, '"') != NULL) {
+ warnx(_
+ ("The value of the --date option is not a valid date.\n"
+ "In particular, it contains quotation marks."));
+ 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) {
+ warn(_("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) {
+ warnx(_("The date command issued by %s returned "
+ "unexpected results.\n"
+ "The command was:\n %s\n"
+ "The response was:\n %s"),
+ program_invocation_short_name, 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) {
+ warnx(_("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"),
+ program_invocation_short_name, 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;
+}
+
+/*
+ * 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.
+ */
+static int
+set_system_clock(const bool hclock_valid, const time_t newtime,
+ const bool testing)
+{
+ int retcode;
+
+ if (!hclock_valid) {
+ warnx(_
+ ("The Hardware Clock does not contain a valid time, so "
+ "we cannot set the System Time from it."));
+ 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) {
+ warnx(_
+ ("Must be superuser to set system clock."));
+ retcode = EX_NOPERM;
+ } else {
+ warn(_("settimeofday() failed"));
+ retcode = 1;
+ }
+ } else
+ retcode = 0;
+ }
+ }
+ return retcode;
+}
+
+/*
+ * 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.
+ */
+static int set_system_clock_timezone(const bool universal, const bool testing)
+{
+ 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) {
+ warnx(_
+ ("Must be superuser to set system clock."));
+ retcode = EX_NOPERM;
+ } else {
+ warn(_("settimeofday() failed"));
+ retcode = 1;
+ }
+ } else
+ retcode = 0;
+ }
+ return retcode;
+}
+
+/*
+ * 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.
+ */
+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)
+{
+ 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;
+}
+
+/*
+ * 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.
+ */
+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)
+{
+ 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);
+ }
+}
+
+/*
+ * 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.
+ */
+static void save_adjtime(const struct adjtime adjtime, const bool testing)
+{
+ 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) {
+ warn(_
+ ("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) {
+ warn(_
+ ("Could not update file with the clock adjustment "
+ "parameters (%s) in it"),
+ adj_file_name);
+ err = 1;
+ }
+ if (close_stream(adjfile) != 0) {
+ warn(_
+ ("Could not update file with the clock adjustment "
+ "parameters (%s) in it"),
+ adj_file_name);
+ err = 1;
+ }
+ }
+ if (err)
+ warnx(_
+ ("Drift adjustment parameters not updated."));
+ }
+ }
+}
+
+/*
+ * 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.
+ */
+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)
+{
+ if (!hclock_valid) {
+ warnx(_("The Hardware Clock does not contain a valid time, "
+ "so we cannot adjust it."));
+ 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();
+
+#ifdef __linux__
+ if (!ur)
+ ur = probe_for_rtc_clock();
+#endif
+
+ if (!ur)
+ ur = probe_for_kd_clock();
+
+ if (!ur && !user_requests_ISA)
+ ur = probe_for_cmos_clock();
+
+ if (debug) {
+ if (ur)
+ printf(_("Using %s.\n"), ur->interface_name);
+ else
+ printf(_("No usable clock interface found.\n"));
+ }
+}
+
+/*
+ * 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.
+ */
+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, const bool predict)
+{
+ /* Contents of the adjtime file, or what they should be. */
+ struct adjtime adjtime;
+ bool universal;
+ /* Set if user lacks necessary authorization to access the clock */
+ bool no_auth;
+ /* The time at which we read the Hardware Clock */
+ struct timeval read_time;
+ /*
+ * The Hardware Clock gives us a valid time, or at
+ * least something close enough to fool mktime().
+ */
+ bool hclock_valid = FALSE;
+ /*
+ * 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.
+ */
+ time_t hclocktime = 0;
+ /* local return code */
+ int rc = 0;
+
+ if (!systz && !predict) {
+ no_auth = ur->get_permissions();
+ if (no_auth)
+ return EX_NOPERM;
+ }
+
+ if (!noadjfile
+ && (adjust || set || systohc || (!utc && !local_opt) || predict)) {
+ 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;
+ }
+
+ 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;
+ }
+
+ if (show || adjust || hctosys || (!noadjfile && !systz && !predict)) {
+ /* data from HW-clock are required */
+ rc = synchronize_to_clock_tick();
+
+ /*
+ * 2 = synchronization timeout. We don't
+ * error out if the user is attempting to
+ * set the RTC - the RTC could be
+ * functioning but contain invalid time data
+ * so we still want to allow a user to set
+ * the RTC time.
+ */
+ if (rc && rc != 2 && !set && !systohc)
+ return EX_IOERR;
+ gettimeofday(&read_time, NULL);
+
+ /*
+ * If we can't synchronize to a clock tick,
+ * we likely can't read from the RTC so
+ * don't bother reading it again.
+ */
+ if (!rc) {
+ rc = read_hardware_clock(universal,
+ &hclock_valid, &hclocktime);
+ if (rc && !set && !systohc)
+ 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;
+ }
+ } else if (predict) {
+ int adjustment;
+ double retro;
+
+ calculate_adjustment(adjtime.drift_factor,
+ adjtime.last_adj_time,
+ adjtime.not_adjusted,
+ set_time, &adjustment, &retro);
+ if (debug) {
+ printf(_
+ ("At %ld seconds after 1969, RTC is predicted to read %ld seconds after 1969.\n"),
+ set_time, set_time + adjustment);
+ }
+ display_time(TRUE, set_time + adjustment, -retro);
+ }
+ if (!noadjfile)
+ save_adjtime(adjtime, testing);
+ return 0;
+}
+
+/*
+ * 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.
+ */
+#ifdef __linux__
+/*
+ * 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__
+static void
+manipulate_epoch(const bool getepoch __attribute__ ((__unused__)),
+ const bool setepoch __attribute__ ((__unused__)),
+ const unsigned long epoch_opt __attribute__ ((__unused__)),
+ const bool testing __attribute__ ((__unused__)))
+{
+ warnx(_("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."));
+}
+# else
+static void
+manipulate_epoch(const bool getepoch,
+ const bool setepoch,
+ const unsigned long epoch_opt,
+ const bool testing)
+{
+ if (getepoch) {
+ unsigned long epoch;
+
+ if (get_epoch_rtc(&epoch, 0))
+ warnx(_
+ ("Unable to get the epoch value from the kernel."));
+ else
+ printf(_("Kernel is assuming an epoch value of %lu\n"),
+ epoch);
+ } else if (setepoch) {
+ if (epoch_opt == -1)
+ warnx(_
+ ("To set the epoch value, you must use the 'epoch' "
+ "option to tell to what value to set it."));
+ 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 /* __alpha__ */
+#endif /* __linux__ */
+
+static void out_version(void)
+{
+ printf(_("%s from %s\n"), program_invocation_short_name, 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;
+
+ fputs(_("\nUsage:\n"), usageto);
+ fputs(_(" hwclock [function] [option...]\n"), usageto);
+
+ fputs(_("\nFunctions:\n"), usageto);
+ fputs(_(" -h, --help show this help text and exit\n"
+ " -r, --show read hardware clock and print result\n"
+ " --set set the RTC to the time given with --date\n"), usageto);
+ fputs(_(" -s, --hctosys set the system time from the hardware clock\n"
+ " -w, --systohc set the hardware clock from 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"), usageto);
+#ifdef __linux__
+ fputs(_(" --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"), usageto);
+#endif
+ fputs(_(" --predict predict RTC reading at time given with --date\n"
+ " -V, --version display version information and exit\n"), usageto);
+
+ fputs(_("\nOptions:\n"), usageto);
+ fputs(_(" -u, --utc the hardware clock is kept in UTC\n"
+ " --localtime the hardware clock is kept in local time\n"), usageto);
+#ifdef __linux__
+ fputs(_(" -f, --rtc <file> special /dev/... file to use instead of default\n"), usageto);
+#endif
+ fprintf(usageto, _(
+ " --directisa access the ISA bus directly instead of %s\n"
+ " --badyear ignore RTC's year because the BIOS is broken\n"
+ " --date <time> 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"), _PATH_RTC_DEV);
+ fprintf(usageto, _(
+ " --noadjfile do not access %s; this requires the use of\n"
+ " either --utc or --localtime\n"
+ " --adjfile <file> specifies the path to the adjust file;\n"
+ " the default is %s\n"), _PATH_ADJPATH, _PATH_ADJPATH);
+ fputs(_(" --test do not update anything, just show what would happen\n"
+ " -D, --debug debugging mode\n" "\n"), usageto);
+#ifdef __alpha__
+ fputs(_(" -J|--jensen, -A|--arc, -S|--srm, -F|--funky-toy\n"
+ " tell hwclock the type of Alpha you have (see hwclock(8))\n"
+ "\n"), usageto);
+#endif
+
+ if (fmt) {
+ va_start(ap, fmt);
+ vfprintf(usageto, fmt, ap);
+ va_end(ap);
+ }
+
+ fflush(usageto);
+ hwclock_exit(fmt ? EX_USAGE : EX_OK);
+}
+
+/*
+ * 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;
+
+ enum {
+ EXCL_NONE,
+
+ EXCL_ADJFILE,
+ EXCL_NO_AJDFILE,
+
+ EXCL_LOCALTIME,
+ EXCL_UTC,
+
+ EXCL_ADJUST,
+ EXCL_GETEPOCH,
+ EXCL_HCTOSYS,
+ EXCL_PREDICT,
+ EXCL_SET,
+ EXCL_SETEPOCH,
+ EXCL_SHOW,
+ EXCL_SYSTOHC,
+ EXCL_SYSTZ
+ };
+ int excl_adj = EXCL_NONE;
+ int excl_utc_local = EXCL_NONE;
+ int excl_action = EXCL_NONE;
+
+ /* 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,
+ predict;
+ bool utc, testing, local_opt, noadjfile, directisa;
+ char *date_opt;
+#ifdef __alpha__
+ bool ARCconsole, Jensen, SRM, funky_toy;
+#endif
+
+ 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, OPT_SET},
+#ifdef __linux__
+ {"getepoch", 0, 0, OPT_GETEPOCH},
+ {"setepoch", 0, 0, OPT_SETEPOCH},
+#endif
+ {"noadjfile", 0, 0, OPT_NOADJFILE},
+ {"localtime", 0, 0, OPT_LOCALTIME},
+ {"badyear", 0, 0, OPT_BADYEAR},
+ {"directisa", 0, 0, OPT_DIRECTISA},
+ {"test", 0, 0, OPT_TEST},
+ {"date", 1, 0, OPT_DATE},
+ {"epoch", 1, 0, OPT_EPOCH},
+#ifdef __linux__
+ {"rtc", 1, 0, 'f'},
+#endif
+ {"adjfile", 1, 0, OPT_ADJFILE},
+ {"systz", 0, 0, OPT_SYSTZ},
+ {"predict-hc", 0, 0, OPT_PREDICT_HC},
+ {NULL, 0, NULL, 0}
+ };
+
+ /* 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.
+ */
+ warnx(_("Unable to connect to audit system"));
+ 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);
+ atexit(close_stdout);
+
+ /* Set option defaults */
+ show = set = systohc = hctosys = systz = adjust = noadjfile = predict =
+ FALSE;
+ getepoch = setepoch = utc = local_opt = directisa = testing = debug = FALSE;
+#ifdef __alpha__
+ ARCconsole = Jensen = SRM = funky_toy = badyear = FALSE;
+#endif
+ date_opt = NULL;
+
+ while ((c = getopt_long(argc, argv, "?hvVDarsuwAJSFf:", longopts, NULL))
+ != -1) {
+ switch (c) {
+ case 'D':
+ debug = TRUE;
+ break;
+ case 'a':
+ adjust = TRUE;
+ exclusive_option(&excl_action, EXCL_ADJUST, EXCL_ERROR);
+ break;
+ case 'r':
+ show = TRUE;
+ exclusive_option(&excl_action, EXCL_SHOW, EXCL_ERROR);
+ break;
+ case 's':
+ hctosys = TRUE;
+ exclusive_option(&excl_action, EXCL_HCTOSYS, EXCL_ERROR);
+ break;
+ case 'u':
+ utc = TRUE;
+ exclusive_option(&excl_utc_local, EXCL_UTC, "--{utc,localtime}");
+ break;
+ case 'w':
+ systohc = TRUE;
+ exclusive_option(&excl_action, EXCL_SYSTOHC, EXCL_ERROR);
+ 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 OPT_SET:
+ set = TRUE;
+ exclusive_option(&excl_action, EXCL_SET, EXCL_ERROR);
+ break;
+#ifdef __linux__
+ case OPT_GETEPOCH:
+ getepoch = TRUE;
+ exclusive_option(&excl_action, EXCL_GETEPOCH, EXCL_ERROR);
+ break;
+ case OPT_SETEPOCH:
+ setepoch = TRUE;
+ exclusive_option(&excl_action, EXCL_SETEPOCH, EXCL_ERROR);
+ break;
+#endif
+ case OPT_NOADJFILE:
+ noadjfile = TRUE;
+ exclusive_option(&excl_adj, EXCL_NO_AJDFILE, "--{adjfile,noadjfile}");
+ break;
+ case OPT_LOCALTIME:
+ local_opt = TRUE; /* --localtime */
+ exclusive_option(&excl_utc_local, EXCL_LOCALTIME, "--{utc,localtime}");
+ break;
+ case OPT_BADYEAR:
+ badyear = TRUE;
+ break;
+ case OPT_DIRECTISA:
+ directisa = TRUE;
+ break;
+ case OPT_TEST:
+ testing = TRUE; /* --test */
+ break;
+ case OPT_DATE:
+ date_opt = optarg; /* --date */
+ break;
+ case OPT_EPOCH:
+ epoch_option = /* --epoch */
+ strtoul_or_err(optarg, _("invalid epoch argument"));
+ break;
+ case OPT_ADJFILE:
+ adj_file_name = optarg; /* --adjfile */
+ exclusive_option(&excl_adj, EXCL_ADJFILE, "--{adjfile,noadjfile}");
+ break;
+ case OPT_SYSTZ:
+ systz = TRUE; /* --systz */
+ exclusive_option(&excl_action, EXCL_SYSTZ, EXCL_ERROR);
+ break;
+ case OPT_PREDICT_HC:
+ predict = TRUE; /* --predict-hc */
+ exclusive_option(&excl_action, EXCL_PREDICT, EXCL_ERROR);
+ break;
+#ifdef __linux__
+ case 'f':
+ rtc_dev_name = optarg; /* --rtc */
+ break;
+#endif
+ 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"), program_invocation_short_name,
+ argc);
+ }
+
+ if (!adj_file_name)
+ adj_file_name = _PATH_ADJPATH;
+
+ if (noadjfile && !excl_utc_local) {
+ warnx(_("With --noadjfile, you must specify "
+ "either --utc or --localtime"));
+ hwclock_exit(EX_USAGE);
+ }
+#ifdef __alpha__
+ set_cmos_epoch(ARCconsole, SRM);
+ set_cmos_access(Jensen, funky_toy);
+#endif
+
+ if (set || predict) {
+ rc = interpret_date_string(date_opt, &set_time);
+ /* (time-consuming) */
+ if (rc != 0) {
+ warnx(_("No usable set-to time. "
+ "Cannot set clock."));
+ hwclock_exit(EX_USAGE);
+ }
+ }
+
+ if (!(show | set | systohc | hctosys | systz | adjust | getepoch
+ | setepoch | predict))
+ show = 1; /* default to show */
+
+ if (getuid() == 0)
+ permitted = TRUE;
+ else {
+ /* program is designed to run setuid (in some situations) */
+ if (set || systohc || adjust) {
+ warnx(_("Sorry, only the superuser can change "
+ "the Hardware Clock."));
+ permitted = FALSE;
+ } else if (systz || hctosys) {
+ warnx(_("Sorry, only the superuser can change "
+ "the System Clock."));
+ permitted = FALSE;
+ } else if (setepoch) {
+ warnx(_("Sorry, only the superuser can change the "
+ "Hardware Clock epoch in the kernel."));
+ permitted = FALSE;
+ } else
+ permitted = TRUE;
+ }
+
+ if (!permitted)
+ hwclock_exit(EX_NOPERM);
+
+#ifdef __linux__
+ if (getepoch || setepoch) {
+ manipulate_epoch(getepoch, setepoch, epoch_option, testing);
+ hwclock_exit(EX_OK);
+ }
+#endif
+
+ if (debug)
+ out_version();
+
+ if (!systz && !predict) {
+ determine_clock_access_method(directisa);
+ if (!ur) {
+ warnx(_("Cannot access the Hardware Clock via "
+ "any known method."));
+ if (!debug)
+ warnx(_("Use the --debug option to see the "
+ "details of our search for an access "
+ "method."));
+ hwclock_exit(EX_SOFTWARE);
+ }
+ }
+
+ rc = manipulate_clock(show, adjust, noadjfile, set, set_time,
+ hctosys, systohc, systz, startup_time, utc,
+ local_opt, testing, predict);
+ hwclock_exit(rc);
+ return rc; /* Not reached */
+}
+
+#ifdef HAVE_LIBAUDIT
+/*
+ * hwclock_exit calls either this function or plain exit depending
+ * HAVE_LIBAUDIT see also clock.h
+ */
+void __attribute__((__noreturn__)) 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 absence 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.
+ *
+ * TODO: 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/sys-utils/hwclock.h b/sys-utils/hwclock.h
new file mode 100644
index 000000000..175a6d1ae
--- /dev/null
+++ b/sys-utils/hwclock.h
@@ -0,0 +1,47 @@
+#ifndef HWCLOCK_CLOCK_H
+#define HWCLOCK_CLOCK_H
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "c.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;
+
+/* hwclock.c */
+extern char *progname;
+extern int debug;
+extern unsigned long epoch_option;
+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 */