diff options
author | Karel Zak | 2017-02-09 11:35:50 +0100 |
---|---|---|
committer | Karel Zak | 2017-02-09 11:35:50 +0100 |
commit | 1dc2aaadba3c26beda23d2dcf76c166fac9a71d7 (patch) | |
tree | c8230d7500c9882c921aedd3f45391dc2565a0a9 | |
parent | build-sys: improve detection of the "isnan" function in uClibc (diff) | |
parent | hwclock: remove --compare option (diff) | |
download | kernel-qcow2-util-linux-1dc2aaadba3c26beda23d2dcf76c166fac9a71d7.tar.gz kernel-qcow2-util-linux-1dc2aaadba3c26beda23d2dcf76c166fac9a71d7.tar.xz kernel-qcow2-util-linux-1dc2aaadba3c26beda23d2dcf76c166fac9a71d7.zip |
Merge branch 'hwclock-jwp-reviewed' of git://github.com/kerolasa/lelux-utiliteetit
* 'hwclock-jwp-reviewed' of git://github.com/kerolasa/lelux-utiliteetit: (25 commits)
hwclock: remove --compare option
hwclock: remove trailing dot from messages that include system error message
hwclock: make --date=argument less prone to injection
hwclock: fix rtc atexit registration
hwclock: clarify cmos inb and outb preprocessor directives
hwclock: try RTCGET and RTCSET only when normal rtc fails
hwclock: stream line synchronize_to_clock_tick_rtc()
hwclock: improve coding style
hwclock: remove division by zero [asan]
hwclock: add debugging to open_rtc()
hwclock: remove magic constants from interpret_date_string()
hwclock: use symbolic magic values passed in between functions
hwclock: initialize struct adjtime members
hwclock: alloate date_resp parsing buffer in interpret_date_string()
hwclock: simplify save_adjtime() execution flow
hwclock: remove dead code and other minor fixes
hwclock: move error messages to determine_clock_access_method()
hwclock: clarify set_cmos_epoch() code
hwclock: move command-line options to control structure
hwclock: remove unnecessary type casts
...
-rw-r--r-- | Documentation/deprecated.txt | 5 | ||||
-rw-r--r-- | bash-completion/hwclock | 1 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | include/pathnames.h | 4 | ||||
-rw-r--r-- | include/timeutils.h | 4 | ||||
-rw-r--r-- | lib/timeutils.c | 19 | ||||
-rw-r--r-- | login-utils/utmpdump.c | 1 | ||||
-rw-r--r-- | sys-utils/Makemodule.am | 2 | ||||
-rw-r--r-- | sys-utils/hwclock-cmos.c | 221 | ||||
-rw-r--r-- | sys-utils/hwclock-rtc.c | 221 | ||||
-rw-r--r-- | sys-utils/hwclock.8.in | 6 | ||||
-rw-r--r-- | sys-utils/hwclock.c | 856 | ||||
-rw-r--r-- | sys-utils/hwclock.h | 70 |
13 files changed, 639 insertions, 772 deletions
diff --git a/Documentation/deprecated.txt b/Documentation/deprecated.txt index fba47e0b5..df7f18fbf 100644 --- a/Documentation/deprecated.txt +++ b/Documentation/deprecated.txt @@ -8,6 +8,11 @@ since: v2.30 -------------------------- +what: hwclock --compare +why: Output printed by --compare option was nonesense. + +-------------------------- + what: tailf why: "tail -f" is better nowadays, tailf has unfixed bugs diff --git a/bash-completion/hwclock b/bash-completion/hwclock index de1ac20f3..89b17bd07 100644 --- a/bash-completion/hwclock +++ b/bash-completion/hwclock @@ -32,7 +32,6 @@ _hwclock_module() --systohc --systz --adjust - --compare --getepoch --setepoch --predict diff --git a/configure.ac b/configure.ac index 3bb3ba810..af7bd2210 100644 --- a/configure.ac +++ b/configure.ac @@ -416,6 +416,7 @@ AC_CHECK_FUNCS([ \ strnlen \ sysconf \ sysinfo \ + timegm \ usleep \ warn \ warnx \ diff --git a/include/pathnames.h b/include/pathnames.h index a5e8507e0..de6abe63e 100644 --- a/include/pathnames.h +++ b/include/pathnames.h @@ -195,5 +195,9 @@ /* ctrlaltdel paths */ #define _PATH_PROC_CTRL_ALT_DEL "/proc/sys/kernel/ctrl-alt-del" +/* hwclock-cmos paths */ +#define _PATH_DEV_PORT "/dev/port" +#define _PATH_PROC_CPUINFO "/proc/cpuinfo" + #endif /* PATHNAMES_H */ diff --git a/include/timeutils.h b/include/timeutils.h index 00d18200d..85fc228db 100644 --- a/include/timeutils.h +++ b/include/timeutils.h @@ -78,4 +78,8 @@ int time_is_thisyear(const time_t *t, struct timeval *now); int strtime_short(const time_t *t, struct timeval *now, int flags, char *buf, size_t bufsz); +#ifndef HAVE_TIMEGM +extern time_t timegm(struct tm *tm); +#endif + #endif /* UTIL_LINUX_TIME_UTIL_H */ diff --git a/lib/timeutils.c b/lib/timeutils.c index ea5c0aad8..d38970c10 100644 --- a/lib/timeutils.c +++ b/lib/timeutils.c @@ -20,6 +20,7 @@ #include <assert.h> #include <ctype.h> +#include <stdlib.h> #include <string.h> #include <time.h> #include <sys/time.h> @@ -460,6 +461,24 @@ int strtime_short(const time_t *t, struct timeval *now, int flags, char *buf, si return rc <= 0 ? -1 : 0; } +#ifndef HAVE_TIMEGM +time_t timegm(struct tm *tm) +{ + const char *zone = getenv("TZ"); + time_t ret; + + setenv("TZ", "", 1); + tzset(); + ret = mktime(tm); + if (zone) + setenv("TZ", zone, 1); + else + unsetenv("TZ"); + tzset(); + return ret; +} +#endif /* HAVE_TIMEGM */ + #ifdef TEST_PROGRAM_TIMEUTILS int main(int argc, char *argv[]) diff --git a/login-utils/utmpdump.c b/login-utils/utmpdump.c index 91eb4fb55..8e4839346 100644 --- a/login-utils/utmpdump.c +++ b/login-utils/utmpdump.c @@ -45,6 +45,7 @@ #include "timeutils.h" #include "xalloc.h" #include "closestream.h" +#include "timeutils.h" static time_t strtotime(const char *s_time) { diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am index 88be83e8c..be643c965 100644 --- a/sys-utils/Makemodule.am +++ b/sys-utils/Makemodule.am @@ -423,7 +423,7 @@ hwclock_SOURCES = \ if LINUX hwclock_SOURCES += sys-utils/hwclock-rtc.c endif -hwclock_LDADD = $(LDADD) libcommon.la $(MATH_LIBS) +hwclock_LDADD = $(LDADD) libcommon.la -lm if HAVE_AUDIT hwclock_LDADD += -laudit endif diff --git a/sys-utils/hwclock-cmos.c b/sys-utils/hwclock-cmos.c index 9103b7343..013fe094d 100644 --- a/sys-utils/hwclock-cmos.c +++ b/sys-utils/hwclock-cmos.c @@ -54,30 +54,28 @@ #include "c.h" #include "nls.h" +#include "pathnames.h" +/* for inb, outb */ #if defined(__i386__) || defined(__x86_64__) # ifdef HAVE_SYS_IO_H # include <sys/io.h> # elif defined(HAVE_ASM_IO_H) -# include <asm/io.h> /* for inb, outb */ +# include <asm/io.h> # else -/* - * Disable cmos access; we can no longer use asm/io.h, since the kernel does - * not export that header. - */ -#undef __i386__ -#undef __x86_64__ -void outb(int a __attribute__ ((__unused__)), - int b __attribute__ ((__unused__))) +# undef __i386__ +# undef __x86_64__ +# warning "disable cmos access - no sys/io.h or asm/io.h" +void outb(int a __attribute__((__unused__)), + int b __attribute__((__unused__))) { } -int inb(int c __attribute__ ((__unused__))) +int inb(int c __attribute__((__unused__))) { return 0; } -#endif /* __i386__ __x86_64__ */ - +# endif /* __i386__ __x86_64__ */ #elif defined(__alpha__) # ifdef HAVE_SYS_IO_H # include <sys/io.h> @@ -87,23 +85,26 @@ extern unsigned int inb(unsigned long port); extern void outb(unsigned char b, unsigned long port); extern int iopl(int level); # endif -#else -static void outb(int a __attribute__ ((__unused__)), - int b __attribute__ ((__unused__))) +#else /* __alpha__ */ +# warning "disable cmos access - not i386, x86_64, or alpha" +static void outb(int a __attribute__((__unused__)), + int b __attribute__((__unused__))) { } -static int inb(int c __attribute__ ((__unused__))) +static int inb(int c __attribute__((__unused__))) { return 0; } -#endif /* __alpha__ */ +#endif /* for inb, outb */ #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) +#define IOPL_NOT_IMPLEMENTED -2 + /* * The epoch. * @@ -152,12 +153,12 @@ static int is_in_cpuinfo(char *fmt, char *str) { FILE *cpuinfo; char field[256]; - char format[256]; + char format[sizeof(field)]; int found = 0; sprintf(format, "%s : %s", fmt, "%255s"); - cpuinfo = fopen("/proc/cpuinfo", "r"); + cpuinfo = fopen(_PATH_PROC_CPUINFO, "r"); if (cpuinfo) { do { if (fscanf(cpuinfo, format, field) == 1) { @@ -175,20 +176,20 @@ static int is_in_cpuinfo(char *fmt, char *str) * 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) +void set_cmos_epoch(const struct hwclock_control *ctl) { unsigned long epoch; /* Believe the user */ - if (epoch_option != -1) { - cmos_epoch = epoch_option; + if (ctl->epoch_option) { + cmos_epoch = ctl->epoch_option; return; } - if (ARCconsole) + if (ctl->ARCconsole) cmos_epoch = 1980; - if (ARCconsole || SRM) + if (ctl->ARCconsole || ctl->SRM) return; #ifdef __linux__ @@ -196,7 +197,7 @@ void set_cmos_epoch(int ARCconsole, int SRM) * If we can ask the kernel, we don't need guessing from * /proc/cpuinfo */ - if (get_epoch_rtc(&epoch, 1) == 0) { + if (get_epoch_rtc(ctl, &epoch, 1) == 0) { cmos_epoch = epoch; return; } @@ -218,39 +219,36 @@ void set_cmos_epoch(int ARCconsole, int SRM) * different "epoch" ideas. */ if (is_in_cpuinfo("system serial number", "MILO")) { - ARCconsole = 1; - if (debug) + if (ctl->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 (is_in_cpuinfo("system type", "Ruffian")) { + if (debug) + printf(_("Ruffian BCD clock\n")); + return; + } } - /* - * 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; + cmos_epoch = 1980; } -void set_cmos_access(int Jensen, int funky_toy) +void set_cmos_access(const struct hwclock_control *ctl) { /* * 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")) { + if (ctl->Jensen || is_in_cpuinfo("system type", "Jensen")) { use_dev_port = 1; clock_ctl_addr = 0x170; clock_data_addr = 0x171; - if (debug) + if (ctl->debug) printf(_("clockport adjusted to 0x%x\n"), clock_ctl_addr); } @@ -260,19 +258,19 @@ void set_cmos_access(int Jensen, int funky_toy) * TOY that must be accessed differently to work correctly. */ /* Nautilus stuff reported by Neoklis Kyriazis */ - if (funky_toy || + if (ctl->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) + if (ctl->debug) printf(_("funky TOY!\n")); } } #endif /* __alpha */ -#if __alpha__ +#ifdef __alpha__ /* * The Alpha doesn't allow user-level code to disable interrupts (for good * reasons). Instead, we ensure atomic operation by performing the operation @@ -282,13 +280,16 @@ void set_cmos_access(int Jensen, int funky_toy) * atomically, eventually. */ static unsigned long -atomic(const char *name, unsigned long (*op) (unsigned long), unsigned long arg) +atomic(const char *name, + unsigned long (*op) (const struct hwclock_control *ctl, unsigned long), + const struct hwclock_control *ctl, + unsigned long arg) { unsigned long ts1, ts2, n, v; for (n = 0; n < 1000; ++n) { asm volatile ("rpcc %0":"r=" (ts1)); - v = (*op) (arg); + v = (*op) (ctl, arg); asm volatile ("rpcc %0":"r=" (ts2)); if ((ts1 ^ ts2) >> 32 == 0) { @@ -307,24 +308,26 @@ atomic(const char *name, unsigned long (*op) (unsigned long), unsigned long arg) */ static unsigned long atomic(const char *name __attribute__ ((__unused__)), - unsigned long (*op) (unsigned long), + unsigned long (*op) (const struct hwclock_control *ctl, unsigned long), + const struct hwclock_control *ctl, unsigned long arg) { - return (*op) (arg); + return (*op) (ctl, arg); } #endif -static inline unsigned long cmos_read(unsigned long reg) +static inline unsigned long cmos_read(const struct hwclock_control *ctl, + 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) + if (write(dev_port_fd, &v, 1) == -1 && ctl->debug) warn(_("cmos_read(): write to control address %X failed"), clock_ctl_addr); lseek(dev_port_fd, clock_data_addr, 0); - if (read(dev_port_fd, &v, 1) == -1 && debug) + if (read(dev_port_fd, &v, 1) == -1 && ctl->debug) warn(_("cmos_read(): read from data address %X failed"), clock_data_addr); return v; @@ -356,17 +359,18 @@ static inline unsigned long cmos_read(unsigned long reg) } } -static inline unsigned long cmos_write(unsigned long reg, unsigned long val) +static inline unsigned long cmos_write(const struct hwclock_control *ctl, + 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) + if (write(dev_port_fd, &v, 1) == -1 && ctl->debug) warn(_("cmos_write(): write to control address %X failed"), clock_ctl_addr); v = (val & 0xff); lseek(dev_port_fd, clock_data_addr, 0); - if (write(dev_port_fd, &v, 1) == -1 && debug) + if (write(dev_port_fd, &v, 1) == -1 && ctl->debug) warn(_("cmos_write(): write to data address %X failed"), clock_data_addr); } else { @@ -376,7 +380,8 @@ static inline unsigned long cmos_write(unsigned long reg, unsigned long val) return 0; } -static unsigned long cmos_set_time(unsigned long arg) +static unsigned long cmos_set_time(const struct hwclock_control *ctl, + unsigned long arg) { unsigned char save_control, save_freq_select, pmbit = 0; struct tm tm = *(struct tm *)arg; @@ -399,10 +404,10 @@ static unsigned long cmos_set_time(unsigned long arg) * 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)); + save_control = cmos_read(ctl, 11); /* tell the clock it's being set */ + cmos_write(ctl, 11, (save_control | 0x80)); + save_freq_select = cmos_read(ctl, 10); /* stop and reset prescaler */ + cmos_write(ctl, 10, (save_freq_select | 0x70)); tm.tm_year += TM_EPOCH; century = tm.tm_year / 100; @@ -431,15 +436,15 @@ static unsigned long cmos_set_time(unsigned long arg) 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); + cmos_write(ctl, 0, tm.tm_sec); + cmos_write(ctl, 2, tm.tm_min); + cmos_write(ctl, 4, tm.tm_hour | pmbit); + cmos_write(ctl, 6, tm.tm_wday); + cmos_write(ctl, 7, tm.tm_mday); + cmos_write(ctl, 8, tm.tm_mon); + cmos_write(ctl, 9, tm.tm_year); if (century_byte) - cmos_write(century_byte, century); + cmos_write(ctl, century_byte, century); /* * The kernel sources, linux/arch/i386/kernel/time.c, have the @@ -452,33 +457,34 @@ static unsigned long cmos_set_time(unsigned long arg) * the Dallas Semiconductor data sheets, but who believes data * sheets anyway ... -- Markus Kuhn */ - cmos_write(11, save_control); - cmos_write(10, save_freq_select); + cmos_write(ctl, 11, save_control); + cmos_write(ctl, 10, save_freq_select); return 0; } -static int hclock_read(unsigned long reg) +static int hclock_read(const struct hwclock_control *ctl, unsigned long reg) { - return atomic("clock read", cmos_read, (reg)); + return atomic("clock read", cmos_read, ctl, reg); } -static void hclock_set_time(const struct tm *tm) +static void hclock_set_time(const struct hwclock_control *ctl, const struct tm *tm) { - atomic("set time", cmos_set_time, (unsigned long)(tm)); + atomic("set time", cmos_set_time, ctl, (unsigned long)(tm)); } -static inline int cmos_clock_busy(void) +static inline int cmos_clock_busy(const struct hwclock_control *ctl) { return #ifdef __alpha__ /* poll bit 4 (UF) of Control Register C */ - funkyTOY ? (hclock_read(12) & 0x10) : + funkyTOY ? (hclock_read(ctl, 12) & 0x10) : #endif /* poll bit 7 (UIP) of Control Register A */ - (hclock_read(10) & 0x80); + (hclock_read(ctl, 10) & 0x80); } -static int synchronize_to_clock_tick_cmos(void) +static int synchronize_to_clock_tick_cmos(const struct hwclock_control *ctl + __attribute__((__unused__))) { int i; @@ -487,12 +493,12 @@ static int synchronize_to_clock_tick_cmos(void) * weird happens, we have a limit on this loop to reduce the impact * of this failure. */ - for (i = 0; !cmos_clock_busy(); i++) + for (i = 0; !cmos_clock_busy(ctl); i++) if (i >= 10000000) return 1; /* Wait for fall. Should be within 2.228 ms. */ - for (i = 0; cmos_clock_busy(); i++) + for (i = 0; cmos_clock_busy(ctl); i++) if (i >= 1000000) return 1; return 0; @@ -514,12 +520,11 @@ static int synchronize_to_clock_tick_cmos(void) * In practice, the chance of this function returning the wrong time is * extremely remote. */ -static int read_hardware_clock_cmos(struct tm *tm) +static int read_hardware_clock_cmos(const struct hwclock_control *ctl + __attribute__((__unused__)), struct tm *tm) { bool got_time = FALSE; - unsigned char status, pmbit; - - status = pmbit = 0; /* just for gcc */ + unsigned char status = 0, pmbit = 0; while (!got_time) { /* @@ -536,25 +541,25 @@ static int read_hardware_clock_cmos(struct tm *tm) * 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()) { + if (!cmos_clock_busy(ctl)) { /* 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); + tm->tm_sec = hclock_read(ctl, 0); + tm->tm_min = hclock_read(ctl, 2); + tm->tm_hour = hclock_read(ctl, 4); + tm->tm_wday = hclock_read(ctl, 6); + tm->tm_mday = hclock_read(ctl, 7); + tm->tm_mon = hclock_read(ctl, 8); + tm->tm_year = hclock_read(ctl, 9); + status = hclock_read(ctl, 11); #if 0 if (century_byte) - century = hclock_read(century_byte); + century = hclock_read(ctl, century_byte); #endif /* * Unless the clock changed while we were reading, * consider this a good clock read . */ - if (tm->tm_sec == hclock_read(0)) + if (tm->tm_sec == hclock_read(ctl, 0)) got_time = TRUE; } /* @@ -602,10 +607,12 @@ static int read_hardware_clock_cmos(struct tm *tm) return 0; } -static int set_hardware_clock_cmos(const struct tm *new_broken_time) +static int set_hardware_clock_cmos(const struct hwclock_control *ctl + __attribute__((__unused__)), + const struct tm *new_broken_time) { - hclock_set_time(new_broken_time); + hclock_set_time(ctl, new_broken_time); return 0; } @@ -625,7 +632,7 @@ static int i386_iopl(const int level __attribute__ ((__unused__))) #else static int i386_iopl(const int level __attribute__ ((__unused__))) { - return -2; + return IOPL_NOT_IMPLEMENTED; } #endif @@ -634,19 +641,19 @@ 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 %s"), "/dev/port"); + if ((dev_port_fd = open(_PATH_DEV_PORT, O_RDWR)) < 0) { + warn(_("cannot open %s"), _PATH_DEV_PORT); rc = 1; } else rc = 0; } else { rc = i386_iopl(3); - if (rc == -2) { + if (rc == IOPL_NOT_IMPLEMENTED) { 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.")); + "the iopl(3) call failed")); if (rc == EPERM && geteuid()) warnx(_("Probably you need root privileges.\n")); } @@ -654,7 +661,7 @@ static int get_permissions_cmos(void) return rc ? 1 : 0; } -static struct clock_ops cmos = { +static struct clock_ops cmos_interface = { N_("Using direct I/O instructions to ISA clock."), get_permissions_cmos, read_hardware_clock_cmos, @@ -668,11 +675,11 @@ static struct clock_ops cmos = { */ struct clock_ops *probe_for_cmos_clock(void) { - int have_cmos = + static const int have_cmos = #if defined(__i386__) || defined(__alpha__) || defined(__x86_64__) TRUE; #else FALSE; #endif - return have_cmos ? &cmos : NULL; + return have_cmos ? &cmos_interface : NULL; } diff --git a/sys-utils/hwclock-rtc.c b/sys-utils/hwclock-rtc.c index 3e32399a3..411ec433a 100644 --- a/sys-utils/hwclock-rtc.c +++ b/sys-utils/hwclock-rtc.c @@ -38,6 +38,7 @@ # include <asm/rtc.h> #endif */ +#ifdef __sparc__ /* The following is roughly equivalent */ struct sparc_rtc_time { @@ -49,20 +50,8 @@ struct sparc_rtc_time 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 /* @@ -104,8 +93,7 @@ struct linux_rtc_time { */ /* default or user defined dev (by hwclock --rtc=<path>) */ -char *rtc_dev_name; - +static const char *rtc_dev_name; static int rtc_dev_fd = -1; static void close_rtc(void) @@ -115,52 +103,53 @@ static void close_rtc(void) rtc_dev_fd = -1; } -static int open_rtc(void) +static int open_rtc(const struct hwclock_control *ctl) { - char *fls[] = { + static const char *fls[] = { #ifdef __ia64__ "/dev/efirtc", "/dev/misc/efirtc", #endif "/dev/rtc", "/dev/rtc0", - "/dev/misc/rtc", - NULL + "/dev/misc/rtc" }; - char **p; + size_t i; if (rtc_dev_fd != -1) return rtc_dev_fd; /* --rtc option has been given */ - if (rtc_dev_name) + if (ctl->rtc_dev_name) { + rtc_dev_name = ctl->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); + } else { + for (i = 0; i < ARRAY_SIZE(fls); i++) { + if (ctl->debug) + printf(_("Trying to open: %s\n"), fls[i]); + rtc_dev_fd = open(fls[i], O_RDONLY); if (rtc_dev_fd < 0 && (errno == ENOENT || errno == ENODEV)) continue; - rtc_dev_name = *p; + rtc_dev_name = fls[i]; break; } if (rtc_dev_fd < 0) rtc_dev_name = *fls; /* default for error messages */ } - - if (rtc_dev_fd != 1) + if (rtc_dev_fd != -1) atexit(close_rtc); return rtc_dev_fd; } -static int open_rtc_or_exit(void) +static int open_rtc_or_exit(const struct hwclock_control *ctl) { - int rtc_fd = open_rtc(); + int rtc_fd = open_rtc(ctl); if (rtc_fd < 0) { - warn(_("cannot open %s"), rtc_dev_name); - hwclock_exit(EX_OSFILE); + warn(_("cannot open rtc device")); + hwclock_exit(ctl, EX_OSFILE); } return rtc_fd; } @@ -169,28 +158,31 @@ 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; +#endif + + ioctlname = "RTC_RD_TIME"; + rc = ioctl(rtc_fd, RTC_RD_TIME, tm); - 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 */ +#ifdef __sparc__ + if (rc == -1) { /* sparc sbus */ + 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); @@ -205,7 +197,8 @@ static int do_rtc_read_ioctl(int rtc_fd, struct tm *tm) * 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) +static int busywait_for_rtc_clock_tick(const struct hwclock_control *ctl, + const int rtc_fd) { struct tm start_time; /* The time when we were called (and started waiting) */ @@ -213,13 +206,13 @@ static int busywait_for_rtc_clock_tick(const int rtc_fd) int rc; struct timeval begin, now; - if (debug) + if (ctl->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; + return RTC_BUSYWAIT_FAILED; /* * Wait for change. Should be within a second, but in case @@ -234,27 +227,27 @@ static int busywait_for_rtc_clock_tick(const int rtc_fd) gettimeofday(&now, NULL); if (time_diff(now, begin) > 1.5) { warnx(_("Timed out waiting for time change.")); - return 2; + return RTC_BUSYWAIT_TIMEOUT; } } while (1); if (rc) - return 3; - return 0; + return RTC_BUSYWAIT_FAILED; + return RTC_BUSYWAIT_OK; } /* * Same as synchronize_to_clock_tick(), but just for /dev/rtc. */ -static int synchronize_to_clock_tick_rtc(void) +static int synchronize_to_clock_tick_rtc(const struct hwclock_control *ctl) { int rtc_fd; /* File descriptor of /dev/rtc */ - int ret; + int ret = 1; - rtc_fd = open_rtc(); + rtc_fd = open_rtc(ctl); if (rtc_fd == -1) { - warn(_("cannot open %s"), rtc_dev_name); - ret = 1; + warn(_("cannot open rtc device")); + return ret; } else { int rc; /* Return code from ioctl */ /* Turn on update interrupts (one per second) */ @@ -268,18 +261,7 @@ static int synchronize_to_clock_tick_rtc(void) #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) { + if (rc != -1) { /* * Just reading rtc_fd fails on broken hardware: no * update interrupt comes and a bootscript with a @@ -297,35 +279,44 @@ static int synchronize_to_clock_tick_rtc(void) tv.tv_sec = 10; 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); + if (0 < rc) + ret = 0; else if (rc == 0) { - if (debug) + if (ctl->debug) printf(_("select() to %s to wait for clock tick timed out"), rtc_dev_name); } else - ret = 0; + warn(_("select() to %s to wait for clock tick failed"), + rtc_dev_name); /* 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 { + } else if (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 (ctl->debug) + printf(_("%s does not have interrupt functions. "), + rtc_dev_name); + ret = busywait_for_rtc_clock_tick(ctl, rtc_fd); + } 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) +static int read_hardware_clock_rtc(const struct hwclock_control *ctl, + struct tm *tm) { int rtc_fd, rc; - rtc_fd = open_rtc_or_exit(); + rtc_fd = open_rtc_or_exit(ctl); /* Read the RTC time/date, return answer via tm */ rc = do_rtc_read_ioctl(rtc_fd, tm); @@ -337,16 +328,20 @@ static int read_hardware_clock_rtc(struct tm *tm) * 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) +static int set_hardware_clock_rtc(const struct hwclock_control *ctl, + const struct tm *new_broken_time) { int rc = -1; int rtc_fd; char *ioctlname; - rtc_fd = open_rtc_or_exit(); + rtc_fd = open_rtc_or_exit(ctl); + + ioctlname = "RTC_SET_TIME"; + rc = ioctl(rtc_fd, RTC_SET_TIME, new_broken_time); #ifdef __sparc__ - { + if (rc == -1) { /* sparc sbus */ struct sparc_rtc_time stm; stm.sec = new_broken_time->tm_sec; @@ -361,18 +356,14 @@ static int set_hardware_clock_rtc(const struct tm *new_broken_time) 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."), + warn(_("ioctl(%s) to %s to set the time failed"), ioctlname, rtc_dev_name); - hwclock_exit(EX_IOERR); + hwclock_exit(ctl, EX_IOERR); } - if (debug) + if (ctl->debug) printf(_("ioctl(%s) was successful.\n"), ioctlname); return 0; @@ -383,8 +374,8 @@ static int get_permissions_rtc(void) return 0; } -static struct clock_ops rtc = { - N_("Using the /dev interface to the clock."), +static struct clock_ops rtc_interface = { + N_("Using the rtc interface to the clock."), get_permissions_rtc, read_hardware_clock_rtc, set_hardware_clock_rtc, @@ -392,34 +383,34 @@ static struct clock_ops rtc = { }; /* return &rtc if /dev/rtc can be opened, NULL otherwise */ -struct clock_ops *probe_for_rtc_clock(void) +struct clock_ops *probe_for_rtc_clock(const struct hwclock_control *ctl) { - int rtc_fd = open_rtc(); - if (rtc_fd >= 0) - return &rtc; - if (debug) - warn(_("cannot open %s"), rtc_dev_name); - return NULL; + const int rtc_fd = open_rtc(ctl); + + if (rtc_fd < 0) + return NULL; + return &rtc_interface; } +#ifdef __alpha__ /* * Get the Hardware Clock epoch setting from the kernel. */ -int get_epoch_rtc(unsigned long *epoch_p, int silent) +int get_epoch_rtc(const struct hwclock_control *ctl, unsigned long *epoch_p, + int silent) { int rtc_fd; - rtc_fd = open_rtc(); + rtc_fd = open_rtc(ctl); 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); + "file. This file does not exist on this system.")); else - warn(_("cannot open %s"), rtc_dev_name); + warn(_("cannot open rtc device")); } return 1; } @@ -431,7 +422,7 @@ int get_epoch_rtc(unsigned long *epoch_p, int silent) return 1; } - if (debug) + if (ctl->debug) printf(_("we have read epoch %lu from %s " "with RTC_EPOCH_READ ioctl.\n"), *epoch_p, rtc_dev_name); @@ -442,40 +433,39 @@ int get_epoch_rtc(unsigned long *epoch_p, int silent) /* * Set the Hardware Clock epoch in the kernel. */ -int set_epoch_rtc(unsigned long epoch) +int set_epoch_rtc(const struct hwclock_control *ctl) { int rtc_fd; - if (epoch < 1900) { + if (ctl->epoch_option < 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); + "You requested %ld"), ctl->epoch_option); return 1; } - rtc_fd = open_rtc(); + rtc_fd = open_rtc(ctl); 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); + "file. This file does not exist on this system.")); else - warn(_("cannot open %s"), rtc_dev_name); + warn(_("cannot open rtc device")); return 1; } - if (debug) + if (ctl->debug) printf(_("setting epoch to %lu " - "with RTC_EPOCH_SET ioctl to %s.\n"), epoch, + "with RTC_EPOCH_SET ioctl to %s.\n"), ctl->epoch_option, rtc_dev_name); - if (ioctl(rtc_fd, RTC_EPOCH_SET, epoch) == -1) { + if (ioctl(rtc_fd, RTC_EPOCH_SET, ctl->epoch_option) == -1) { if (errno == EINVAL) warnx(_("The kernel device driver for %s " "does not have the RTC_EPOCH_SET ioctl."), @@ -488,3 +478,4 @@ int set_epoch_rtc(unsigned long epoch) return 0; } +#endif /* __alpha__ */ diff --git a/sys-utils/hwclock.8.in b/sys-utils/hwclock.8.in index 9534c19bc..2dec7823e 100644 --- a/sys-utils/hwclock.8.in +++ b/sys-utils/hwclock.8.in @@ -41,12 +41,6 @@ discussion below, under .BR "The Adjust Function" . . .TP -.BR \-c , \ \-\-compare -Periodically compare the Hardware Clock to the System Time and output -the difference every 10 seconds. This will also print the frequency -offset and tick. -. -.TP .B \-\-getepoch .TQ .B \-\-setepoch diff --git a/sys-utils/hwclock.c b/sys-utils/hwclock.c index 10d9e37e9..02a028950 100644 --- a/sys-utils/hwclock.c +++ b/sys-utils/hwclock.c @@ -82,25 +82,21 @@ #include "hwclock.h" #include "timeutils.h" #include "env.h" +#include "xalloc.h" #ifdef HAVE_LIBAUDIT #include <libaudit.h> static int hwaudit_fd = -1; -static int hwaudit_on; #endif /* The struct that holds our hardware access routines */ struct clock_ops *ur; -#define FLOOR(arg) ((arg >= 0 ? (int) arg : ((int) arg) - 1)); - /* Maximal clock adjustment in seconds per day. (adjtime() glibc call has 2145 seconds limit on i386, so it is good enough for us as well, 43219 is a maximal safe value preventing exact_adjustment overflow.) */ #define MAX_DRIFT 2145.0 -const char *adj_file_name = NULL; - struct adjtime { /* * This is information we keep in the adjtime file that tells us how @@ -122,7 +118,7 @@ struct adjtime { * authority (as opposed to just doing a drift adjustment) */ /* line 3 */ - enum a_local_utc { LOCAL, UTC, UNKNOWN } local_utc; + enum a_local_utc { UTC = 0, LOCAL, UNKNOWN } local_utc; /* * To which time zone, local or UTC, we most recently set the * hardware clock. @@ -130,18 +126,6 @@ struct adjtime { }; /* - * We are running in debug mode, wherein we put a lot of information about - * what we're doing to standard output. - */ -int 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 = ULONG_MAX; - -/* * 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. @@ -230,19 +214,19 @@ static struct timeval time_inc(struct timeval addend, double increment) } static bool -hw_clock_is_utc(const bool utc, const bool local_opt, +hw_clock_is_utc(const struct hwclock_control *ctl, const struct adjtime adjtime) { bool ret; - if (utc) + if (ctl->utc) ret = TRUE; /* --utc explicitly given on command line */ - else if (local_opt) + else if (ctl->local_opt) ret = FALSE; /* --localtime explicitly given */ else /* get info from adjtime file - default is UTC */ ret = (adjtime.local_utc != LOCAL); - if (debug) + if (ctl->debug) printf(_("Assuming hardware clock is kept in %s time.\n"), ret ? _("UTC") : _("local")); return ret; @@ -257,33 +241,23 @@ hw_clock_is_utc(const bool utc, const bool local_opt, * * return value 0 if all OK, !=0 otherwise. */ -static int read_adjtime(struct adjtime *adjtime_p) +static int read_adjtime(const struct hwclock_control *ctl, + struct adjtime *adjtime_p) { FILE *adjfile; char line1[81]; /* String: first line of adjtime file */ char line2[81]; /* String: second line of adjtime file */ char line3[81]; /* String: third line of adjtime file */ - long timeval; - - if (access(adj_file_name, R_OK) != 0) { - /* 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 = UTC; - adjtime_p->dirty = FALSE; /* don't create a zero adjfile */ + if (access(ctl->adj_file_name, R_OK) != 0) return 0; - } - adjfile = fopen(adj_file_name, "r"); /* open file for reading */ + adjfile = fopen(ctl->adj_file_name, "r"); /* open file for reading */ if (adjfile == NULL) { - warn(_("cannot open %s"), adj_file_name); + warn(_("cannot open %s"), ctl->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)) @@ -293,20 +267,12 @@ static int read_adjtime(struct adjtime *adjtime_p) 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; + &adjtime_p->last_adj_time, + &adjtime_p->not_adjusted); - sscanf(line2, "%ld", &timeval); - adjtime_p->last_calib_time = timeval; + sscanf(line2, "%ld", &adjtime_p->last_calib_time); if (!strcmp(line3, "UTC\n")) { adjtime_p->local_utc = UTC; @@ -320,9 +286,7 @@ static int read_adjtime(struct adjtime *adjtime_p) } } - adjtime_p->dirty = FALSE; - - if (debug) { + if (ctl->debug) { printf(_ ("Last drift adjustment done at %ld seconds after 1969\n"), (long)adjtime_p->last_adj_time); @@ -350,16 +314,16 @@ static int read_adjtime(struct adjtime *adjtime_p) * * Return 0 if it worked, nonzero if it didn't. */ -static int synchronize_to_clock_tick(void) +static int synchronize_to_clock_tick(const struct hwclock_control *ctl) { int rc; - if (debug) + if (ctl->debug) printf(_("Waiting for clock tick...\n")); - rc = ur->synchronize_to_clock_tick(); + rc = ur->synchronize_to_clock_tick(ctl); - if (debug) { + if (ctl->debug) { if (rc) printf(_("...synchronization failed\n")); else @@ -386,30 +350,14 @@ static int synchronize_to_clock_tick(void) * return *valid_p == true. */ static void -mktime_tz(struct tm tm, const bool universal, - bool * valid_p, time_t * systime_p) +mktime_tz(const struct hwclock_control *ctl, struct tm tm, + 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 */ - xsetenv("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) { + if (ctl->universal) + *systime_p = timegm(&tm); + else + *systime_p = mktime(&tm); + if (*systime_p == -1) { /* * This apparently (not specified in mktime() documentation) * means the 'tm' structure does not contain valid values @@ -417,28 +365,20 @@ mktime_tz(struct tm tm, const bool universal, * mktime() returns -1). */ *valid_p = FALSE; - *systime_p = 0; - if (debug) + if (ctl->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) + if (ctl->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) - xsetenv("TZ", zone, TRUE); - else - unsetenv("TZ"); - tzset(); } /* @@ -448,24 +388,25 @@ mktime_tz(struct tm tm, const bool universal, * clock. */ static int -read_hardware_clock(const bool universal, bool * valid_p, time_t * systime_p) +read_hardware_clock(const struct hwclock_control *ctl, + bool * valid_p, time_t *systime_p) { struct tm tm; int err; - err = ur->read_hardware_clock(&tm); + err = ur->read_hardware_clock(ctl, &tm); if (err) return err; - if (badyear) + if (ctl->badyear) read_date_from_file(&tm); - if (debug) + if (ctl->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); + mktime_tz(ctl, tm, valid_p, systime_p); return 0; } @@ -475,8 +416,7 @@ read_hardware_clock(const bool universal, bool * valid_p, time_t * systime_p) * according to <universal>. */ static void -set_hardware_clock(const time_t newtime, - const bool universal, const bool testing) +set_hardware_clock(const struct hwclock_control *ctl, const time_t newtime) { struct tm new_broken_time; /* @@ -484,21 +424,21 @@ set_hardware_clock(const time_t newtime, * in the time zone of caller's choice */ - if (universal) + if (ctl->universal) new_broken_time = *gmtime(&newtime); else new_broken_time = *localtime(&newtime); - if (debug) + if (ctl->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) + if (ctl->testing) printf(_("Clock not changed - testing only.\n")); else { - if (badyear) { + if (ctl->badyear) { /* * Write the real year to a file, then write a fake * year between 1995 and 1998 to the RTC. This way, @@ -509,7 +449,7 @@ set_hardware_clock(const time_t newtime, new_broken_time.tm_year = 95 + ((new_broken_time.tm_year + 1) & 3); } - ur->set_hardware_clock(&new_broken_time); + ur->set_hardware_clock(ctl, &new_broken_time); } } @@ -532,9 +472,9 @@ set_hardware_clock(const time_t newtime, * Idea for future enhancement. */ static void -set_hardware_clock_exact(const time_t sethwtime, - const struct timeval refsystime, - const bool universal, const bool testing) +set_hardware_clock_exact(const struct hwclock_control *ctl, + const time_t sethwtime, + const struct timeval refsystime) { /* * The Hardware Clock can only be set to any integer time plus one @@ -598,7 +538,7 @@ set_hardware_clock_exact(const time_t sethwtime, double ticksize; /* FOR TESTING ONLY: inject random delays of up to 1000ms */ - if (debug >= 10) { + if (ctl->debug >= 10) { int usec = random() % 1000000; printf(_("sleeping ~%d usec\n"), usec); xusleep(usec); @@ -610,20 +550,20 @@ set_hardware_clock_exact(const time_t sethwtime, prevsystime = nowsystime; if (ticksize < 0) { - if (debug) + if (ctl->debug) printf(_("time jumped backward %.6f seconds " "to %ld.%06ld - retargeting\n"), - ticksize, (long)nowsystime.tv_sec, - (long)nowsystime.tv_usec); + ticksize, nowsystime.tv_sec, + nowsystime.tv_usec); /* The retarget is handled at the end of the loop. */ } else if (deltavstarget < 0) { /* deltavstarget < 0 if current time < target time */ - if (debug >= 2) + if (ctl->debug >= 2) printf(_("%ld.%06ld < %ld.%06ld (%.6f)\n"), - (long)nowsystime.tv_sec, - (long)nowsystime.tv_usec, - (long)targetsystime.tv_sec, - (long)targetsystime.tv_usec, + nowsystime.tv_sec, + nowsystime.tv_usec, + targetsystime.tv_sec, + targetsystime.tv_usec, deltavstarget); continue; /* not there yet - keep spinning */ } else if (deltavstarget <= target_time_tolerance_secs) { @@ -634,13 +574,13 @@ set_hardware_clock_exact(const time_t sethwtime, * We missed our window. Increase the tolerance and * aim for the next opportunity. */ - if (debug) + if (ctl->debug) printf(_("missed it - %ld.%06ld is too far " "past %ld.%06ld (%.6f > %.6f)\n"), - (long)nowsystime.tv_sec, - (long)nowsystime.tv_usec, - (long)targetsystime.tv_sec, - (long)targetsystime.tv_usec, + nowsystime.tv_sec, + nowsystime.tv_usec, + targetsystime.tv_sec, + targetsystime.tv_usec, deltavstarget, target_time_tolerance_secs); target_time_tolerance_secs += tolerance_incr_secs; @@ -662,17 +602,17 @@ set_hardware_clock_exact(const time_t sethwtime, + (int)(time_diff(nowsystime, refsystime) - RTC_SET_DELAY_SECS /* don't count this */ + 0.5 /* for rounding */); - if (debug) + if (ctl->debug) printf(_("%ld.%06ld is close enough to %ld.%06ld (%.6f < %.6f)\n" "Set RTC to %ld (%ld + %d; refsystime = %ld.%06ld)\n"), - (long)nowsystime.tv_sec, (long)nowsystime.tv_usec, - (long)targetsystime.tv_sec, (long)targetsystime.tv_usec, + nowsystime.tv_sec, nowsystime.tv_usec, + targetsystime.tv_sec, targetsystime.tv_usec, deltavstarget, target_time_tolerance_secs, - (long)newhwtime, (long)sethwtime, + newhwtime, sethwtime, (int)(newhwtime - sethwtime), - (long)refsystime.tv_sec, (long)refsystime.tv_usec); + refsystime.tv_sec, refsystime.tv_usec); - set_hardware_clock(newhwtime, universal, testing); + set_hardware_clock(ctl, newhwtime); } /* @@ -715,49 +655,49 @@ display_time(const bool hclock_valid, struct timeval hwctime) * 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) +static int interpret_date_string(const struct hwclock_control *ctl, + time_t *const time_p) { - FILE *date_child_fp; - char date_resp[100]; + FILE *date_child_fp = NULL; + char *date_command = NULL; + char *date_resp = NULL; + size_t len = 0; const char magic[] = "seconds-into-epoch="; - char date_command[100]; - int retcode; /* our eventual return code */ - int rc; /* local return code */ + int retcode = 1; + long seconds_since_epoch; - if (date_opt == NULL) { + if (!ctl->date_opt) { 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; + return retcode; } /* Quotes in date_opt would ruin the date command we construct. */ - if (strchr(date_opt, '"') != NULL) { + if (strchr(ctl->date_opt, '"') != NULL || + strchr(ctl->date_opt, '`') != NULL || + strchr(ctl->date_opt, '$') != NULL) { warnx(_ ("The value of the --date option is not a valid date.\n" - "In particular, it contains quotation marks.")); - return 12; + "In particular, it contains illegal character(s).")); + return retcode; } - sprintf(date_command, "date --date=\"%s\" +seconds-into-epoch=%%s", - date_opt); - if (debug) + xasprintf(&date_command, "date --date=\"%s\" +%s%%s", + ctl->date_opt, magic); + if (ctl->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; + goto out; } - if (!fgets(date_resp, sizeof(date_resp), date_child_fp)) - date_resp[0] = '\0'; /* in case fgets fails */ - if (debug) + if (getline(&date_resp, &len, date_child_fp) < 0) { + warn(_("getline() failed")); + goto out; + } + if (ctl->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 " @@ -765,30 +705,29 @@ static int interpret_date_string(const char *date_opt, time_t * const time_p) "The command was:\n %s\n" "The response was:\n %s"), program_invocation_short_name, date_command, date_resp); - retcode = 8; + goto out; + } + + if (sscanf(date_resp + sizeof(magic) - 1, "%ld", &seconds_since_epoch) < 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); } 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); - } + retcode = 0; + *time_p = seconds_since_epoch; + if (ctl->debug) + printf(_("date string %s equates to " + "%ld seconds since 1969.\n"), + ctl->date_opt, *time_p); } - pclose(date_child_fp); + out: + free(date_command); + free(date_resp); + if (date_child_fp) + pclose(date_child_fp); return retcode; } @@ -814,8 +753,8 @@ static int interpret_date_string(const char *date_opt, time_t * const time_p) * have. */ static int -set_system_clock(const bool hclock_valid, const struct timeval newtime, - const bool testing, const bool universal) +set_system_clock(const struct hwclock_control *ctl, const bool hclock_valid, + const struct timeval newtime) { int retcode; @@ -839,13 +778,13 @@ set_system_clock(const bool hclock_valid, const struct timeval newtime, minuteswest -= 60; #endif - if (debug) { + if (ctl->debug) { printf(_("Calling settimeofday:\n")); printf(_("\ttv.tv_sec = %ld, tv.tv_usec = %ld\n"), - (long)newtime.tv_sec, (long)newtime.tv_usec); + newtime.tv_sec, newtime.tv_usec); printf(_("\ttz.tz_minuteswest = %d\n"), minuteswest); } - if (testing) { + if (ctl->testing) { printf(_ ("Not setting system clock because running in test mode.\n")); retcode = 0; @@ -856,7 +795,7 @@ set_system_clock(const bool hclock_valid, const struct timeval newtime, * mode does not clobber the Hardware Clock with UTC. This * is only available on first call of settimeofday after boot. */ - if (!universal) + if (!ctl->universal) rc = settimeofday(tv_null, &tz); if (!rc) rc = settimeofday(&newtime, &tz); @@ -887,7 +826,7 @@ set_system_clock(const bool hclock_valid, const struct timeval newtime, * 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) +static int set_system_clock_timezone(const struct hwclock_control *ctl) { int retcode; struct timeval tv; @@ -895,14 +834,14 @@ static int set_system_clock_timezone(const bool universal, const bool testing) int minuteswest; gettimeofday(&tv, NULL); - if (debug) { + if (ctl->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, + printf(_("Current system time: %ld = %s\n"), tv.tv_sec, ctime_now); } @@ -915,12 +854,12 @@ static int set_system_clock_timezone(const bool universal, const bool testing) minuteswest -= 60; #endif - if (debug) { + if (ctl->debug) { struct tm broken_time; char ctime_now[200]; gettimeofday(&tv, NULL); - if (!universal) + if (!ctl->universal) tv.tv_sec += minuteswest * 60; broken_time = *gmtime(&tv.tv_sec); @@ -930,10 +869,10 @@ static int set_system_clock_timezone(const bool universal, const bool testing) 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); + tv.tv_sec, tv.tv_usec); printf(_("\ttz.tz_minuteswest = %d\n"), minuteswest); } - if (testing) { + if (ctl->testing) { printf(_ ("Not setting system clock because running in test mode.\n")); retcode = 0; @@ -948,7 +887,7 @@ static int set_system_clock_timezone(const bool universal, const bool testing) * compensate. If the systemtime is in fact in UTC, then this is wrong * so we first do a dummy call to make sure the time is not shifted. */ - if (universal) + if (ctl->universal) rc = settimeofday(tv_null, &tz_utc); /* Now we set the real timezone. Due to the above dummy call, this will @@ -985,33 +924,33 @@ static int set_system_clock_timezone(const bool universal, const bool testing) * so don't adjust the drift factor. */ static void -adjust_drift_factor(struct adjtime *adjtime_p, +adjust_drift_factor(const struct hwclock_control *ctl, + struct adjtime *adjtime_p, const struct timeval nowtime, const bool hclock_valid, - const struct timeval hclocktime, - const bool update) + const struct timeval hclocktime) { - if (!update) { - if (debug) + if (!ctl->update) { + if (ctl->debug) printf(_("Not adjusting drift factor because the " "--update-drift option was not used.\n")); } else if (!hclock_valid) { - if (debug) + if (ctl->debug) printf(_("Not adjusting drift factor because the " "Hardware Clock previously contained " "garbage.\n")); } else if (adjtime_p->last_calib_time == 0) { - if (debug) + if (ctl->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.tv_sec - adjtime_p->last_calib_time) < 4 * 60 * 60) { - if (debug) + if (ctl->debug) printf(_("Not adjusting drift factor because it has " "been less than four hours since the last " "calibration.\n")); - } else if (adjtime_p->last_calib_time != 0) { + } else { /* * At adjustment time we drift correct the hardware clock * according to the contents of the adjtime file and refresh @@ -1045,14 +984,14 @@ adjust_drift_factor(struct adjtime *adjtime_p, drift_factor = adjtime_p->drift_factor + factor_adjust; if (fabs(drift_factor) > MAX_DRIFT) { - if (debug) + if (ctl->debug) printf(_("Clock drift factor was calculated as " "%f seconds/day.\n" "It is far too much. Resetting to zero.\n"), drift_factor); drift_factor = 0; } else { - if (debug) + if (ctl->debug) printf(_("Clock drifted %f seconds in the past " "%f seconds\nin spite of a drift factor of " "%f seconds/day.\n" @@ -1082,7 +1021,8 @@ adjust_drift_factor(struct adjtime *adjtime_p, * */ static void -calculate_adjustment(const double factor, +calculate_adjustment(const struct hwclock_control *ctl, + const double factor, const time_t last_time, const double not_adjusted, const time_t systime, struct timeval *tdrift_p) @@ -1092,16 +1032,16 @@ calculate_adjustment(const double factor, exact_adjustment = ((double)(systime - last_time)) * factor / (24 * 60 * 60) + not_adjusted; - tdrift_p->tv_sec = FLOOR(exact_adjustment); + tdrift_p->tv_sec = (time_t) floor(exact_adjustment); tdrift_p->tv_usec = (exact_adjustment - (double)tdrift_p->tv_sec) * 1E6; - if (debug) { - printf(P_("Time since last adjustment is %d second\n", - "Time since last adjustment is %d seconds\n", - (int)(systime - last_time)), - (int)(systime - last_time)); + if (ctl->debug) { + printf(P_("Time since last adjustment is %ld second\n", + "Time since last adjustment is %ld seconds\n", + (systime - last_time)), + (systime - last_time)); printf(_("Calculated Hardware Clock drift is %ld.%06ld seconds\n"), - (long)tdrift_p->tv_sec, (long)tdrift_p->tv_usec); + tdrift_p->tv_sec, tdrift_p->tv_usec); } } @@ -1111,59 +1051,45 @@ calculate_adjustment(const double factor, * 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) +static void save_adjtime(const struct hwclock_control *ctl, + const struct adjtime *adjtime) { - char newfile[412]; /* Stuff to write to disk file */ + char *content; /* Stuff to write to disk file */ + FILE *fp; + int err = 0; - 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 == LOCAL) ? "LOCAL" : "UTC"); - - 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.")); - } + if (!adjtime->dirty) + return; + + xasprintf(&content, "%f %ld %f\n%ld\n%s\n", + adjtime->drift_factor, + adjtime->last_adj_time, + adjtime->not_adjusted, + adjtime->last_calib_time, + (adjtime->local_utc == LOCAL) ? "LOCAL" : "UTC"); + + if (ctl->testing) { + printf(_ + ("Not updating adjtime file because of testing mode.\n")); + printf(_("Would have written the following to %s:\n%s"), + ctl->adj_file_name, content); + free(content); + return; + } + + fp = fopen(ctl->adj_file_name, "w"); + if (fp == NULL) { + warn(_("Could not open file with the clock adjustment parameters " + "in it (%s) for writing"), ctl->adj_file_name); + err = 1; + } else if (fputs(content, fp) < 0 || close_stream(fp) != 0) { + warn(_("Could not update file with the clock adjustment " + "parameters (%s) in it"), ctl->adj_file_name); + err = 1; } + free(content); + if (err) + warnx(_("Drift adjustment parameters not updated.")); } /* @@ -1191,10 +1117,9 @@ static void save_adjtime(const struct adjtime adjtime, const bool testing) * */ static void -do_adjustment(struct adjtime *adjtime_p, +do_adjustment(const struct hwclock_control *ctl, struct adjtime *adjtime_p, const bool hclock_valid, const struct timeval hclocktime, - const struct timeval read_time, - const bool universal, const bool testing) + const struct timeval read_time) { if (!hclock_valid) { warnx(_("The Hardware Clock does not contain a valid time, " @@ -1204,41 +1129,47 @@ do_adjustment(struct adjtime *adjtime_p, adjtime_p->not_adjusted = 0; adjtime_p->dirty = TRUE; } else if (adjtime_p->last_adj_time == 0) { - if (debug) + if (ctl->debug) printf(_("Not setting clock because last adjustment time is zero, " "so history is bad.\n")); } else if (fabs(adjtime_p->drift_factor) > MAX_DRIFT) { - if (debug) + if (ctl->debug) printf(_("Not setting clock because drift factor %f is far too high.\n"), adjtime_p->drift_factor); } else { - set_hardware_clock_exact(hclocktime.tv_sec, + set_hardware_clock_exact(ctl, hclocktime.tv_sec, time_inc(read_time, - -(hclocktime.tv_usec / 1E6)), - universal, testing); + -(hclocktime.tv_usec / 1E6))); adjtime_p->last_adj_time = hclocktime.tv_sec; adjtime_p->not_adjusted = 0; adjtime_p->dirty = TRUE; } } -static void determine_clock_access_method(const bool user_requests_ISA) +static void determine_clock_access_method(const struct hwclock_control *ctl) { ur = NULL; - if (user_requests_ISA) + if (ctl->directisa) ur = probe_for_cmos_clock(); - #ifdef __linux__ if (!ur) - ur = probe_for_rtc_clock(); + ur = probe_for_rtc_clock(ctl); #endif + if (ur) { + if (ctl->debug) + puts(ur->interface_name); - if (debug) { - if (ur) - puts(_(ur->interface_name)); - else + } else { + if (ctl->debug) printf(_("No usable clock interface found.\n")); + warnx(_("Cannot access the Hardware Clock via " + "any known method.")); + if (!ctl->debug) + warnx(_("Use the --debug option to see the " + "details of our search for an access " + "method.")); + hwclock_exit(ctl, EX_SOFTWARE); } } @@ -1250,18 +1181,9 @@ static void determine_clock_access_method(const bool user_requests_ISA) * 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 update, - const bool testing, const bool predict, const bool get) +manipulate_clock(const struct hwclock_control *ctl, const time_t set_time, + const struct timeval startup_time, struct adjtime *adjtime) { - /* Contents of the adjtime file, or what they should be. */ - struct adjtime adjtime = { 0 }; - 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; /* @@ -1279,42 +1201,28 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile, /* local return code */ int rc = 0; - if (!systz && !predict) { - no_auth = ur->get_permissions(); - if (no_auth) + if (!ctl->systz && !ctl->predict) + if (ur->get_permissions()) return EX_NOPERM; - } - if (!noadjfile && !(systz && (utc || local_opt))) { - rc = read_adjtime(&adjtime); - if (rc) - return rc; - } else { - /* A little trick to avoid writing the file if we don't have to */ - adjtime.dirty = FALSE; + if ((ctl->set || ctl->systohc || ctl->adjust) && + (adjtime->local_utc == UTC) != ctl->universal) { + adjtime->local_utc = ctl->universal ? UTC : LOCAL; + adjtime->dirty = TRUE; } - 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 || get || adjust || hctosys || (!noadjfile && !systz && !predict)) { + if (ctl->show || ctl->get || ctl->adjust || ctl->hctosys + || (!ctl->noadjfile && !ctl->systz && !ctl->predict)) { /* data from HW-clock are required */ - rc = synchronize_to_clock_tick(); + rc = synchronize_to_clock_tick(ctl); /* - * 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. + * We don't error out if the user is attempting to set the + * RTC and synchronization timeout happens - 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) + if (rc == RTC_BUSYWAIT_FAILED && !ctl->set && !ctl->systohc) return EX_IOERR; gettimeofday(&read_time, NULL); @@ -1324,9 +1232,9 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile, * don't bother reading it again. */ if (!rc) { - rc = read_hardware_clock(universal, - &hclock_valid, &hclocktime.tv_sec); - if (rc && !set && !systohc) + rc = read_hardware_clock(ctl, &hclock_valid, + &hclocktime.tv_sec); + if (rc && !ctl->set && !ctl->systohc) return EX_IOERR; } } @@ -1338,33 +1246,32 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile, * --predict. For --predict negate the drift correction, because we * want to 'predict' a future Hardware Clock time that includes drift. */ - hclocktime = predict ? t2tv(set_time) : hclocktime; - calculate_adjustment(adjtime.drift_factor, - adjtime.last_adj_time, - adjtime.not_adjusted, + hclocktime = ctl->predict ? t2tv(set_time) : hclocktime; + calculate_adjustment(ctl, adjtime->drift_factor, + adjtime->last_adj_time, + adjtime->not_adjusted, hclocktime.tv_sec, &tdrift); - if (!show && !predict) + if (!ctl->show && !ctl->predict) hclocktime = time_inc(tdrift, hclocktime.tv_sec); - if (show || get) { + if (ctl->show || ctl->get) { display_time(hclock_valid, time_inc(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, + } else if (ctl->set) { + set_hardware_clock_exact(ctl, set_time, startup_time); + if (!ctl->noadjfile) + adjust_drift_factor(ctl, adjtime, time_inc(t2tv(set_time), time_diff (read_time, startup_time)), - hclock_valid, hclocktime, update); - } else if (adjust) { + hclock_valid, hclocktime); + } else if (ctl->adjust) { if (tdrift.tv_sec > 0 || tdrift.tv_sec < -1) - do_adjustment(&adjtime, hclock_valid, - hclocktime, read_time, universal, testing); + do_adjustment(ctl, adjtime, hclock_valid, + hclocktime, read_time); else printf(_("Needed adjustment is less than one second, " "so not setting clock.\n")); - } else if (systohc) { + } else if (ctl->systohc) { struct timeval nowtime, reftime; /* * We can only set_hardware_clock_exact to a @@ -1375,37 +1282,34 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile, 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, nowtime, - hclock_valid, hclocktime, update); - } else if (hctosys) { - rc = set_system_clock(hclock_valid, hclocktime, - testing, universal); + set_hardware_clock_exact(ctl, (time_t) reftime.tv_sec, reftime); + if (!ctl->noadjfile) + adjust_drift_factor(ctl, adjtime, nowtime, + hclock_valid, hclocktime); + } else if (ctl->hctosys) { + rc = set_system_clock(ctl, hclock_valid, hclocktime); if (rc) { printf(_("Unable to set system clock.\n")); return rc; } - } else if (systz) { - rc = set_system_clock_timezone(universal, testing); + } else if (ctl->systz) { + rc = set_system_clock_timezone(ctl); if (rc) { printf(_("Unable to set system clock.\n")); return rc; } - } else if (predict) { + } else if (ctl->predict) { hclocktime = time_inc(hclocktime, (double) -(tdrift.tv_sec + tdrift.tv_usec / 1E6)); - if (debug) { + if (ctl->debug) { printf(_ ("At %ld seconds after 1969, RTC is predicted to read %ld seconds after 1969.\n"), - set_time, (long)hclocktime.tv_sec); + set_time, hclocktime.tv_sec); } display_time(TRUE, hclocktime); } - if (!noadjfile) - save_adjtime(adjtime, testing); + if (!ctl->noadjfile) + save_adjtime(ctl, adjtime); return 0; } @@ -1423,10 +1327,7 @@ manipulate_clock(const bool show, const bool adjust, const bool noadjfile, */ # 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__))) +manipulate_epoch(const struct hwclock_control *ctl __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 " @@ -1435,30 +1336,27 @@ manipulate_epoch(const bool getepoch __attribute__ ((__unused__)), } # else static void -manipulate_epoch(const bool getepoch, - const bool setepoch, - const unsigned long epoch_opt, - const bool testing) +manipulate_epoch(const struct hwclock_control *ctl) { - if (getepoch) { + if (ctl->getepoch) { unsigned long epoch; - if (get_epoch_rtc(&epoch, 0)) + if (get_epoch_rtc(ctl, &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 == ULONG_MAX) + } else if (ctl->setepoch) { + if (ctl->epoch_option == 0) warnx(_ ("To set the epoch value, you must use the 'epoch' " "option to tell to what value to set it.")); - else if (testing) + else if (ctl->testing) printf(_ ("Not setting the epoch to %lu - testing only.\n"), - epoch_opt); - else if (set_epoch_rtc(epoch_opt)) + ctl->epoch_option); + else if (set_epoch_rtc(ctl)) printf(_ ("Unable to set the epoch value in the kernel.\n")); } @@ -1466,70 +1364,6 @@ manipulate_epoch(const bool getepoch, # endif /* __alpha__ */ #endif /* __linux__ */ -/* - * Compare the system and CMOS time and output the drift - * in 10 second intervals. - */ -static int compare_clock (const bool utc, const bool local_opt) -{ - struct tm tm; - struct timeval tv; - struct adjtime adjtime; - double time1_sys, time2_sys; - time_t time1_hw, time2_hw; - bool hclock_valid = FALSE, universal, first_pass = TRUE; - int rc; - - if (ur->get_permissions()) - return EX_NOPERM; - - /* dummy call for increased precision */ - gettimeofday(&tv, NULL); - - rc = read_adjtime(&adjtime); - if (rc) - return rc; - - universal = hw_clock_is_utc(utc, local_opt, adjtime); - - synchronize_to_clock_tick(); - ur->read_hardware_clock(&tm); - - gettimeofday(&tv, NULL); - time1_sys = tv.tv_sec + tv.tv_usec / 1000000.0; - - mktime_tz(tm, universal, &hclock_valid, &time1_hw); - - while (1) { - double res; - - synchronize_to_clock_tick(); - ur->read_hardware_clock(&tm); - - gettimeofday(&tv, NULL); - time2_sys = tv.tv_sec + tv.tv_usec / 1000000.0; - - mktime_tz(tm, universal, &hclock_valid, &time2_hw); - - res = (((double) time1_hw - time1_sys) - - ((double) time2_hw - time2_sys)) - / (double) (time2_hw - time1_hw); - - if (!first_pass) - printf("%10.0f %10.6f %15.0f %4.0f\n", - (double) time2_hw, time2_sys, res * 1e6, res *1e4); - else { - first_pass = FALSE; - printf("hw-time system-time freq-offset-ppm tick\n"); - printf("%10.0f %10.6f\n", (double) time1_hw, time1_sys); - } - fflush(stdout); - sleep(10); - } - - return 0; -} - static void out_version(void) { printf(UTIL_LINUX_VERSION); @@ -1545,7 +1379,7 @@ static void out_version(void) * fmt, ... ), show a usage information and terminate the program * afterwards. */ -static void usage(const char *fmt, ...) +static void usage(const struct hwclock_control *ctl, const char *fmt, ...) { FILE *usageto; va_list ap; @@ -1568,7 +1402,6 @@ static void usage(const char *fmt, ...) " --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); - fputs(_(" -c, --compare periodically compare the system clock with the CMOS clock\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" @@ -1611,7 +1444,7 @@ static void usage(const char *fmt, ...) } fflush(usageto); - hwclock_exit(fmt ? EX_USAGE : EX_OK); + hwclock_exit(ctl, fmt ? EX_USAGE : EX_OK); } /* @@ -1625,7 +1458,9 @@ static void usage(const char *fmt, ...) */ int main(int argc, char **argv) { + struct hwclock_control ctl = { 0 }; struct timeval startup_time; + struct adjtime adjtime = { 0 }; /* * The time we started up, in seconds into the epoch, including * fractions. @@ -1633,15 +1468,6 @@ int main(int argc, char **argv) time_t set_time = 0; /* Time to which user said to set Hardware Clock */ int rc, c; - /* Variables set by various options; show may also be set later */ - /* The options debug, badyear and epoch_option are global */ - bool show, set, systohc, hctosys, systz, adjust, getepoch, setepoch, - predict, compare, get; - bool utc, testing, local_opt, update, noadjfile, directisa; - char *date_opt; -#ifdef __alpha__ - bool ARCconsole, Jensen, SRM, funky_toy; -#endif /* Long only options. */ enum { OPT_ADJFILE = CHAR_MAX + 1, @@ -1663,7 +1489,6 @@ int main(int argc, char **argv) static const struct option longopts[] = { {"adjust", 0, 0, 'a'}, - {"compare", 0, 0, 'c'}, {"help", 0, 0, 'h'}, {"show", 0, 0, 'r'}, {"hctosys", 0, 0, 's'}, @@ -1742,107 +1567,95 @@ int main(int argc, char **argv) textdomain(PACKAGE); atexit(close_stdout); - /* Set option defaults */ - show = set = systohc = hctosys = systz = adjust = noadjfile = predict = - compare = get = update = 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, - "?hvVDacrsuwAJSFf:", longopts, NULL)) != -1) { + "?hvVDarsuwAJSFf:", longopts, NULL)) != -1) { err_exclusive_options(c, longopts, excl, excl_st); switch (c) { case 'D': - ++debug; + ctl.debug++; break; case 'a': - adjust = TRUE; - break; - case 'c': - compare = TRUE; + ctl.adjust = 1; break; case 'r': - show = TRUE; + ctl.show = 1; break; case 's': - hctosys = TRUE; + ctl.hctosys = 1; break; case 'u': - utc = TRUE; + ctl.utc = 1; break; case 'w': - systohc = TRUE; + ctl.systohc = 1; break; #ifdef __alpha__ case 'A': - ARCconsole = TRUE; + ctl.ARCconsole = 1; break; case 'J': - Jensen = TRUE; + ctl.Jensen = 1; break; case 'S': - SRM = TRUE; + ctl.SRM = 1; break; case 'F': - funky_toy = TRUE; + ctl.funky_toy = 1; break; #endif case OPT_SET: - set = TRUE; + ctl.set = 1; break; #ifdef __linux__ case OPT_GETEPOCH: - getepoch = TRUE; + ctl.getepoch = 1; break; case OPT_SETEPOCH: - setepoch = TRUE; + ctl.setepoch = 1; break; #endif case OPT_NOADJFILE: - noadjfile = TRUE; + ctl.noadjfile = 1; break; case OPT_LOCALTIME: - local_opt = TRUE; /* --localtime */ + ctl.local_opt = 1; /* --localtime */ break; case OPT_BADYEAR: - badyear = TRUE; + ctl.badyear = 1; break; case OPT_DIRECTISA: - directisa = TRUE; + ctl.directisa = 1; break; case OPT_TEST: - testing = TRUE; /* --test */ + ctl.testing = 1; /* --test */ break; case OPT_DATE: - date_opt = optarg; /* --date */ + ctl.date_opt = optarg; /* --date */ break; case OPT_EPOCH: - epoch_option = /* --epoch */ + ctl.epoch_option = /* --epoch */ strtoul_or_err(optarg, _("invalid epoch argument")); break; case OPT_ADJFILE: - adj_file_name = optarg; /* --adjfile */ + ctl.adj_file_name = optarg; /* --adjfile */ break; case OPT_SYSTZ: - systz = TRUE; /* --systz */ + ctl.systz = 1; /* --systz */ break; case OPT_PREDICT_HC: - predict = TRUE; /* --predict-hc */ + ctl.predict = 1; /* --predict-hc */ break; case OPT_GET: - get = TRUE; /* --get */ + ctl.get = 1; /* --get */ break; case OPT_UPDATE: - update = TRUE; /* --update-drift */ + ctl.update = 1; /* --update-drift */ break; #ifdef __linux__ case 'f': - rtc_dev_name = optarg; /* --rtc */ + ctl.rtc_dev_name = optarg; /* --rtc */ break; #endif case 'v': /* --version */ @@ -1850,9 +1663,9 @@ int main(int argc, char **argv) out_version(); return 0; case 'h': /* --help */ - usage(NULL); + case '?': default: - errtryhelp(EXIT_FAILURE); + usage(&ctl, NULL); } } @@ -1860,100 +1673,89 @@ int main(int argc, char **argv) argv += optind; #ifdef HAVE_LIBAUDIT - if (testing != TRUE) { - if (adjust == TRUE || hctosys == TRUE || systohc == TRUE || - set == TRUE || setepoch == TRUE) { - hwaudit_on = TRUE; + if (!ctl.testing) { + if (ctl.adjust || ctl.hctosys || ctl.systohc || + ctl.set || ctl.setepoch) { + ctl.hwaudit_on = 1; } } #endif if (argc > 0) { - usage(_("%s takes no non-option arguments. " + usage(&ctl, _("%s takes no non-option arguments. " "You supplied %d.\n"), program_invocation_short_name, argc); } - if (!adj_file_name) - adj_file_name = _PATH_ADJTIME; + if (!ctl.adj_file_name) + ctl.adj_file_name = _PATH_ADJTIME; - if (noadjfile && !utc && !local_opt) { + if (ctl.noadjfile && !ctl.utc && !ctl.local_opt) { warnx(_("With --noadjfile, you must specify " "either --utc or --localtime")); - hwclock_exit(EX_USAGE); + hwclock_exit(&ctl, EX_USAGE); } #ifdef __alpha__ - set_cmos_epoch(ARCconsole, SRM); - set_cmos_access(Jensen, funky_toy); + set_cmos_epoch(&ctl); + set_cmos_access(&ctl); #endif - if (set || predict) { - rc = interpret_date_string(date_opt, &set_time); + if (ctl.set || ctl.predict) { + rc = interpret_date_string(&ctl, &set_time); /* (time-consuming) */ if (rc != 0) { warnx(_("No usable set-to time. " "Cannot set clock.")); - hwclock_exit(EX_USAGE); + hwclock_exit(&ctl, EX_USAGE); } } - if (!(show | set | systohc | hctosys | systz | adjust | getepoch - | setepoch | predict | compare | get)) - show = 1; /* default to show */ - + if (!(ctl.show | ctl.set | ctl.systohc | ctl.hctosys | + ctl.systz | ctl.adjust | ctl.getepoch | ctl.setepoch | + ctl.predict | ctl.get)) + ctl.show = 1; /* default to show */ #ifdef __linux__ - if (getepoch || setepoch) { - manipulate_epoch(getepoch, setepoch, epoch_option, testing); - hwclock_exit(EX_OK); + if (ctl.getepoch || ctl.setepoch) { + manipulate_epoch(&ctl); + hwclock_exit(&ctl, EX_OK); } #endif - if (debug) + if (ctl.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); - } - } + if (!ctl.systz && !ctl.predict) + determine_clock_access_method(&ctl); - if (compare) { - if (compare_clock(utc, local_opt)) - hwclock_exit(EX_NOPERM); - - rc = EX_OK; + if (!ctl.noadjfile && !(ctl.systz && (ctl.utc || ctl.local_opt))) { + if ((rc = read_adjtime(&ctl, &adjtime)) != 0) + hwclock_exit(&ctl, rc); } else - rc = manipulate_clock(show, adjust, noadjfile, set, set_time, - hctosys, systohc, systz, startup_time, utc, - local_opt, update, testing, predict, get); - - hwclock_exit(rc); + /* Avoid writing adjtime file if we don't have to. */ + adjtime.dirty = FALSE; + ctl.universal = hw_clock_is_utc(&ctl, adjtime); + rc = manipulate_clock(&ctl, set_time, startup_time, &adjtime); + hwclock_exit(&ctl, 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) +void __attribute__((__noreturn__)) +hwclock_exit(const struct hwclock_control *ctl +#ifndef HAVE_LIBAUDIT + __attribute__((__unused__)) +#endif + , int status) { - if (hwaudit_on) { +#ifdef HAVE_LIBAUDIT + if (ctl->hwaudit_on) { audit_log_user_message(hwaudit_fd, AUDIT_USYS_CONFIG, "op=change-system-time", NULL, NULL, NULL, status ? 0 : 1); close(hwaudit_fd); } +#endif exit(status); } -#endif /* * History of this program: diff --git a/sys-utils/hwclock.h b/sys-utils/hwclock.h index 974d96a40..b08bd2d7a 100644 --- a/sys-utils/hwclock.h +++ b/sys-utils/hwclock.h @@ -9,16 +9,62 @@ #include "c.h" +enum { + RTC_BUSYWAIT_OK = 0, + RTC_BUSYWAIT_FAILED, + RTC_BUSYWAIT_TIMEOUT +}; + +struct hwclock_control { + char *date_opt; + unsigned long epoch_option; + char *adj_file_name; +#ifdef __linux__ + char *rtc_dev_name; +#endif + unsigned int debug; + unsigned int +#ifdef HAVE_LIBAUDIT + hwaudit_on:1, +#endif + adjust:1, + show:1, + hctosys:1, + utc:1, + systohc:1, +#ifdef __alpha__ + ARCconsole:1, + Jensen:1, + SRM:1, + funky_toy:1, +#endif +#ifdef __linux__ + getepoch:1, + setepoch:1, +#endif + noadjfile:1, + local_opt:1, + badyear:1, + directisa:1, + testing:1, + systz:1, + predict:1, + get:1, + set:1, + update:1, + universal:1; /* will store hw_clock_is_utc() return value */ +}; + 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); + int (*read_hardware_clock) (const struct hwclock_control *ctl, struct tm * tm); + int (*set_hardware_clock) (const struct hwclock_control *ctl, const struct tm * tm); + int (*synchronize_to_clock_tick) (const struct hwclock_control *ctl); }; extern struct clock_ops *probe_for_cmos_clock(void); -extern struct clock_ops *probe_for_rtc_clock(void); +extern struct clock_ops *probe_for_rtc_clock(const struct hwclock_control *ctl); typedef int bool; @@ -27,19 +73,13 @@ 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); +extern void set_cmos_epoch(const struct hwclock_control *ctl); +extern void set_cmos_access(const struct hwclock_control *ctl); /* 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; +extern int get_epoch_rtc(const struct hwclock_control *ctl, unsigned long *epoch, int silent); +extern int set_epoch_rtc(const struct hwclock_control *ctl); -#ifdef HAVE_LIBAUDIT -extern void hwaudit_exit(int status); -# define hwclock_exit(_status) hwaudit_exit(_status) -#else -# define hwclock_exit(_status) exit(_status) -#endif +extern void hwclock_exit(const struct hwclock_control *ctl, int status); #endif /* HWCLOCK_CLOCK_H */ |