diff options
Diffstat (limited to 'clockB')
| -rw-r--r-- | clockB/Makefile | 31 | ||||
| -rw-r--r-- | clockB/directio.c | 727 | ||||
| -rw-r--r-- | clockB/hwclock.8 | 599 | ||||
| -rw-r--r-- | clockB/hwclock.c | 1744 | ||||
| -rw-r--r-- | clockB/hwclock.h | 139 | ||||
| -rw-r--r-- | clockB/kd.c | 219 | ||||
| -rw-r--r-- | clockB/rtc.c | 425 | ||||
| -rw-r--r-- | clockB/shhopt.c | 467 | ||||
| -rw-r--r-- | clockB/shhopt.h | 33 | ||||
| -rw-r--r-- | clockB/util.c | 120 |
10 files changed, 0 insertions, 4504 deletions
diff --git a/clockB/Makefile b/clockB/Makefile deleted file mode 100644 index 1a2f4ecd6f..0000000000 --- a/clockB/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -# Makefile -- Makefile for util-linux Linux utilities -# -include ../make_include -include ../MCONFIG - -# Where to put man pages? - -MAN8= hwclock.8 - -# Where to put binaries? -# See the "install" rule for the links. . . - -SBIN= hwclock - - -all: $(SBIN) - - -CFLAGS = -s -Wall -Wwrite-strings -Wstrict-prototypes -Winline $(CDEBUG) - -hwclock: hwclock.o rtc.o directio.o kd.o util.o shhopt.o -hwclock.o: shhopt.h -hwclock.o rtc.o directio.o kd.o util.o: hwclock.h - -install: all - $(INSTALLDIR) $(SBINDIR) $(BINDIR) $(USRBINDIR) - $(INSTALLBIN) $(SBIN) $(SBINDIR) - $(INSTALLMAN) $(MAN8) $(MAN8DIR) - -clean: - -rm -f *.o *~ core $(SBIN) diff --git a/clockB/directio.c b/clockB/directio.c deleted file mode 100644 index d3a77eb196..0000000000 --- a/clockB/directio.c +++ /dev/null @@ -1,727 +0,0 @@ -/************************************************************************** - - This is a component of the hwclock program. - - This file contains the code for accessing the hardware clock via - direct I/O (kernel-style input and output operations) as opposed - to via a device driver. - - - MAINTENANCE NOTES - - Here is some information on how the Hardware Clock works, from - unknown source and authority. In theory, the specification for this - stuff is the specification of Motorola's MC146818A clock chip, used - in the early ISA machines. Subsequent machines should have copied - its function exactly. In reality, though, the copies are inexact - and the MC146818A itself may fail to implement its specifications, - and we have just have to work with whatever is there (actually, - anything that Windows works with, because that's what determines - whether broken hardware has to be fixed!). - - 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. - - The century situation is messy: - - Usually byte 50 (0x32) gives the century (in BCD, so 0x19 or 0x20 in - pure binary), 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. - - 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 - 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) - - Avoid setting the RTC clock within 2 seconds of the day rollover - that starts a new month or enters daylight saving time. - -****************************************************************************/ - -#include <stdio.h> -#include <fcntl.h> -#include <unistd.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> - -#if defined(__i386__) || defined(__alpha__) -#include <asm/io.h> /* for inb, outb */ -#else -void outb(int a, int b){} -int inb(int c){ return 0; } -#endif - -#include "hwclock.h" - -#define BCD_TO_BIN(val) (((val)&0x0f) + ((val)>>4)*10) -#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10) - - -/*---------------------------------------------------------------------------- - ATOMIC_TOP and ATOMIC_BOTTOM are wierd macros that help us to do - atomic operations when we do ugly low level I/O. - - You put ATOMIC_TOP above some code and ATOMIC_BOTTOM below it and - it makes sure all the enclosed code executes without interruption - by some other process (and, in some cases, even the kernel). - - These work fundamentally differently depending on the machine - architecture. In the case of a x86, it simply turns interrupts off - at the top and turns them back on at the bottom. - - For Alpha, we can't mess with interrupts (we shouldn't for x86 - either, but at least it tends to work!), so instead we start a loop - at the top and close it at the bottom. This loop repeats the - enclosed code until the upper 32 bits of the cycle counter are the - same before and after. That means there was no context change - while the enclosed code was executing. - - For other architectures, we do nothing, and the atomicity is only - feigned. - ------------------------------------------------------------------------------*/ - -#if defined(__i386__) -#define ATOMIC_TOP \ - { \ - const bool interrupts_were_enabled = interrupts_enabled; \ - __asm__ volatile ("cli"); \ - interrupts_enabled = FALSE; - -#define ATOMIC_BOTTOM \ - if (interrupts_were_enabled) { \ - __asm__ volatile ("sti"); \ - interrupts_enabled = TRUE; \ - } \ - } -#elif defined(__alpha__) -#define ATOMIC_TOP \ - { \ - unsigned long ts1, ts2, n; \ - n = 0; \ - do { \ - asm volatile ("rpcc %0" : "r="(ts1)); - -#define ATOMIC_BOTTOM \ - asm volatile ("rpcc %0" : "r="(ts2)); \ - n++; \ - } while ((ts1 ^ ts2) >> 32 != 0); \ - } -#else -#define ATOMIC_BOTTOM -#define ATOMIC_TOP -#endif - - -#if defined(__i386__) || defined(__alpha__) -/* The following are just constants. Oddly, this program will not - compile if the inb() and outb() functions use something even - slightly different from these variables. This is probably at least - partially related to the fact that __builtin_constant_p() doesn't - work (is never true) in an inline function. See comment to this - effect in asm/io.h. -*/ -static unsigned short clock_ctl_addr = 0x70; -static unsigned short clock_data_addr = 0x71; -#endif - - -static bool interrupts_enabled; - /* Interrupts are enabled as normal. We, unfortunately, turn interrupts - on the machine off in some places where we do the direct ISA accesses - to the Hardware Clock. It is in extremely poor form for a user space - program to do this, but that's the price we have to pay to run on an - ISA machine without the rtc driver in the kernel. - - Code which turns interrupts off uses this value to determine if they - need to be turned back on. - */ - - -void -assume_interrupts_enabled(void) { - interrupts_enabled = TRUE; -} - - - -static int -i386_iopl(const int level) { -/*---------------------------------------------------------------------------- - When compiled for an Intel target, this is just the iopl() kernel call. - When compiled for any other target, this is a dummy function. - - We do it this way in order to keep the conditional compilation stuff - out of the way so it doesn't mess up readability of the code. ------------------------------------------------------------------------------*/ -#ifdef __i386__ - extern int iopl(int level); - return iopl(level); -#else - return -1; -#endif -} - - - -bool -uf_bit_needed(const bool user_wants_uf) { -/*---------------------------------------------------------------------------- - Return true iff the UIP bit doesn't work on this hardware clock, so - we will need to use the UF bit to synchronize with the clock (if in - fact we synchronize using direct I/O to the clock). - - To wit, we need to use the UF bit on a DEC Alpha PC164/LX164/SX164. - Or, of course, if the user told us to. ------------------------------------------------------------------------------*/ - bool retval; - - if (user_wants_uf) retval = TRUE; - else { - if (alpha_machine && ( - is_in_cpuinfo("system variation", "PC164") || - is_in_cpuinfo("system variation", "LX164") || - is_in_cpuinfo("system variation", "SX164"))) - retval = TRUE; - else retval = FALSE; - } - if (debug && retval) - printf("We will be using the UF bit instead of the usual " - "UIP bit to synchronize with the clock, as required on " - "certain models of DEC Alpha.\n"); - - return retval; -} - - - -int -zero_year(const bool arc_opt, const bool srm_opt) { -/*---------------------------------------------------------------------------- - Return the year of the century (e.g. 0) to which a zero value in - the year register of the hardware clock applies (or at least what - we are to assume -- nobody can say for sure!) - - 'arc_opt' and 'srm_opt' are the true iff the user specified the - corresponding invocation option to instruct us that the machine is an - Alpha with ARC or SRM console time. - - A note about hardware clocks: - - ISA machines are simple: the year register is a year-of-century - register, so the zero year is zero. On Alphas, we may see 1980 or - 1952 (Digital Unix?) or 1958 (ALPHA_PRE_V1_2_SRM_CONSOLE) ------------------------------------------------------------------------------*/ - int retval; /* our return value */ - - if (arc_opt || srm_opt) { - /* User is telling us what epoch his machine uses. Believe it. */ - if (arc_opt) retval = 0; - else retval = 0; - } else { - unsigned long kernel_epoch; - char *reason; /* malloc'ed */ - - get_epoch(&kernel_epoch, &reason); - if (reason == NULL) retval = kernel_epoch; - else { - /* OK, the user doesn't know and the kernel doesn't know; - figure it out from the machine model - */ - free(reason); /* Don't care about kernel's excuses */ - /* See whether we are dealing with SRM or MILO, as they have - different "epoch" ideas. */ - if (is_in_cpuinfo("system serial number", "MILO")) { - if (debug) printf("booted from MILO\n"); - /* See whether we are dealing with a RUFFIAN aka UX, 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"); - retval = 0; - } else { - if (debug) printf("Not Ruffian BCD clock\n"); - retval = 80; - } - } else { - if (debug) printf("Not booted from MILO\n"); - retval = 0; - } - } - } - return retval; -} - - - -static inline unsigned char -hclock_read(const unsigned char reg, const int dev_port) { -/*--------------------------------------------------------------------------- - Relative byte 'reg' of the Hardware Clock value. - - Get this with direct CPU I/O instructions. If 'dev_port' is not -1, - use the /dev/port device driver (via the 'dev_port' file descriptor) - to do this I/O. Otherwise, use the kernel's inb()/outb() facility. - - On a system without the inb()/outb() facility, if 'dev_port' is -1, - just return 0. - - Results undefined if 'reg' is out of range. ----------------------------------------------------------------------------*/ - unsigned char ret; - - ATOMIC_TOP - if (dev_port >= 0) { - const unsigned char v = reg | 0x80; - lseek(dev_port, 0x170, 0); - write(dev_port, &v, 1); - lseek(dev_port, 0x171, 0); - read(dev_port, &ret, 1); - } else { -#if defined(__i386__) || defined(__alpha__) - /* & 0x7f ensures that we are not disabling NMI while we read. - Setting on Bit 7 here would disable NMI - - 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. - */ - outb(reg & 0x7f, clock_ctl_addr); - ret = inb(clock_data_addr); -#else - ret = 0; -#endif - } - ATOMIC_BOTTOM - return ret; -} - - - -static inline void -hclock_write(unsigned char reg, unsigned char val, const int dev_port) { -/*---------------------------------------------------------------------------- - Set relative byte 'reg' of the Hardware Clock value to 'val'. - Do this with the kernel's outb() function if 'dev_port' is -1, but - if not, use the /dev/port device (via the 'dev_port' file descriptor), - which is almost the same thing. - - On a non-ISA, non-Alpha machine, if 'dev_port' is -1, do nothing. -----------------------------------------------------------------------------*/ - if (dev_port >= 0) { - unsigned char v; - v = reg | 0x80; - lseek(dev_port, 0x170, 0); - write(dev_port, &v, 1); - v = (val & 0xff); - lseek(dev_port, 0x171, 0); - write(dev_port, &v, 1); - } else { -#if defined(__i386__) || defined(__alpha__) - /* & 0x7f ensures that we are not disabling NMI while we read. - Setting on Bit 7 here would disable NMI - */ - outb(reg & 0x7f, clock_ctl_addr); - outb(val, clock_data_addr); -#endif - } -} - - - -static inline int -hardware_clock_busy(const int dev_port, const bool use_uf_bit) { -/*---------------------------------------------------------------------------- - Return whether the hardware clock is in the middle of an update - (which happens once each second). - - Use the clock's UIP bit (bit 7 of Control Register A) to tell - unless 'use_uf_bit' is true, in which case use the UF bit (bit 4 of - Control Register C). ------------------------------------------------------------------------------*/ - return - use_uf_bit ? (hclock_read(12, dev_port) & 0x10) : - (hclock_read(10, dev_port) & 0x80); -} - - - -void -synchronize_to_clock_tick_ISA(int *retcode_p, const int dev_port, - const bool use_uf_bit) { -/*---------------------------------------------------------------------------- - Same as synchronize_to_clock_tick(), but just for ISA. ------------------------------------------------------------------------------*/ - int i; /* local loop index */ - - /* 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; - !hardware_clock_busy(dev_port, use_uf_bit) && (i < 10000000); - i++); - if (i >= 10000000) *retcode_p = 1; - else { - /* Wait for fall. Should be within 2.228 ms. */ - for (i = 0; - hardware_clock_busy(dev_port, use_uf_bit) && (i < 1000000); - i++); - if (i >= 10000000) *retcode_p = 1; - else *retcode_p = 0; - } -} - - - -void -read_hardware_clock_isa(struct tm *tm, const int dev_port, - int hc_zero_year) { -/*---------------------------------------------------------------------------- - 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. If 'dev_port' isn't -1, use the /dev/port facility to - do this I/O. Otherwise, use the kernel's inb()/outb() service. - - 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 off interrupts). - - In practice, the chance of this function returning the wrong time is - extremely remote. - ------------------------------------------------------------------------------*/ - bool got_time; - /* We've successfully read a time from the Hardware Clock */ - int attempts; - /* Number of times we've tried to read the clock. This only - matters because we will give up (and proceed with garbage in - variables) rather than hang if something is broken and we are - never able to read the clock - */ - int hclock_sec = 0, hclock_min = 0, hclock_hour = 0, hclock_wday = 0, - hclock_mon = 0, hclock_mday = 0, hclock_year = 0; - /* The values we got from the Hardware Clock's registers, assuming - they are in pure binary. - */ - - int status = 0; /* Hardware Clock status register, as if pure binary */ - int adjusted_year; - int ampmhour; - int pmbit; - - got_time = FALSE; - attempts = 0; /* initial value */ - while (!got_time && attempts++ < 1000000) { - /* 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 ((hclock_read(10, dev_port) & 0x80) == 0) { - /* No clock update in progress, go ahead and read */ - - status = hclock_read(11, dev_port); - - hclock_sec = hclock_read(0, dev_port); - hclock_min = hclock_read(2, dev_port); - hclock_hour = hclock_read(4, dev_port); - hclock_wday = hclock_read(6, dev_port); - hclock_mday = hclock_read(7, dev_port); - hclock_mon = hclock_read(8, dev_port); - hclock_year = hclock_read(9, dev_port); - /* Unless the clock changed while we were reading, consider this - a good clock read . - */ - if (hclock_sec == hclock_read(0, dev_port)) got_time = TRUE; - /* Yes, in theory we could have been running for 60 seconds and - the above test wouldn't work! - */ - } - } - - if (!(status & 0x04)) { - /* The hardware clock is in BCD mode. This is normal. */ - tm->tm_sec = BCD_TO_BIN(hclock_sec); - tm->tm_min = BCD_TO_BIN(hclock_min); - ampmhour = BCD_TO_BIN(hclock_hour & 0x7f); - pmbit = hclock_hour & 0x80; - tm->tm_wday = BCD_TO_BIN(hclock_wday) - 1; /* Used to be 3. Why?? */ - tm->tm_mday = BCD_TO_BIN(hclock_mday); - tm->tm_mon = BCD_TO_BIN(hclock_mon) - 1; - adjusted_year = BCD_TO_BIN(hclock_year); - } else { - /* The hardware clock registers are in pure binary format. */ - tm->tm_sec = hclock_sec; - tm->tm_min = hclock_min; - ampmhour = hclock_hour & 0x7f; - pmbit = hclock_hour & 0x80; - tm->tm_wday = hclock_wday - 1; /* Used to be 3. Why?? */ - tm->tm_mday = hclock_mday; - tm->tm_mon = hclock_mon - 1; - adjusted_year = hclock_year; - } - - if (!(status & 0x02)) { - /* Clock is in 12 hour (am/pm) mode. This is unusual. */ - if (pmbit == 0x80) { - if (ampmhour == 12) tm->tm_hour = 12; - else tm->tm_hour = 12 + ampmhour; - } else { - if (ampmhour ==12) tm->tm_hour = 0; - else tm->tm_hour = ampmhour; - } - } else { - /* Clock is in 24 hour mode. This is normal. */ - tm->tm_hour = ampmhour; - } - /* We don't use the century byte (Byte 50) of the Hardware Clock. - Here's why: It didn't exist in the original ISA specification, - so old machines don't have it, and even some new ones don't. - Some machines, including the IBM Valuepoint 6387-X93, use that - byte for something else. Some machines have the century in - Byte 55. - - Furthermore, the Linux standard time data structure doesn't - allow for times beyond about 2037 and no Linux systems were - running before 1937. Therefore, all the century byte could tell - us is that the clock is wrong or this whole program is obsolete! - - So we just say if the year of century is less than 37, it's the - 2000's, otherwise it's the 1900's. - - Alpha machines (some, anyway) don't have this ambiguity - because they do not have a year-of-century register. We - pretend they do anyway, for simplicity and to avoid - recognizing times that can't be represented in Linux standard - time. So even though we already have enough information to - know that the clock says 2050, we will render it as 1950. - */ - { - const int year_of_century = (adjusted_year + hc_zero_year) % 100; - if (year_of_century >= 37) tm->tm_year = year_of_century; - else tm->tm_year = year_of_century + 100; - } - tm->tm_isdst = -1; /* don't know whether it's daylight */ -} - - - -void -set_hardware_clock_isa(const struct tm new_tm, - const int hc_zero_year, - const int dev_port, - const bool testing) { -/*---------------------------------------------------------------------------- - Set the Hardware Clock to the time (in broken down format) - new_tm. Use direct I/O instructions to what we assume is - an ISA Hardware Clock. - - Iff 'dev_port' is -1, use the kernel inb()/outb() service, otherwise - use the /dev/port device (via file descriptor 'dev_port') - to do those I/O instructions. -----------------------------------------------------------------------------*/ - unsigned char save_control, save_freq_select; - - if (testing) - printf("Not setting Hardware Clock because running in test mode.\n"); - else { - int ampmhour; - /* The hour number that goes into the hardware clock, taking into - consideration whether the clock is in 12 or 24 hour mode - */ - int pmbit; - /* Value to OR into the hour register as the am/pm bit */ - const int adjusted_year = - (new_tm.tm_year - hc_zero_year)%100; - /* The number that goes in the hardware clock's year register */ - - int hclock_sec, hclock_min, hclock_hour, hclock_wday, hclock_mon, - hclock_mday, hclock_year; - /* The values we will put, in pure binary, in the Hardware Clock's - registers. - */ - - ATOMIC_TOP - - save_control = hclock_read(11, dev_port); - /* tell the clock it's being set */ - hclock_write(11, (save_control | 0x80), dev_port); - save_freq_select = hclock_read(10, dev_port); - /* stop and reset prescaler */ - hclock_write (10, (save_freq_select | 0x70), dev_port); - - - if (!(save_control & 0x02)) { - /* Clock is in 12 hour (am/pm) mode. This is unusual. */ - if (new_tm.tm_hour == 0) { - ampmhour = 12; - pmbit = 0x00; - } else if (new_tm.tm_hour < 12) { - ampmhour = new_tm.tm_hour; - pmbit = 0x00; - } else if (new_tm.tm_hour == 12) { - ampmhour = 12; - pmbit = 0x80; - } else { - ampmhour = new_tm.tm_hour - 12; - pmbit = 0x80; - } - } else { - /* Clock is in 24 hour mode. This is normal. */ - ampmhour = new_tm.tm_hour; - pmbit = 0x00; - } - - - if (!(save_control & 0x04)) { - /* Clock's registers are in BCD. This is normal. */ - hclock_sec = BIN_TO_BCD(new_tm.tm_sec); - hclock_min = BIN_TO_BCD(new_tm.tm_min); - hclock_hour = pmbit | BIN_TO_BCD(ampmhour); - hclock_wday = BIN_TO_BCD(new_tm.tm_wday + 1); /* Used to be 3. Why??*/ - hclock_mday = BIN_TO_BCD(new_tm.tm_mday); - hclock_mon = BIN_TO_BCD(new_tm.tm_mon + 1); - hclock_year = BIN_TO_BCD(adjusted_year); - } else { - /* Clock's registers are in pure binary. This is unusual. */ - hclock_sec = new_tm.tm_sec; - hclock_min = new_tm.tm_min; - hclock_hour = pmbit | ampmhour; - hclock_wday = new_tm.tm_wday + 1; /* Used to be 3. Why?? */ - hclock_mday = new_tm.tm_mday; - hclock_mon = new_tm.tm_mon + 1; - hclock_year = adjusted_year; - } - - hclock_write(0, hclock_sec, dev_port); - hclock_write(2, hclock_min, dev_port); - hclock_write(4, hclock_hour, dev_port); - hclock_write(6, hclock_wday, dev_port); - hclock_write(7, hclock_mday, dev_port); - hclock_write(8, hclock_mon, dev_port); - hclock_write(9, hclock_year, dev_port); - - /* We don't set the century byte (usually Byte 50) because it isn't - always there. (see further comments in read_hardware_clock_isa). - In previous releases, we did. - */ - - /* 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 - - Hence, they will also be done in this order here. - faith@cs.unc.edu, Thu Nov 9 08:26:37 1995 - */ - - hclock_write (11, save_control, dev_port); - hclock_write (10, save_freq_select, dev_port); - - ATOMIC_BOTTOM - } -} - - - -void -get_inb_outb_privilege(const enum clock_access_method clock_access, - bool * const no_auth_p) { - - if (clock_access == ISA) { - const int rc = i386_iopl(3); - if (rc != 0) { - fprintf(stderr, MYNAME " is unable to get I/O port access. " - "I.e. iopl(3) returned nonzero return code %d.\n" - "This is often because the program isn't running " - "with superuser privilege, which it needs.\n", - rc); - *no_auth_p = TRUE; - } else *no_auth_p = FALSE; - } else *no_auth_p = FALSE; -} - - - -void -get_dev_port_access(const enum clock_access_method clock_access, - int * dev_port_p) { - - if (clock_access == DEV_PORT) { - /* Get the /dev/port file open */ - *dev_port_p = open("/dev/port", O_RDWR); - if (*dev_port_p < 0) { - fprintf(stderr, MYNAME "is unable to open the /dev/port file. " - "I.e. open() of the file failed with errno = %s (%d).\n" - "Run with the --debug option and check documentation " - "to find out why we are trying " - "to use /dev/port instead of some other means to access " - "the Hardware Clock.", - strerror(errno), errno); - } - } else *dev_port_p = 0; -} - - - diff --git a/clockB/hwclock.8 b/clockB/hwclock.8 deleted file mode 100644 index 0a216840ae..0000000000 --- a/clockB/hwclock.8 +++ /dev/null @@ -1,599 +0,0 @@ -.TH CLOCK 8 "02 March 1998" -.SH NAME -clock \- query and set the hardware clock (RTC) -.SH SYNOPSIS -.B "hwclock --show" -.br -.B "hwclock --set --date=newdate" -.br -.B "hwclock --systohc" -.br -.B "hwclock --hctosys" -.br -.B "hwclock --getepoch" -.br -.B "hwclock --setepoch --epoch=year" -.br -.B "hwclock --adjust" -.br -.B "hwclock --version" -.PP -other options: -.PP -.B "--utc --localtime --directisa --test --debug" -.PP -and arcane options for DEC Alpha: -.PP -.B "--arc --jensen --srm --funky-toy" -.PP -Minimum unique abbreviations of all options are acceptable. -.PP -Also, equivalent options -r, -w, -s, -a, -v, -u, -D, -A, -J, -S, and -F -are accepted for compatibility with the program "clock". - -.SH DESCRIPTION -.I hwclock -is a tool for accessing the Hardware Clock. You can display the -current time, set the Hardware Clock to a specified time, set the -Hardware Clock to the System Time, and set the System Time from the -Hardware Clock. -.PP -You can also run -.I hwclock -periodically to insert or remove time from the Hardware Clock to -compensate for systematic drift (where the clock consistently gains or -loses time at a certain rate if left to run). - -.SH OPTIONS -You need exactly one of the following options to tell -.I hwclock -what function to perform: -.PP -.TP -.B \-\-show -Read the Hardware Clock and print the time on Standard Output. -The time is always in local time, even if you keep your Hardware Clock -in Coordinated Universal Time. See the -.B \-\-utc -option. - -.TP -.B \-\-set -Set the Hardware Clock to the time given by the -.B \-\-date -option. -.TP -.B \-\-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/lib/zoneinfo , -as -.BR tzset (3) -would interpret them. EXCEPT: always set the Daylight Savings Time part of -the kernel's timezone value to 0 ("not Daylight Savings Time"). If DST -is indicated, just add an hour to the base part. - -See the discussion of timezones below. - -This is a good option to use in one of the system startup scripts. -.TP -.B \-\-systohc -Set the Hardware Clock to the current System 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 out standard output the kernel's Hardware Clock epoch value. -This is the number of years into AD to which a zero year value in the -Hardware Clock refers. For example, if you are using the convention -that the year counter in your Hardware Clock contains the number of -full years since 1952, then the kernel's Hardware Counter epoch value -must be 1952. - -This epoch value is used whenever hwclock reads or sets the Hardware Clock. -.TP -.B \-\-setepoch -Set the kernel's Hardware Clock epoch value to the value specified by the -.B \-\-epoch -option. See the -.B \-\-getepoch -option for details. -.TP -.B \-\-version -Print the version of -.I hwclock -on Standard Output. -.br -You need the following option if you specify -.B \-\-set -option. Otherwise, it is ignored. -.TP -.B \-\-date=date_string -Specifies the time to which to set the Hardware Clock. The value of this -option is an argument to the -.I date(1) -program. For example, -.sp -.I hwclock --set --date="9/22/96 16:45:05" -.sp -The argument is in local time, even if you keep your Hardware Clock in -Coordinated Universal time. See the -.I \-\-utc -option. - -.TP -.B \-\-epoch=year -Specifies the year which is the beginning of the Hardware Clock's -epoch. I.e. the number of years into AD to which a zero value in the -Hardware Clock's year counter refers. - -For example, -.sp -.I hwclock --setepoch --epoch=1952 - -.PP -The following options apply to most functions. -.TP -.B \-\-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 -.IR hwclock . - -If you specify the wrong one of these options (or specify neither and -take a wrong default), both setting and querying of the Hardware Clock -will be messed up. - -If you specify neither -.B \-\-utc -nor -.B \-\-localtime -, the default is whichever was specified the last time -.I hwclock -was used to set the clock (i.e. hwclock was successfully run with the -.B \-\-set -, -.B \-\-systohc -, -or -.B \-\-adjust -options), as recorded in the adjtime file. If the adjtime file doesn't -exist, the default is local time. - -.TP -.B \-\-directisa -is meaningful only on an ISA machine or an Alpha (which implements enough -of ISA to be, roughly speaking, an ISA machine for -.IR hwclock 's -purposes). For other machines, it has no effect. This option tells -.I hwclock -to use explicit I/O instructions to access the Hardware Clock. -Without this option, -.I hwclock -will try to use the /dev/rtc device (which it assumes to be driven by the -rtc device driver). If it is unable to open the device (for read), it will -use the explicit I/O instructions anyway. - -The rtc device driver was new in Linux Release 2. -.TP -.B \-\-badyear -Indicates that the Hardware Clock is incapable of storing years outside -the range 1994-1999. There is a problem in some BIOSes (almost all -Award BIOSes made between 4/26/94 and 5/31/95) wherein they are unable -to deal with years after 1999. If one attempts to set the year-of-century -value to something less than 94 (or 95 in some cases), the value that -actually gets set is 94 (or 95). Thus, if you have one of these machines, -.I 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 -.I hwclock -knows it's working with a brain-damaged clock, it ignores the year part of -the Hardware Clock value and instead tries to guess the year based on the -last calibrated date in the adjtime file, by assuming that that date is -within the past year. For this to work, you had better do a -.I hwclock \-\-set -or -.I hwclock \-\-systohc -at least once a year! - -Though -.I 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. - -.I hwclock -warns you that you probably need -.B \-\-badyear -whenever it finds your Hardware Clock set to 1994 or 1995. - -.TP -.B \-\-srm -.TP -.B \-\-arc -.TP -.B \-\-jensen -.TP -.B \-\-funky\-toy -These options all tell -.I hwclock -what kind of Alpha machine you have. They -are invalid if you don't have an Alpha and shouldn't be necessary if you -do, because -.I hwclock -should be able to determine by itself what it's -running on. These options make it possible for -.I hwclock -to work even when -its environment does not conform to its expectations and thus it cannot -accurately determine what sort of system it is running on. If you think -hwclock is incorrectly determining the system's characteristics, try -running with the -.B \-\-debug -option to see what conclusions the program is -reaching and how. If you find you need one of these options to make -.I hwclock -work, contact the -.I hwclock -maintainer to see if the program can be improved to detect your system -automatically. - -.B \-\-jensen -means you are running on a Jensen model. - -.B \-\-arc -means your machine is running with ARC console time. - -.B \-\-srm -means your machine is running with SRM console time. - -.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 -.B \-\-debug, -in learning about -.I hwclock. -.TP -.B \-\-debug -Display a lot of information about what -.I 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 -.I hwclock -because all of the other names are inappropriate to the point of being -misleading. -.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 /usr/local/timezone -directory, as explained in the man page for 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 -.I hwclock -sets the kernel timezone to the value indicated by TZ and/or -/usr/local/timezone when you set the System Time using the -.B \-\-hctosys -option. -.PP -A complication is that the timezone value actually consists of two -parts: 1) how far from the Standard Meridian the locality is -geographically, and 2) whether or not a Daylight Savings Time (DST) -convention is in effect in the locality at the present time. In -practice, the DST part of the timezone value is almost never used, so -if the geographical part were to be set to its correct value, the -users of the timezone value would actually compute the wrong local -time. -.PP -Therefore, -.I hwclock -violates the definition of the kernel's timezone value and always sets -the DST part to zero. If DST is supposed to be in effect, -.I hwclock -simply adds an hour to the geographical part. - -.SH How hwclock Accesses the Hardware Clock -.PP -.I 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). -.PP -On older systems, the method of accessing the Hardware Clock depends on -the system hardware. -.PP -On an ISA system, -.I 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 -.I 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, -.I hwclock -can access the clock via the console driver, via the device special -file /dev/tty1. -.PP -.I 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, -.I hwclock -will fall back to another method, if available. On an ISA or Alpha -machine, you can force -.I hwclock -to use the direct manipulation of the CMOS registers without even trying -.I /dev/rtc -by specifying the \-\-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. -.IR hwclock 's -"adjust" function lets you make systematic corrections to correct the -systematic drift. -.PP -It works like this: -.I hwclock -keeps a file, -.I /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. -.I 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. -.I 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. -.I 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 -.I hwclock \-\-adjust. -.I 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 -.I \-\-systohc -), -.I 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 -.I 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 -.I 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 -.IR clock ) -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 -.I hwclock -command line. -.PP -You can use an adjtime file that was previously used with the -.I clock -program with -.I 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. -.I 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 -.I 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. - - - -.SH "ENVIRONMENT VARIABLES" -.I TZ - -.SH FILES -.I /etc/adjtime -.I /usr/lib/zoneinfo/ -.I /dev/rtc -.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. - - diff --git a/clockB/hwclock.c b/clockB/hwclock.c deleted file mode 100644 index e7f1c919b4..0000000000 --- a/clockB/hwclock.c +++ /dev/null @@ -1,1744 +0,0 @@ -/************************************************************************** - hwclock -*************************************************************************** - - This is a program for reading and setting the Hardware Clock on an ISA - family computer. This is the clock that is also known as the RTC, - real time clock, or, unfortunately, the CMOS clock. - - See man page for details. - - By Bryan Henderson, 96.09.19. bryanh@giraffe-data.com - - Based on work by others; see history at end of source code. - -**************************************************************************/ -/************************************************************************** - 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. - - Definition of century: In this program, a century is a 100 year - period in which all the years' numbers in the Gregorian calendar - differ only in their last two decimal digits. E.g. 1900-1999 is - a century. The 20th Century (1901-2000), however, is not. - - - About the unusual situation of the Jensen variety of Alpha: - - Martin Ostermann writes: - - The problem with the Jensen is twofold: First, it has the clock at a - different address. Secondly, it has a distinction beween "local" and - normal bus addresses. The local ones pertain to the hardware integrated - into the chipset, like serial/parallel ports and of course, the RTC. - Those need to be addressed differently. This is handled fine in the kernel, - and it's not a problem, since this usually gets totally optimized by the - compile. But the i/o routines of (g)libc lack this support so far. - The result of this is, that the old clock program worked only on the - Jensen when USE_DEV_PORT was defined, but not with the normal inb/outb - functions. - - - - 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). - -****************************************************************************/ - -#include <string.h> -#include <stdio.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <errno.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/time.h> -#include <sys/stat.h> -#include "shhopt.h" -#include "../version.h" /* Defines UTIL_LINUX, among other things */ -#include "hwclock.h" - -#define FLOOR(arg) ((arg >= 0 ? (int) arg : ((int) arg) - 1)); - -/* Here the information for time adjustments is kept. */ -#define ADJPATH "/etc/adjtime" - -/* Note that we must define the boolean type as int because we use the - shhopt option processing library which, unfortunately, returns flag - options as integers. It is customary to define bool as char, but - then we would have to do a lot of conversion in order to interface - with shhopt. -*/ - -/* The following are times, in unix standard format (seconds since 1969) */ -#define START_OF_1994 757411200 -#define END_OF_1995 820396800 - -struct adjtime { - /* This is information we keep in the adjtime file that tells us how - to do drift corrections, among other things. 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; - float drift_factor; - time_t last_adj_time; - float not_adjusted; - 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) - */ - enum a_local_utc {LOCAL, UTC} local_utc; - /* To which time zone, local or UTC, we most recently set the - hardware clock. - */ -}; - - - - - -bool debug; - /* We are running in debug mode, wherein we put a lot of information about - what we're doing to standard output. Because of the pervasive and yet - background nature of this value, this is a global variable. */ - - - -/* We're going to assume that if the CPU is in the Intel x86 family, - this is an ISA family machine. For all practical purposes, this is - the case at the time of this writing, especially after we assume a - Linux kernel is running on it. - */ -const bool isa_machine = -#ifdef __i386__ -TRUE -#else -FALSE; -#endif -; - -const bool alpha_machine = -#ifdef __alpha__ -TRUE -#else -FALSE; -#endif -; - - - -static bool -hw_clock_is_utc(const bool utc, const bool local_opt, - const struct adjtime adjtime) { -/*---------------------------------------------------------------------------- - Return true iff the hardware clock keeps Coordinated Universal Time - rather than local time. - - 'utc' means the user told us in the invocation options that the - hardware clock is kept in UTC. ------------------------------------------------------------------------------*/ - - bool retval; /* our return value */ - - if (utc) retval = TRUE; - else if (local_opt) retval = FALSE; - else retval = (adjtime.local_utc == UTC); - if (debug) printf("Assuming hardware clock is kept in %s time.\n", - retval ? "UTC" : "LOCAL"); - return retval; -} - - - -static void -read_adjtime(struct adjtime *adjtime_p, int *rc_p) { -/*---------------------------------------------------------------------------- - Read the adjustment parameters and other persistent variables 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 *rc_p = 0 if all OK, !=0 otherwise. - - Note: The default is LOCAL rather than UTC for historical reasons. - ------------------------------------------------------------------------------*/ - FILE *adjfile; - int rc; /* local return code */ - struct stat statbuf; /* We don't even use the contents of this. */ - - rc = stat(ADJPATH, &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 = LOCAL; - - *rc_p = 0; - } else { - adjfile = fopen(ADJPATH, "r"); /* open file for reading */ - if (adjfile == NULL) { - const int fopen_errno = errno; - fprintf(stderr, MYNAME " is unable to open file " ADJPATH ". " - "fopen() errno=%d:%s", fopen_errno, strerror(fopen_errno)); - *rc_p = 2; - } else { - 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 */ - - line1[0] = '\0'; /* In case fgets fails */ - fgets(line1, sizeof(line1), adjfile); - line2[0] = '\0'; /* In case fgets fails */ - fgets(line2, sizeof(line2), adjfile); - line3[0] = '\0'; /* In case fgets fails */ - fgets(line3, sizeof(line3), adjfile); - - 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; - adjtime_p->local_utc = LOCAL; - - sscanf(line1, "%f %d %f", - &adjtime_p->drift_factor, - (int *) &adjtime_p->last_adj_time, - &adjtime_p->not_adjusted); - - sscanf(line2, "%d", (int *) &adjtime_p->last_calib_time); - - { - char local_utc_string[sizeof(line3)]; - - local_utc_string[0] = '\0'; /* In case nothing in line3 */ - sscanf(line3, "%s", local_utc_string); - - *rc_p = 0; /* Initial assumption - local/utc token is valid */ - if (strlen(local_utc_string) == 0) - adjtime_p->local_utc = LOCAL; - else if (strcmp(local_utc_string, "UTC") == 0) - adjtime_p->local_utc = UTC; - else if (strcmp(local_utc_string, "LOCAL") == 0) - adjtime_p->local_utc = LOCAL; - else { - fprintf(stderr, "%s: The first token of the third line of the file " - ADJPATH " is invalid. It must be LOCAL or UTC, indicating " - "to which time zone the hardware clock is set. Its " - "present value is '%s'.\n", MYNAME, local_utc_string); - *rc_p = 5; - } - } - } - adjtime_p->dirty = FALSE; - - if (debug) { - printf("Last drift adjustment done %s (Time %d)\n", - ctime2(adjtime_p->last_adj_time), - (int) adjtime_p->last_adj_time); - printf("Last calibration done %s (Time %d)\n", - ctime2(adjtime_p->last_calib_time), - (int) adjtime_p->last_calib_time); - } - } -} - - - -static void -synchronize_to_clock_tick(enum clock_access_method clock_access, - const int dev_port, const bool use_uf_bit, - int *retcode_p) { -/*----------------------------------------------------------------------------- - Wait until the moment the Hardware Clock updates to the next second, - so we know the exact time. - - The clock only has 1 second precision, so it gives the exact time only - once per second. - - Return *retcode_p == 0 if it worked, nonzero if it didn't. ------------------------------------------------------------------------------*/ - if (debug) printf("Waiting for clock tick...\n"); - - switch (clock_access) { - case ISA: synchronize_to_clock_tick_ISA(retcode_p, -1, use_uf_bit); break; - case DEV_PORT: synchronize_to_clock_tick_ISA(retcode_p, dev_port, - use_uf_bit); break; - case RTC_IOCTL: synchronize_to_clock_tick_RTC(retcode_p); break; - case KD: synchronize_to_clock_tick_KD(retcode_p); break; - default: - fprintf(stderr, "%s: Internal error in synchronize_to_clock_tick. " - "Invalid value for clock_access argument: %d.\n", - MYNAME, clock_access); - *retcode_p = 1; - } - if (debug) printf("...got clock tick\n"); - return; -} - - -static struct tm -make_within_one_year(const struct tm base_tm, const time_t last_known_time) { -/*---------------------------------------------------------------------------- - Compute a time that is the same as the input base_tm, except for a - different year. The year shall be whatever year it takes to make the - output time within one year after last_known_time. - - The timezone for both the input and output values is the value of - the TZ environment variable. ------------------------------------------------------------------------------*/ - struct tm broken_last_known_time; - /* The input time last_known_time, in broken down format */ - struct tm test_time; - - if (debug) - printf("Ignoring clock year and assuming " - "it's within 1 year after %s\n", - ctime2(last_known_time)); - - broken_last_known_time = *localtime(&last_known_time); - - test_time = base_tm; - test_time.tm_year = broken_last_known_time.tm_year; - - if (mktime(&test_time) < last_known_time) - test_time.tm_year += 1; - - return(test_time); -} - - - -static void -mktime_tz(struct tm hw_tm, const bool universal, const bool badyear, - const time_t last_known_time, - bool *valid_p, time_t *systime_p) { -/*----------------------------------------------------------------------------- - Convert a time in broken down format (hours, minutes, etc.) as read - from the Hardware Clock 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. - - Argument 'badyear' true means the input time is from one of those - machines with the Award BIOS that is incapable of storing a year - value less than 94 or 95, which means we can't use the year value - from the clock (see documentation of hwclock's --badyear option). - In this case, we instead determine the year by assuming that it's - less than a year since the time <last_known_time>. - - - If the argument contains values that do not constitute a valid time, - and mktime() recognizes this, return *valid_p == false and - *systime_p undefined. However, mktime() sometimes goes ahead and - computes a fictional time "as if" the input values were valid, - e.g. if they indicate the 31st day of April, mktime() may compute - the time of May 1. In such a case, we return the same fictional - value mktime() does as *systime_p and return *valid_p == true. - ------------------------------------------------------------------------------*/ - time_t mktime_result; /* The value returned by our mktime() call */ - struct tm adjusted_tm; - /* The same as the value from our argument, except if we determine - the year in the argument is garbage, this value contains the year - computed from the ADJTIME file instead. - */ - 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 = (char *) getenv("TZ"); /* remember original time zone */ - - if (universal) { - /* Set timezone to UTC */ - setenv("TZ", "UTC 0", TRUE); - /* Note: tzset() gets called implicitly by the time code, but only the - first time. When changing the environment variable, better call - tzset() explicitly. - - Also: documentation for tzset() says if TZ = "", that means UTC. - But practice shows that that only works if tzset() hasn't already - been called before. So we explicitly say "UTC 0". - */ - tzset(); - } - - if (badyear) - adjusted_tm = make_within_one_year(hw_tm, last_known_time); - else adjusted_tm = hw_tm; - - mktime_result = mktime(&adjusted_tm); - if (mktime_result == -1) { - /* This apparently (not specified in mktime() documentation) means - the 'adjusted_tm' structure does not contain valid values (however, not - containing valid values does _not_ imply mktime() returns -1). - */ - /* Note that we are assuming here that the invalidity came from the - hardware values and was not introduced by our adjustments! - */ - *valid_p = FALSE; - *systime_p = 0; - if (debug) - printf("Invalid values in hardware clock: " - "%2d/%.2d/%.2d %.2d:%.2d:%.2d\n", - hw_tm.tm_year, hw_tm.tm_mon+1, hw_tm.tm_mday, - hw_tm.tm_hour, hw_tm.tm_min, hw_tm.tm_sec - ); - } else { - *valid_p = TRUE; - *systime_p = mktime_result; - if (debug) - printf("Hw clock time : %s = %d seconds since 1969\n", - ctime2(*systime_p), (int) *systime_p); - } - /* now put back the original zone. */ - if (zone) setenv("TZ", zone, TRUE); - else unsetenv("TZ"); - tzset(); -} - - - -static void -read_hardware_clock(const enum clock_access_method method, - const int dev_port, - const bool universal, const int hc_zero_year, - const bool badyear, - const time_t last_known_time, - bool *valid_p, time_t *systime_p) { -/*---------------------------------------------------------------------------- - Read the hardware clock and return the current time via *systime_p - argument. - - If the hardware clock fails to tell us a time, return *valid_p == false - and undefined value as *systime_p. Otherwise *valid_p == true. - - Consider the hardware clock to be set in Coordinated Universal Time - (UTC) iff 'universal' == true. - - Consider the year value of the clock to be useless iff 'badyear' == true. - - Recognize that the present time is is after 'last_known_time', which - information may be necessary to interpret the value of some hardware - clocks. - - Use the method indicated by 'method' argument to access the hardware clock. ------------------------------------------------------------------------------*/ - struct tm tm; - - switch (method) { - case RTC_IOCTL: - read_hardware_clock_rtc_ioctl(&tm); - break; - case ISA: - read_hardware_clock_isa(&tm, -1, hc_zero_year); - break; - case DEV_PORT: - read_hardware_clock_isa(&tm, dev_port, hc_zero_year); - break; - case KD: - read_hardware_clock_kd(&tm); - break; - default: - fprintf(stderr, - "%s: Internal error: invalid value for clock access method.\n", - MYNAME); - exit(5); - } - if (debug) - printf ("Time read from Hardware Clock: Y=%d M=%d D=%d %02d:%02d:%02d\n", - tm.tm_year, tm.tm_mon+1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); - mktime_tz(tm, universal, badyear, last_known_time, valid_p, systime_p); -} - - - -static void -set_hardware_clock(const enum clock_access_method method, - const int dev_port, - const time_t newtime, - const bool universal, - const int hc_zero_year, const bool badyear, - const bool testing) { -/*---------------------------------------------------------------------------- - Set the Hardware Clock to the time 'newtime', in local time zone or UTC, - according to 'universal'. - - 'badyear' true means the clock is incapable of storing the proper - year value, so we instead store 95, 96, 97, or 98 so that it is at - least in the right place in the leap year cycle (and will remain so - for at least the next year). - - Use the method indicated by the 'method' argument. -----------------------------------------------------------------------------*/ - 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 the clock is incapable of storing the true year value, change - the year to a fictional stand-in year as described in the prolog. - */ - if (badyear) - new_broken_time.tm_year = 95 + ((new_broken_time.tm_year + 1) % 4); - - if (debug) - printf("Setting Hardware Clock to %.2d:%.2d:%.2d " - "= %d seconds since 1969\n", - new_broken_time.tm_hour, new_broken_time.tm_min, - new_broken_time.tm_sec, (int) newtime); - - switch (method) { - case RTC_IOCTL: - set_hardware_clock_rtc_ioctl(new_broken_time, testing); - break; - case ISA: - set_hardware_clock_isa(new_broken_time, hc_zero_year, -1, testing); - break; - case DEV_PORT: - set_hardware_clock_isa(new_broken_time, hc_zero_year, dev_port, testing); - break; - case KD: - set_hardware_clock_kd(new_broken_time, testing); - break; - default: - fprintf(stderr, - "%s: Internal error: invalid value for clock access method.\n", - MYNAME); - exit(5); - } -} - - - -static void -set_hardware_clock_exact(const time_t settime, - const struct timeval ref_time, - const enum clock_access_method clock_access, - const int dev_port, - const bool universal, - const int hc_zero_year, - const bool badyear, - const bool testing) { -/*---------------------------------------------------------------------------- - Set the Hardware Clock to the time 'settime', in local time zone or UTC, - according to 'universal'. - - But iff 'badyear', use a fictional year as appropriate for the --badyear - option. - - But correct 'settime' and wait for a fraction of a second so that - 'settime' is the value of the Hardware Clock as of system time - 'ref_time', which is in the past. For example, if 'settime' is - 14:03:05 and 'ref_time' is 12:10:04.5 and the current system - time is 12:10:06.0: Wait .5 seconds (to make exactly 2 seconds since - 'ref_time') and then set the Hardware Clock to 14:03:07, thus - getting a precise and retroactive setting of the clock. - - (Don't be confused by the fact that the system clock and the Hardware - Clock differ by two hours in the above example. That's just to remind - you that there are two independent time scales here). - - This function ought to be able to accept set times as fractional times. - Idea for future enhancement. - ------------------------------------------------------------------------------*/ - time_t newtime; /* Time to which we will set Hardware Clock */ - struct timeval now_time; /* locally used time */ - - gettimeofday(&now_time, NULL); - newtime = settime + (int) time_diff(now_time, ref_time) + 1; - if (debug) - printf("Time elapsed since reference time has been %.6f seconds.\n" - "Delaying further to reach the next full second.\n", - time_diff(now_time, ref_time)); - - /* Now delay some more until Hardware Clock time 'newtime' arrives */ - do gettimeofday(&now_time, NULL); - while (time_diff(now_time, ref_time) < newtime - settime); - - set_hardware_clock(clock_access, dev_port, newtime, - universal, hc_zero_year, badyear, testing); -} - - - -static void -display_time(const bool hclock_valid, const time_t systime, - const float sync_duration, const bool badyear_warn) { -/*---------------------------------------------------------------------------- - Put the time 'systime' on standard output in display format. - Except if hclock_valid == false, just tell standard output that we don't - know what time it is. - - Include in the output the adjustment 'sync_duration'. - - If the year is 1994 or 1995 and 'badyear_warn' is true, warn the - user that he has a brain-damaged clock and needs to use --badyear. - Since we didn't exist in 1994 and 1995, we know the clock isn't - correct. - ------------------------------------------------------------------------------*/ - if (!hclock_valid) - fprintf(stderr, "%s: The Hardware Clock registers contain values that are " - "either invalid (e.g. 50th day of month) or beyond the range " - "we can handle (e.g. Year 2095).\n", MYNAME); - else { - if (badyear_warn && (systime > START_OF_1994 && systime < END_OF_1995)) { - printf("WARNING: The Hardware Clock shows a time in 1994 " - "or 1995. This probably means you have a Hardware Clock " - "that is incapable of tracking years after 1999, and you " - "must use the --badyear option to make hwclock work for " - "you. See hwclock documentation for details.\n"); - } - - printf("%s %.6f seconds\n", ctime2(systime), -(sync_duration)); - } -} - - - -static int -interpret_date_string(const char *date_opt, time_t * const time_p) { -/*---------------------------------------------------------------------------- - Interpret the value of the --date option, which is something like - "13:05:01". In fact, it can be any of the myriad ASCII strings that specify - a time which the "date" program can understand. The date option value in - question is our "dateopt" argument. - - The specified time is in the local time zone. - - Our output, "*time_p", is a seconds-into-epoch time. - - We use the "date" program to interpret the date string. "date" must be - runnable by issuing the command "date" to the /bin/sh shell. That means - in must be in the current PATH. - - If anything goes wrong (and many things can), we return return code - 10 and arbitrary *time_p. Otherwise, return code is 0 and *time_p - is valid. -----------------------------------------------------------------------------*/ - FILE *date_child_fp; - char date_resp[100]; - const char magic[]="seconds-into-epoch="; - char date_command[100]; - int retcode; /* our eventual return code */ - int rc; /* local return code */ - - if (date_opt == NULL) { - fprintf(stderr, "%s: No --date option specified.\n", MYNAME); - retcode = 14; - } else if (strchr(date_opt, '"') != NULL) { - /* Quotation marks in date_opt would ruin the date command we construct. - */ - fprintf(stderr, "%s: The value of the --date option is not a valid date.\n" - "In particular, it contains quotation marks.\n", MYNAME); - retcode = 12; - } else { - 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) { - fprintf(stderr, "%s: Unable to run 'date' program in /bin/sh shell. " - "popen() failed with errno=%s (%d)\n", - MYNAME, strerror(errno), errno); - retcode = 10; - } else { - date_resp[0] = '\0'; /* in case fgets fails */ - fgets(date_resp, sizeof(date_resp), date_child_fp); - if (debug) printf("response from date command = %s\n", date_resp); - if (strncmp(date_resp, magic, sizeof(magic)-1) != 0) { - fprintf(stderr, "%s: The date command issued by " MYNAME " returned " - "unexpected results.\n" - "The command was:\n %s\nThe response was:\n %s\n", - MYNAME, date_command, date_resp); - retcode = 8; - } else { - int seconds_since_epoch; - rc = sscanf(date_resp + sizeof(magic)-1, "%d", &seconds_since_epoch); - if (rc < 1) { - fprintf(stderr, "%s: The date command issued by " MYNAME " returned" - "something other than an integer where the converted" - "time value was expected.\n" - "The command was:\n %s\nThe response was:\n %s\n", - MYNAME, date_command, date_resp); - retcode = 6; - } else { - retcode = 0; - *time_p = seconds_since_epoch; - if (debug) - printf("date string %s equates to %d seconds since 1969.\n", - date_opt, (int) *time_p); - } - } - fclose(date_child_fp); - } - } - return(retcode); -} - - - -static int -set_system_clock(const bool hclock_valid, const time_t newtime, - const bool testing) { -/*---------------------------------------------------------------------------- - Set the System Clock to time 'newtime'. - - Also set the kernel time zone value to the value indicated by the - TZ environment variable and/or /usr/lib/zoneinfo/, interpreted as - tzset() would interpret them. Except: do not consider Daylight - Savings Time to be a separate component of the time zone. Include - any effect of DST in the basic timezone value and set the kernel - DST value to 0. - - EXCEPT: if hclock_valid is false, just issue an error message - saying there is no valid time in the Hardware Clock to which to set - the system time. - - If 'testing' is true, don't actually update anything -- just say we - would have. ------------------------------------------------------------------------------*/ - int retcode; /* our eventual return code */ - - if (!hclock_valid) { - fprintf(stderr, "%s: The Hardware Clock does not contain a valid time, so " - "we cannot set the System Time from it.\n", MYNAME); - retcode = 1; - } else { - struct timeval tv; - int rc; /* local return code */ - - tv.tv_sec = newtime; - tv.tv_usec = 0; - - tzset(); /* init timezone, daylight from TZ or ...zoneinfo/localtime */ - /* An undocumented function of tzset() is to set global variabales - 'timezone' and 'daylight' - */ - - if (debug) { - printf( "Calling settimeofday:\n" ); - /* Note: tv_sec and tv_usec are declared variously on different - systems: int, long, time_t. Casting to long below makes it - compile everywhere. - */ - printf( "\ttv.tv_sec = %ld, tv.tv_usec = %ld\n", - (long) tv.tv_sec, (long) tv.tv_usec ); - } - if (testing) { - printf("Not setting system clock because running in test mode.\n"); - retcode = 0; - } else { - /* For documentation of settimeofday() see, in addition to its man page, - kernel/time.c in the Linux source code. - */ - const struct timezone tz = { timezone/60 - 60*daylight, 0 }; - /* put daylight in minuteswest rather than dsttime, - since the latter is mostly ignored ... */ - rc = settimeofday(&tv, &tz); - if (rc != 0) { - if (errno == EPERM) - fprintf(stderr, "%s: Must be superuser to set system clock.\n", - MYNAME); - else - fprintf(stderr, - "%s: settimeofday() failed, errno=%s (%d)\n", - MYNAME, strerror(errno), errno); - retcode = 1; - } else retcode = 0; - } - } - return(retcode); -} - - -static void -adjust_drift_factor(struct adjtime *adjtime_p, - const time_t actual_time, - const bool hclock_valid, - const struct timeval hclocktime ) { -/*--------------------------------------------------------------------------- - Update the drift factor and calibration parameters in '*adjtime_p' - to reflect the fact that at some recent instant when the actual time - was 'actual_time', the Hardware Clock said the time was - 'hclocktime', and that we have corrected the Hardware Clock - accordingly. Note that 'hclocktime' is a fractional time, taking - into consideration the Hardware Clock register contents and how long - those contents had been that. - - We assume that the only cause of error in the Hardware Clock is - systematic drift and that the user has been doing regular drift - adjustments using the drift factor in the adjtime file. Therefore, - if 'actual_time' and 'hclocktime' are different, that means the drift - factor isn't quite right. - - 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. - - Also, don't adjust if the error is more than 30 minutes, because that - kind of error probably isn't drift. - -----------------------------------------------------------------------------*/ - 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 there is no \n" - "previous calibration information (i.e. adjtime file is \n" - "nonexistent or has 0 in last calibration time field).\n"); - } else if (time_diff(hclocktime, t2tv(adjtime_p->last_calib_time)) - < 23.0 * 60.0 * 60.0) { - if (debug) - printf("Not adjusting drift factor because it has been less than a " - "day since the last calibration.\n"); - } else { - const float sec_per_day = 24.0 * 60.0 * 60.0; - float atime_per_htime; /* adjusted time units per hardware time unit */ - float adj_days; /* days since last adjustment (in hardware clock time) */ - float cal_days; /* days since last calibration (in hardware clock time) */ - float exp_drift; /* expected drift (sec) since last adjustment */ - float unc_drift; /* uncorrected drift (sec) since last calibration */ - float factor_adjust; /* amount to add to previous drift factor */ - atime_per_htime = 1.0 + adjtime_p->drift_factor / sec_per_day; - adj_days = time_diff(hclocktime, t2tv(adjtime_p->last_adj_time)) - / sec_per_day; - exp_drift = adj_days * adjtime_p->drift_factor + adjtime_p->not_adjusted; - unc_drift = time_diff(t2tv(actual_time), hclocktime) - exp_drift; - cal_days = ((float)(adjtime_p->last_adj_time - adjtime_p->last_calib_time) - + adjtime_p->not_adjusted) / (sec_per_day * atime_per_htime) - + adj_days; - factor_adjust = unc_drift / cal_days; - - if (unc_drift > 30*60.0) { - if (debug) - printf("Not adjusting drift factor because we calculated the \n" - "uncorrected drift as %.0f seconds, which is so large that \n" - "it probably is not drift at all, but rather some \n" - "clock setting anomaly.\n\n", unc_drift); - } else { - 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) (actual_time - adjtime_p->last_calib_time), - adjtime_p->drift_factor, - factor_adjust ); - - adjtime_p->drift_factor += factor_adjust; - } - } - adjtime_p->last_calib_time = actual_time; - - adjtime_p->last_adj_time = actual_time; - - adjtime_p->not_adjusted = 0; - - adjtime_p->dirty = TRUE; -} - - - -static void -calculate_adjustment( - const float factor, - const time_t last_time, - const float not_adjusted, - const time_t systime, - int *adjustment_p, - float *retro_p, - const int debug ) { -/*---------------------------------------------------------------------------- - 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. -----------------------------------------------------------------------------*/ - float exact_adjustment; - - exact_adjustment = ((float) (systime - last_time)) * factor / (24 * 60 * 60) - + not_adjusted; - *adjustment_p = FLOOR(exact_adjustment); - - *retro_p = exact_adjustment - (float) *adjustment_p; - if (debug) { - printf ("Time since last adjustment is %d seconds\n", - (int) (systime - last_time)); - printf ("Need to insert %d seconds and refer time back " - "%.6f seconds ago\n", - *adjustment_p, *retro_p); - } -} - - - -static void -save_adjtime(const struct adjtime adjtime, const bool testing) { -/*----------------------------------------------------------------------------- - Write the contents of the <adjtime> structure to its disk file. - - But if the contents are clean (unchanged since read from disk), don't - bother. ------------------------------------------------------------------------------*/ - char newfile[506]; /* Stuff to write to disk file */ - /* snprintf is not always available, but this is safe - as long as libc does not use more than 100 positions for %ld or %f - */ - - int rc; /* locally used: return code from a function */ - - if (adjtime.dirty) { - /* We'd use snprintf here, but apparently, it isn't always available. */ - 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", - ADJPATH, newfile); - } else { - FILE *adjfile; - - adjfile = fopen(ADJPATH, "w"); - if (adjfile == NULL) { - const int fopen_errno = errno; - printf("Could not open file with the clock adjustment parameters " - "in it (%s) for output.\n" - "fopen() returned errno %d: %s.\n" - "Drift adjustment parameters not updated.\n", - ADJPATH, fopen_errno, strerror(errno)); - } else { - rc = fprintf(adjfile, newfile); - if (rc < 0) { - const int fprintf_errno = errno; - printf("Could not update file (%s) " - "with the clock adjustment parameters in it.\n" - "fprintf() returned errno %d: %s.\n" - "Drift adjustment parameters not updated.\n", - ADJPATH, fprintf_errno, strerror(errno)); - } - rc = fclose(adjfile); - if (rc < 0) { - const int fclose_errno = errno; - printf("Could not update file (%s) " - "with the clock adjustment parameters in it.\n" - "fclose() returned errno %d: %s.\n" - "Drift adjustment parameters not updated.\n", - ADJPATH, fclose_errno, strerror(errno)); - } - } - } - } else if (debug) - printf("Skipping update of adjtime file because nothing has changed.\n"); -} - - - -static void -do_adjustment(struct adjtime *adjtime_p, - const bool hclock_valid, const time_t hclocktime, - const struct timeval read_time, - const enum clock_access_method clock_access, - const int dev_port, const bool universal, - const int hc_zero_year, - const bool badyear, const bool testing) { -/*--------------------------------------------------------------------------- - Do the adjustment requested, by 1) setting the Hardware Clock (if - necessary), and 2) updating the last-adjusted time in the adjtime - structure. - - Do not update anything if the Hardware Clock does not currently present - a valid time. - - arguments 'factor' and 'last_time' are current values from the adjtime - file. - - 'hclock_valid' means the Hardware Clock contains a valid time, and that - time is 'hclocktime'. - - 'read_time' is the current system time (to be precise, it is the system - time at the time 'hclocktime' was read, which due to computational delay - could be a short time ago). - - 'universal': the Hardware Clock is kept in UTC. - - 'badyear': the Hardware Clock is incapable of storing years outside - the range 1994-1999. - - 'testing': We are running in test mode (no updating of clock). - - We do not bother to update the clock if the adjustment would be less than - one second. This is to avoid cumulative error and needless CPU hogging - (remember we use an infinite loop for some timing) if the user runs us - frequently. - -----------------------------------------------------------------------------*/ - if (!hclock_valid) { - fprintf(stderr, "%s: The Hardware Clock does not contain a valid time, " - "so we cannot adjust it.\n", MYNAME); - /* Any previous calibration had to be before the clock got hosed, so - wipe out the record of it so it won't be used in the future. - */ - adjtime_p->last_calib_time = 0; - 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 adjusting clock because we have no information about \n" - "the previous calibration (i.e. the adjtime file is \n" - "nonexistent or contains zero in the last calibrated time \n" - "field).\n"); - } else { - int adjustment; - /* Number of seconds we must insert in the Hardware Clock */ - float 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, - debug ); - if (adjustment > 0 || adjustment < -1) { - set_hardware_clock_exact(hclocktime + adjustment, - time_inc(read_time, -retro), - clock_access, dev_port, universal, - hc_zero_year, badyear, 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, - const bool user_says_jensen, - enum clock_access_method *clock_access_p) { -/*---------------------------------------------------------------------------- - Figure out how we're going to access the hardware clock, by seeing - what facilities are available, looking at invocation options, and - using compile-time constants. - - 'user_requests_ISA' means the user explicitly asked for the ISA method, - so we'll use that (even if we know it will fail because the machine - is incapable!). ------------------------------------------------------------------------------*/ - const bool jensen = - user_says_jensen || - (alpha_machine && is_in_cpuinfo("system type", "Jensen")); - /* See comments at top of program for how Jensen is a special case. */ - bool rtc_works; - /* The /dev/rtc method is available and seems to work on this machine */ - bool kdghwclk_works; - /* The KDHWCLK method is available and seems to work on this machine. */ - - see_if_rtc_works(&rtc_works); /* May issue error messages */ - see_if_kdghwclk_works(&kdghwclk_works); /* May issue error messages */ - - if (user_requests_ISA) *clock_access_p = ISA; - else if (rtc_works) *clock_access_p = RTC_IOCTL; - else if (kdghwclk_works) *clock_access_p = KD; - else if (got_kdghwclk) *clock_access_p = ISA; - /* I don't know on what machine the above line makes any sense, but the - code has always been this way. -BJH 99.03.31 - */ - else if (isa_machine) *clock_access_p = ISA; - else if (jensen) *clock_access_p = DEV_PORT; - else if (alpha_machine) *clock_access_p = ISA; - else *clock_access_p = NOCLOCK; - if (debug) { - switch (*clock_access_p) { - case ISA: printf("Using direct I/O instructions to ISA clock.\n"); break; - case KD: printf("Using KDGHWCLK interface to m68k clock.\n"); break; - case RTC_IOCTL: printf("Using /dev/rtc interface to clock.\n"); break; - case DEV_PORT: printf("Using /dev/port interface to clock.\n"); break; - case NOCLOCK: printf("Unable to find a usable clock access method.\n"); - break; - default: - printf("determine_clock_access_method() returned invalid value: %d.\n", - *clock_access_p); - } - } -} - - - -static void -do_systohc(const enum clock_access_method clock_access, - const int dev_port, - const time_t hclocktime, const bool hclock_valid, - const struct timeval read_time, - const bool universal, const int hc_zero_year, - const bool badyear, const bool testing, - struct adjtime * const adjtime_p) { -/*---------------------------------------------------------------------------- - Perform the specifics of the hwclock --systohc function. ------------------------------------------------------------------------------*/ - 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, - clock_access, dev_port, universal, - hc_zero_year, badyear, testing); - adjust_drift_factor(adjtime_p, (time_t) reftime.tv_sec, hclock_valid, - time_inc(t2tv(hclocktime), - - time_diff(read_time, reftime) - ) - ); -} - - -static void -manipulate_clock(const bool show, const bool adjust, - const bool set, const time_t set_time, - const bool hctosys, const bool systohc, - const struct timeval startup_time, - const enum clock_access_method clock_access, - const bool utc, const bool local_opt, - const bool badyear, const bool arc_opt, const bool srm_opt, - const bool user_wants_uf, - const bool testing, - int *retcode_p - ) { -/*--------------------------------------------------------------------------- - Do all the normal work of hwclock - read, set clock, etc. - - Issue output to stdout and error message to stderr where appropriate. - - Return rc == 0 if everything went OK, rc != 0 if not. -----------------------------------------------------------------------------*/ - struct adjtime adjtime; - /* Contents of the adjtime file, or what they should be. */ - int rc; /* local return code */ - bool no_auth; /* User lacks necessary authorization to access the clock */ - int dev_port; - /* File descriptor for /dev/port, if we're using it. -1 if we - couldn't open it. 0 if we aren't using it. - */ - get_inb_outb_privilege(clock_access, &no_auth); - - if (no_auth) *retcode_p = 1; - else { - get_dev_port_access(clock_access, &dev_port); - - if (dev_port < 0) *retcode_p = 3; - else { - read_adjtime(&adjtime, &rc); - if (rc != 0) *retcode_p = 2; - else { - const bool use_uf_bit = uf_bit_needed(user_wants_uf); - const int hc_zero_year = zero_year(arc_opt, srm_opt); - /* year of century to which a value of zero corresponds in the - Hardware Clock's year register. - */ - const bool universal = hw_clock_is_utc(utc, local_opt, adjtime); - /* The hardware clock is kept in Coordinated Universal Time. */ - - if ((set || systohc || adjust) && - (adjtime.local_utc == UTC) != universal) { - adjtime.local_utc = universal ? UTC : LOCAL; - adjtime.dirty = TRUE; - } - - synchronize_to_clock_tick(clock_access, dev_port, use_uf_bit, - retcode_p); - /* this takes up to 1 second */ - if (*retcode_p == 0) { - struct timeval read_time; - /* The time at which we read the Hardware Clock */ - - bool hclock_valid; - /* The Hardware Clock gives us a valid time, or at least something - close enough to fool mktime(). - */ - - time_t hclocktime; - /* 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. - */ - - gettimeofday(&read_time, NULL); - read_hardware_clock(clock_access, dev_port, universal, - hc_zero_year, badyear, - adjtime.last_calib_time, - &hclock_valid, &hclocktime); - - if (show) { - display_time(hclock_valid, hclocktime, - time_diff(read_time, startup_time), !badyear); - *retcode_p = 0; - } else if (set) { - set_hardware_clock_exact(set_time, startup_time, - clock_access, dev_port, universal, - hc_zero_year, - badyear, testing); - adjust_drift_factor(&adjtime, set_time, hclock_valid, - time_inc(t2tv(hclocktime), - - time_diff(read_time, startup_time) - ) - ); - *retcode_p = 0; - } else if (adjust) { - do_adjustment(&adjtime, hclock_valid, hclocktime, - read_time, clock_access, dev_port, - universal, hc_zero_year, - badyear, testing); - *retcode_p = 0; - } else if (systohc) { - do_systohc(clock_access, dev_port, - hclocktime, hclock_valid, read_time, - universal, hc_zero_year, badyear, testing, - &adjtime); - *retcode_p = 0; - } else if (hctosys) { - rc = set_system_clock(hclock_valid, hclocktime, testing); - if (rc != 0) { - printf("Unable to set system clock.\n"); - *retcode_p = 1; - } else *retcode_p = 0; - } - save_adjtime(adjtime, testing); - } - } - } - if (clock_access == DEV_PORT && dev_port >= 0) close(dev_port); - } -} - - - -static void -report_version(void) { - - char *additional_version; /* malloc'ed */ - /* Stuff to add on to the version report, after the basic version. - If this is hwclock packaged with util-linux, this is the - util-linux version. Otherwise, it's nothing. - */ - -#ifdef UTIL_LINUX - additional_version = malloc(strlen(util_linux_version) + 5); - sprintf(additional_version, "/%s", util_linux_version); -#else - additional_version = strdup(""); -#endif - printf(MYNAME " " VERSION "%s\n", additional_version); - free(additional_version); -} - - - -static void -manipulate_epoch(const bool getepoch, const bool setepoch, - const int epoch_opt, const bool testing) { -/*---------------------------------------------------------------------------- - Get or set the Hardware Clock epoch value in the kernel, as appropriate. - 'getepoch', 'setepoch', and 'epoch' are hwclock invocation options. - - 'epoch' == -1 if the user did not specify an "epoch" option. - ------------------------------------------------------------------------------*/ - /* - Maintenance note: This should work on non-Alpha machines, but the - evidence today (98.03.04) indicates that the kernel only keeps the - epoch value on Alphas. If that is ever fixed, this function should be - changed. - */ - - if (!alpha_machine) - fprintf(stderr, - "%s: The kernel keeps an epoch value for the Hardware Clock " - "only on an Alpha machine.\nThis copy of hwclock was built for " - "a machine other than Alpha\n(and thus is presumably not running " - "on an Alpha now). No action taken.\n", MYNAME); - else { - if (getepoch) { - unsigned long epoch; - char *reason; /* malloc'ed */ - - get_epoch(&epoch, &reason); - if (reason != NULL) { - printf("Unable to get the epoch value from the kernel. %s\n", - reason); - free(reason); - } else - printf("Kernel is assuming an epoch value of %lu\n", epoch); - } else if (setepoch) { - if (epoch_opt == -1) - fprintf(stderr, "%s: To set the epoch value, you must use the 'epoch' " - "option to tell to what value to set it.\n", MYNAME); - else { - int rc; - set_epoch(epoch_opt, testing, &rc); - if (rc != 0) - printf("Unable to set the epoch value in the kernel.\n"); - } - } - } -} - - - -int -main(int argc, char **argv, char **envp) { -/*---------------------------------------------------------------------------- - MAIN ------------------------------------------------------------------------------*/ - struct timeval startup_time; - /* The time we started up, in seconds into the epoch, including fractions. - */ - time_t set_time; /* Time to which user said to set Hardware Clock */ - - enum clock_access_method clock_access; - /* The method that we determine is best for accessing Hardware Clock - on this system. - */ - - bool permitted; /* User is permitted to do the function */ - int retcode; /* Our eventual return code */ - - int rc; /* local return code */ - - /* option_def is the control table for the option parser. These other - variables are the results of parsing the options and their meanings - are given by the option_def. The only exception is <show>, which - may be modified after parsing is complete to effect an implied option. - */ - bool show, set, systohc, hctosys, adjust, getepoch, setepoch, version; - bool utc, local_opt, badyear, testing, directisa; - bool arc_opt, jensen_opt, srm_opt, funky_opt; - char *date_opt; - int epoch_opt; - - const optStruct option_def[] = { - { 'r', (char *) "show", OPT_FLAG, &show, 0 }, - { 0, (char *) "set", OPT_FLAG, &set, 0 }, - { 'w', (char *) "systohc", OPT_FLAG, &systohc, 0 }, - { 's', (char *) "hctosys", OPT_FLAG, &hctosys, 0 }, - { 0, (char *) "getepoch", OPT_FLAG, &getepoch, 0 }, - { 0, (char *) "setepoch", OPT_FLAG, &setepoch, 0 }, - { 'a', (char *) "adjust", OPT_FLAG, &adjust, 0 }, - { 'v', (char *) "version", OPT_FLAG, &version, 0 }, - { 0, (char *) "date", OPT_STRING, &date_opt, 0 }, - { 0, (char *) "epoch", OPT_UINT, &epoch_opt, 0 }, - { 'u', (char *) "utc", OPT_FLAG, &utc, 0 }, - { 0, (char *) "localtime", OPT_FLAG, &local_opt, 0 }, - { 0, (char *) "badyear", OPT_FLAG, &badyear, 0 }, - { 0, (char *) "directisa", OPT_FLAG, &directisa, 0 }, - { 0, (char *) "test", OPT_FLAG, &testing, 0 }, - { 'D', (char *) "debug", OPT_FLAG, &debug, 0 }, - { 'A', (char *) "arc", OPT_FLAG, &arc_opt, 0 }, - { 'J', (char *) "jensen", OPT_FLAG, &jensen_opt,0 }, - { 'S', (char *) "srm", OPT_FLAG, &srm_opt, 0 }, - { 'F', (char *) "funky-toy", OPT_FLAG, &funky_opt, 0 }, - { 0, (char *) NULL, OPT_END, NULL, 0 } - }; - int argc_parse; /* argc, except we modify it as we parse */ - char **argv_parse; /* argv, except we modify it as we parse */ - - assume_interrupts_enabled(); /* Since we haven't messed with them yet */ - - gettimeofday(&startup_time, NULL); /* Remember what time we were invoked */ - - /* set option defaults */ - show = set = systohc = hctosys = adjust = getepoch = setepoch = - version = utc = local_opt = badyear = - directisa = testing = debug = - jensen_opt = arc_opt = srm_opt = funky_opt = FALSE; - date_opt = NULL; - epoch_opt = -1; - - argc_parse = argc; argv_parse = argv; - optParseOptions(&argc_parse, argv_parse, option_def, 0); - /* Uses and sets argc_parse, argv_parse. - Sets show, set, systohc, hctosys, adjust, getepoch, setepoch, - version, utc, localtime, badyear, - directisa, testing, debug, - date_opt, epoch_opt, - jensen_opt, arc_opt, srm_opt, funky_opt - */ - - if (argc_parse - 1 > 0) { - fprintf(stderr, MYNAME " takes no non-option arguments. " - "You supplied %d. See man page for complete syntax.\n", - argc_parse - 1); - exit(100); - } - - if (show + set + systohc + hctosys + adjust + - getepoch + setepoch + version > 1) { - fprintf(stderr, - "You have specified multiple function options to hwclock.\n" - "You can only perform one function at a time.\n"); - exit(100); - } - - if (set) { - rc = interpret_date_string(date_opt, &set_time); /* (time-consuming) */ - if (rc != 0) { - fprintf(stderr, "%s: No usable set-to time given. Cannot set clock.\n", - MYNAME); - exit(100); - } - } - - if (jensen_opt && !alpha_machine) { - fprintf(stderr, "%s: Your options indicate that this is a Jensen model of " - "DEC Alpha, but this is not an Alpha machine!\n", MYNAME); - exit(100); - } - - if (srm_opt && alpha_machine) { - fprintf(stderr, "%s: Your options indicate that this machine keeps SRM " - "console time, but only DEC Alphas have such a clock and this is " - "not an Alpha!\n", MYNAME); - exit(100); - } - if (arc_opt && alpha_machine) { - fprintf(stderr, "%s: Your options indicate that this machine's clock" - "keeps ARC console time, " - "but only DEC Alphas have such a clock and this is " - "not an Alpha!\n", MYNAME); - exit(100); - } - - if (directisa && !(isa_machine || alpha_machine)) { - fprintf(stderr, "%s: You have requested direct access to the ISA Hardware " - "Clock using machine instructions from the user process. " - "But this method only works on an ISA machine with an x86 " - "CPU, or a similar machine such as DEC Alpha. " - "This is not one.\n", MYNAME); - exit(100); - } - - if (utc && local_opt) { - fprintf(stderr, "%s: The --utc and --localtime options are mutually " - "exclusive. You specified both.\n", MYNAME); - exit(100); - } - - - if (!(show | set | systohc | hctosys | adjust | getepoch | setepoch | - version)) - show = TRUE; /* default to show */ - - - if (getuid() == 0) permitted = TRUE; - else { - /* program is designed to run setuid (in some situations) -- be secure! */ - if (set || hctosys || systohc || adjust) { - fprintf(stderr, - "%s: Sorry, only the superuser can change the " - "Hardware Clock.\n", MYNAME); - permitted = FALSE; - } else if (setepoch) { - fprintf(stderr, - "%s: Sorry, only the superuser can change " - "the Hardware Clock epoch in the kernel.\n", MYNAME); - permitted = FALSE; - } else permitted = TRUE; - } - - if (!permitted) retcode = 2; - else { - retcode = 0; - if (version) { - report_version(); - } else if (getepoch || setepoch) { - manipulate_epoch(getepoch, setepoch, epoch_opt, testing); - } else { - determine_clock_access_method(directisa, jensen_opt, &clock_access); - if (clock_access == NOCLOCK) - fprintf(stderr, "%s: Cannot access the Hardware Clock via any known " - "method. Use --debug option to see the details of our " - "search for an access method.\n", MYNAME); - else - manipulate_clock(show, adjust, set, set_time, hctosys, systohc, - startup_time, clock_access, utc, local_opt, badyear, - arc_opt, srm_opt, funky_opt, testing, &rc); - } - } - exit(retcode); -} - - -/**************************************************************************** - - History of this program: - - 99.04.08 BJH Version 2.5 - - Make it work on Alphas without /dev/rtc. Thanks to David Mosberger - <davidm@azstarnet.com>, Jay Estabrook <jestabro@amt.tay1.dec.com>, - Martin Ostermann <ost@coments.rwth-aachen.de>, Andries Brouwer - <aeb@cwi.nl>. Most of this code is lifted from another program - called "clock" (not the original ancestor of hwclock) that has - circulated for use on Alpha. - - Make it work on Sparc. - - Add --badyear option. Thanks to David J Coffin (dcoffin@shore.net) - for the design of this. - - Add --localtime option, local/UTC value in adjtime file, and defaults - for local/utc. - - Don't set CMOS memory Byte 50 (century byte). On some machines, - that byte not only isn't used as a century byte, but it is used for - something else. - - Don't update the drift factor if the variation is so huge that it - probably wasn't due to drift. - - Compute drift factor with better precision. - - 98.08.12 BJH Version 2.4 - - Don't use century byte from Hardware Clock. Add comments telling why. - - - 98.06.20 BJH Version 2.3. - - Make --hctosys set the kernel timezone from TZ environment variable - and/or /usr/lib/zoneinfo. From Klaus Ripke (klaus@ripke.com). - - 98.03.05 BJH. Version 2.2. - - Add --getepoch and --setepoch. - - Fix some word length things so it works on Alpha. - - Make it work when /dev/rtc doesn't have the interrupt functions. - In this case, busywait for the top of a second instead of blocking and - waiting for the update complete interrupt. - - Fix a bunch of bugs too numerous to mention. - - 97.06.01: BJH. Version 2.1. Read and write the century byte (Byte - 50) of the ISA Hardware Clock when using direct ISA I/O. Problem - discovered by job (jei@iclnl.icl.nl). - - Use the rtc clock access method in preference to the KDGHWCLK method. - Problem discovered by Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>. - - November 1996: Version 2.0.1. Modifications by Nicolai Langfeldt - (janl@math.uio.no) to make it compile on linux 1.2 machines as well - as more recent versions of the kernel. Introduced the NO_CLOCK - access method and wrote feature test code to detect absense of rtc - headers. - - - Bryan Henderson based hwclock on the program "clock", in September - 1996. While remaining mostly backward compatible with clock, - hwclock added the following: - - - You can set the hardware clock without also modifying the Linux - 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. (This - is the drift rate that is used with the --adjust function to - automatically adjust the clock periodically to compensate for drift). - - - More mnemonic GNU-style command line options. - - - Comments describing how the clock and program work to improve - maintainability. - - - Removed the old dead I/O code that worked without the inb/outb - instructions and without the asm/io.h definitions. - - The first version of hwclock was Version 2. - - Here is the history section from the "clock" program at the time it was - used as a basis for hwclock: - - V1.0 - - - V1.0 by Charles Hedrick, hedrick@cs.rutgers.edu, April 1992. - - ******************** - V1.1 - Modified for clock adjustments - Rob Hooft, hooft@chem.ruu.nl, Nov 1992 - Also moved error messages to stderr. The program now uses getopt. - Changed some exit codes. Made 'gcc 2.3 -Wall' happy. - - ***** - V1.2 - - Applied patches by Harald Koenig (koenig@nova.tat.physik.uni-tuebingen.de) - Patched and indented by Rob Hooft (hooft@EMBL-Heidelberg.DE) - - A free quote from a MAIL-message (with spelling corrections): - - "I found the explanation and solution for the CMOS reading 0xff problem - in the 0.99pl13c (ALPHA) kernel: the RTC goes offline for a small amount - of time for updating. Solution is included in the kernel source - (linux/kernel/time.c)." - - "I modified clock.c to fix this problem and added an option (now default, - look for USE_INLINE_ASM_IO) that I/O instructions are used as inline - code and not via /dev/port (still possible via #undef ...)." - - With the new code, which is partially taken from the kernel sources, - the CMOS clock handling looks much more "official". - Thanks Harald (and Torsten for the kernel code)! - - ***** - V1.3 - Canges from alan@spri.levels.unisa.edu.au (Alan Modra): - a) Fix a few typos in comments and remove reference to making - clock -u a cron job. The kernel adjusts cmos time every 11 - minutes - see kernel/sched.c and kernel/time.c set_rtc_mmss(). - This means we should really have a cron job updating - /etc/adjtime every 11 mins (set last_time to the current time - and not_adjusted to ???). - b) Swapped arguments of outb() to agree with asm/io.h macro of the - same name. Use outb() from asm/io.h as it's slightly better. - c) Changed CMOS_READ and CMOS_WRITE to inline functions. Inserted - cli()..sti() pairs in appropriate places to prevent possible - errors, and changed ioperm() call to iopl() to allow cli. - d) Moved some variables around to localise them a bit. - e) Fixed bug with clock -ua or clock -us that cleared environment - variable TZ. This fix also cured the annoying display of bogus - day of week on a number of machines. (Use mktime(), ctime() - rather than asctime() ) - f) Use settimeofday() rather than stime(). This one is important - as it sets the kernel's timezone offset, which is returned by - gettimeofday(), and used for display of MSDOS and OS2 file - times. - g) faith@cs.unc.edu added -D flag for debugging - - V1.4: alan@SPRI.Levels.UniSA.Edu.Au (Alan Modra) - Wed Feb 8 12:29:08 1995, fix for years > 2000. - faith@cs.unc.edu added -v option to print version. */ - - diff --git a/clockB/hwclock.h b/clockB/hwclock.h deleted file mode 100644 index 6a3ab14bd0..0000000000 --- a/clockB/hwclock.h +++ /dev/null @@ -1,139 +0,0 @@ -#include <time.h> -#include <sys/time.h> -#include <unistd.h> - -typedef int bool; -#define TRUE 1 -#define FALSE 0 - -#define MYNAME "hwclock" -#define VERSION "2.5" - - -enum clock_access_method {ISA, RTC_IOCTL, KD, DEV_PORT, NOCLOCK}; - /* A method for accessing (reading, writing) the hardware clock: - - ISA: - via direct CPU I/O instructions that work on an ISA family - machine (IBM PC compatible) or most DEC Alphas, which implement - enough of ISA to get by. - - RTC_IOCTL: - via the rtc device driver, using device special file /dev/rtc. - - KD: - via the console driver, using device special file /dev/tty1. - This is the m68k ioctl interface, known as KDGHWCLK. - - DEV_PORT: - via the /dev/port device, which is almost the same thing as - direct I/O instructions via the ISA method, but works on a Jensen - model of Alpha, whereas ISA does not. Also, use I/O addresses - 0x170 and 0x171 instead of the ISA 0x70 and 0x71. - - NO_CLOCK: - Unable to determine a usable access method for the system clock. - */ - - - - -/* hwclock.c */ -extern int debug; -extern const bool alpha_machine; -extern const bool isa_machine; - -/* directio.c */ - -extern void -assume_interrupts_enabled(void); - -extern void -synchronize_to_clock_tick_ISA(int *retcode_p, const int dev_port, - const bool use_uf_bit); - -extern void -read_hardware_clock_isa(struct tm *tm, const int dev_port, - int hc_zero_year); - -extern void -set_hardware_clock_isa(const struct tm new_tm, - const int hc_zero_year, - const int dev_port, - const bool testing); - -extern void -get_inb_outb_privilege(const enum clock_access_method clock_access, - bool * const no_auth_p); - -extern void -get_dev_port_access(const enum clock_access_method clock_access, - int * dev_port_p); - -extern bool -uf_bit_needed(const bool user_wants_uf); - -extern int -zero_year(const bool arc_opt, const bool srm_opt); - - -/* rtc.c */ -extern void -synchronize_to_clock_tick_RTC(int *retcode_p); - -extern void -read_hardware_clock_rtc_ioctl(struct tm *tm); - -extern void -set_hardware_clock_rtc_ioctl(const struct tm new_broken_time, - const bool testing); -extern void -see_if_rtc_works(bool * const rtc_works_p); - -extern void -get_epoch(unsigned long *epoch_p, char **reason_p); - -extern void -set_epoch(unsigned long epoch, const bool testing, int *retcode_p); - - -/* kd.c */ - -extern void -synchronize_to_clock_tick_KD(int *retcode_p); - -extern void -read_hardware_clock_kd(struct tm *tm); - -extern void -set_hardware_clock_kd(const struct tm new_broken_time, - const bool testing); - -extern void -see_if_kdghwclk_works(bool * const kdghwclk_works_p); - -extern const bool got_kdghwclk; - -/* util.c */ -extern bool -is_in_cpuinfo(const char * const fmt, const char * const str); - -extern char * -ctime2(const time_t time); - -extern struct timeval -t2tv(time_t argument); - -extern struct timeval -t2tv(time_t argument); - -extern float -time_diff(struct timeval subtrahend, struct timeval subtractor); - -extern struct timeval -time_inc(struct timeval addend, float increment); - - - - - diff --git a/clockB/kd.c b/clockB/kd.c deleted file mode 100644 index 68287cb545..0000000000 --- a/clockB/kd.c +++ /dev/null @@ -1,219 +0,0 @@ -/************************************************************************** - - This is a component of the hwclock program. - - This file contains the code for accessing the hardware clock via - the KDHWCLK facility of M68k machines. - -****************************************************************************/ - -#include <stdio.h> -#include <fcntl.h> -#include <errno.h> -#include <string.h> -#include <sys/ioctl.h> - -#include "hwclock.h" - - - -#if defined(KDGHWCLK) -const bool got_kdghwclk = TRUE; -static const int kdghwclk_ioctl = KDGHWCLK; -static const int kdshwclk_ioctl = KDSHWCLK; -#else -const bool got_kdghwclk = FALSE; -static const int kdghwclk_ioctl; /* Never used; just to make compile work */ -struct hwclk_time {int sec;}; - /* Never used; just to make compile work */ -#endif - - -void -synchronize_to_clock_tick_KD(int *retcode_p) { -/*---------------------------------------------------------------------------- - Wait for the top of a clock tick by calling KDGHWCLK in a busy loop until - we see it. ------------------------------------------------------------------------------*/ - int con_fd; - - if (debug) - printf("Waiting in loop for time from KDGHWCLK to change\n"); - - con_fd = open("/dev/tty1", O_RDONLY); - if (con_fd < 0) { - fprintf(stderr, "%s: open() failed to open /dev/tty1, errno = %s (%d).\n", - MYNAME, strerror(errno), errno); - *retcode_p = 1; - } else { - int rc; /* return code from ioctl() */ - int i; /* local loop index */ - /* The time when we were called (and started waiting) */ - struct hwclk_time start_time, nowtime; - - rc = ioctl(con_fd, kdghwclk_ioctl, &start_time); - if (rc == -1) { - fprintf(stderr, "%s: KDGHWCLK to read time failed, " - "errno = %s (%d).\n", MYNAME, strerror(errno), errno); - *retcode_p = 3; - } - - for (i = 0; - (rc = ioctl(con_fd, kdghwclk_ioctl, &nowtime)) != -1 - && start_time.sec == nowtime.sec && i < 1000000; - i++); - if (i >= 1000000) { - fprintf(stderr, "%s: Timed out waiting for time change.\n", MYNAME); - *retcode_p = 2; - } else if (rc == -1) { - fprintf(stderr, "%s: KDGHWCLK to read time failed, " - "errno = %s (%d).\n", MYNAME, strerror(errno), errno); - *retcode_p = 3; - } else *retcode_p = 0; - close(con_fd); - } -} - - - -void -read_hardware_clock_kd(struct tm *tm) { -/*---------------------------------------------------------------------------- - Read the hardware clock and return the current time via <tm> - argument. Use ioctls to /dev/tty1 on what we assume is an m68k - machine. - - Note that we don't use /dev/console here. That might be a serial - console. ------------------------------------------------------------------------------*/ -#ifdef KDGHWCLK - int con_fd; - struct hwclk_time t; - - con_fd = open("/dev/tty1", O_RDONLY); - if (con_fd < 0) { - fprintf(stderr, "%s: open() failed to open /dev/tty1, errno = %s (%d).\n", - MYNAME, strerror(errno), errno); - exit(5); - } else { - int rc; /* return code from ioctl() */ - - rc = ioctl(con_fd, kdghwclk_ioctl, &t); - if (rc == -1) { - fprintf(stderr, "%s: ioctl() failed to read time from /dev/tty1, " - "errno = %s (%d).\n", - MYNAME, strerror(errno), errno); - exit(5); - } - close(con_fd); - } - - 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 */ -#else - /* This routine should never be invoked. It is here just to make the - program compile. - */ -#endif -} - - - -void -set_hardware_clock_kd(const struct tm new_broken_time, - const bool testing) { -/*---------------------------------------------------------------------------- - 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. -----------------------------------------------------------------------------*/ -#ifdef KDGHWCLK - int con_fd; /* File descriptor of /dev/tty1 */ - struct hwclk_time t; - - con_fd = open("/dev/tty1", O_RDONLY); - if (con_fd < 0) { - fprintf(stderr, "%s: Error opening /dev/tty1. Errno: %s (%d)\n", - MYNAME, strerror(errno), errno); - exit(1); - } else { - int rc; /* locally used return code */ - - 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 (testing) - printf("Not setting Hardware Clock because running in test mode.\n"); - else { - rc = ioctl(con_fd, kdshwclk_ioctl, &t ); - if (rc < 0) { - fprintf(stderr, "%s: ioctl() to open /dev/tty1 failed. " - "Errno: %s (%d)\n", - MYNAME, strerror(errno), errno); - exit(1); - } - } - close(con_fd); - } -#else - /* This function should never be invoked. It is here just to make the - program compile. - */ -#endif -} - - - -void -see_if_kdghwclk_works(bool * const kdghwclk_works_p) { -/*---------------------------------------------------------------------------- - Find out if we are capable of accessing the Hardware Clock via the - KDHWCLK facility (ioctl to /dev/tty1). ------------------------------------------------------------------------------*/ - if (got_kdghwclk) { - int con_fd; - struct hwclk_time t; - - con_fd = open("/dev/tty1", O_RDONLY); - if (con_fd >= 0) { - if (ioctl( con_fd, kdghwclk_ioctl, &t ) >= 0) - *kdghwclk_works_p = TRUE; - else { - if (errno == EINVAL) { - /* KDGHWCLK not implemented in this kernel... */ - *kdghwclk_works_p = FALSE; - if (debug) - printf(MYNAME "was built with KDGHWCLK capability, but the " - "ioctl does not exist in the kernel. The ioctl (to " - "/dev/tty1) failed with errno EINVAL.\n"); - } else { - *kdghwclk_works_p = FALSE; - fprintf(stderr, - "%s: KDGHWCLK ioctl failed, errno = %s (%d).\n", - MYNAME, strerror(errno), errno); - } - } - } else { - *kdghwclk_works_p = FALSE; - fprintf(stderr, - "%s: Can't open /dev/tty1. open() errno = %s (%d).\n", - MYNAME, strerror(errno), errno); - } - close(con_fd); - } else *kdghwclk_works_p = FALSE; -} - - - diff --git a/clockB/rtc.c b/clockB/rtc.c deleted file mode 100644 index 46041b297e..0000000000 --- a/clockB/rtc.c +++ /dev/null @@ -1,425 +0,0 @@ -/************************************************************************** - - This is a component of the hwclock program. - - This file contains the code for accessing the hardware clock via - the rtc device driver (usually hooked up to the /dev/rtc device - special file). - -****************************************************************************/ - -#include <stdio.h> -#include <fcntl.h> -#include <errno.h> -#include <string.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/ioctl.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) - - -#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. Might need to make - it conditional on i386 or something too -janl */ -#if LINUX_VERSION_CODE >= 131072 -#include <linux/kd.h> -static const bool got_rtc = TRUE; -#else -static const bool got_rtc = FALSE; -#endif - -/* struct linux_rtc_time is present since Linux 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 Linux 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 ioctls are in kernels since - Linux 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 - - - -static void -do_rtc_read_ioctl(int rtc_fd, struct tm *tm, int *retcode_p) { -/*---------------------------------------------------------------------------- - Do the ioctl to read the time. This is, unfortunately, a slightly - different function for Sparc than for everything else. But we - return the standard 'tm' structure result in spite of the fact that - the Sparc ioctl returns something else. - - If the ioctl fails, issue message to stderr and return rc = 1; - else, no message and rc = 0. ------------------------------------------------------------------------------*/ - int rc; - -#ifdef __sparc__ - struct sparc_rtc_time stm; - - rc = ioctl(rtc_fd, RTCGET, &stm); - 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 */ -#else - rc = ioctl(rtc_fd, RTC_RD_TIME, tm); -#endif - if (rc == -1) { - fprintf(stderr, "%s: ioctl() to /dev/rtc to read the time failed. " - "errno = %s (%d)\n", MYNAME, strerror(errno), errno); - *retcode_p = 1; - } else *retcode_p = 0; - tm->tm_isdst = -1; /* don't know whether it's daylight */ - return; -} - - - -void -get_epoch(unsigned long *epoch_p, char **reason_p){ -/*---------------------------------------------------------------------------- - Get the Hardware Clock epoch setting from the kernel. - - If we succeed, return the setting (number of year A.D.) as - *epoch_p and *reason_p == NULL. - - If we fail, return and English description of what went wrong as a - null-terminated string in newly malloc'ed storage and the pointer to - it as *reason_p. -----------------------------------------------------------------------------*/ - int rtc_fd; - - rtc_fd = open("/dev/rtc", O_RDONLY); - if (rtc_fd < 0) { - if (errno == ENOENT) - *reason_p = - strdup("To manipulate the epoch value in the kernel, we must " - "access the Linux 'rtc' device driver via the device special " - "file /dev/rtc. This file does not exist on this system.\n"); - else { - *reason_p = malloc(200); - sprintf(*reason_p, "Unable to open /dev/rtc, open() errno = %s (%d)\n", - strerror(errno), errno); - } - } else { - int rc; /* return code from ioctl */ - rc = ioctl(rtc_fd, RTC_EPOCH_READ, epoch_p); - if (rc == -1) { - *reason_p = malloc(200); - sprintf(*reason_p, "ioctl(RTC_EPOCH_READ) to /dev/rtc failed, " - "errno = %s (%d).\n", strerror(errno), errno); - } else { - *reason_p = NULL; - if (debug) printf("we have read epoch %ld from /dev/rtc " - "with RTC_EPOCH_READ ioctl.\n", *epoch_p); - } - close(rtc_fd); - } - return; -} - - - -static void -busywait_for_rtc_clock_tick(const int rtc_fd, int *retcode_p) { -/*---------------------------------------------------------------------------- - Wait for the top of a clock tick by reading /dev/rtc in a busy loop until - we see it. ------------------------------------------------------------------------------*/ - struct tm start_time; - /* The time when we were called (and started waiting) */ - int rc; - - if (debug) - printf("Waiting in loop for time from /dev/rtc to change\n"); - - do_rtc_read_ioctl(rtc_fd, &start_time, &rc); - if (rc != 0) *retcode_p = 1; - else { - /* Wait for change. 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. - */ - struct tm nowtime; - int iterations; /* how many time's we've spun through the loop */ - int rc; /* Return code from do_rtc_read_ioctl */ - - iterations = 0; - do { - do_rtc_read_ioctl(rtc_fd, &nowtime, &rc); - } while (rc == 0 && start_time.tm_sec == nowtime.tm_sec - && iterations++ < 1000000); - - if (iterations >= 1000000) { - fprintf(stderr, "%s: Timed out waiting for time change.\n", MYNAME); - *retcode_p = 2; - } else if (rc != 0) *retcode_p = 3; - else *retcode_p = 0; - } -} - - - -void -synchronize_to_clock_tick_RTC(int *retcode_p) { -/*---------------------------------------------------------------------------- - Same as synchronize_to_clock_tick(), but just for /dev/rtc. ------------------------------------------------------------------------------*/ - int rtc_fd; /* File descriptor of /dev/rtc */ - - rtc_fd = open("/dev/rtc", O_RDONLY); - - if (rtc_fd == -1) { - fprintf(stderr, "%s: open() of /dev/rtc failed, errno = %s (%d).\n", - MYNAME, strerror(errno), errno); - *retcode_p = 1; - } else { - int rc; /* Return code from ioctl */ - /* Turn on update interrupts (one per second) */ - rc = ioctl(rtc_fd, RTC_UIE_ON, 0); - if (rc == -1 && 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("/dev/rtc does not have interrupt functions. \n"); - busywait_for_rtc_clock_tick(rtc_fd, retcode_p); - } else if (rc != -1) { - int rc; /* return code from ioctl */ - unsigned long dummy; - - /* this blocks until the next update interrupt */ - rc = read(rtc_fd, &dummy, sizeof(dummy)); - if (rc == -1) { - fprintf(stderr, "%s: read() to /dev/rtc to wait for clock tick " - "failed, errno = %s (%d).\n", MYNAME, strerror(errno), errno); - *retcode_p = 1; - } else { - *retcode_p = 0; - } - /* Turn off update interrupts */ - rc = ioctl(rtc_fd, RTC_UIE_OFF, 0); - if (rc == -1) { - fprintf(stderr, - "%s: ioctl() to /dev/rtc to turn off update interrupts " - "failed, errno = %s (%d).\n", MYNAME, strerror(errno), errno); - } - } else { - fprintf(stderr, "%s: ioctl() to /dev/rtc to turn on update interrupts " - "failed unexpectedly, errno = %s (%d).\n", - MYNAME, strerror(errno), errno); - *retcode_p = 1; - } - close(rtc_fd); - } -} - - - -void -read_hardware_clock_rtc_ioctl(struct tm *tm) { -/*---------------------------------------------------------------------------- - Read the hardware clock and return the current time via <tm> - argument. Use ioctls to "rtc" device /dev/rtc. ------------------------------------------------------------------------------*/ - int rc; /* Local return code */ - int rtc_fd; /* File descriptor of /dev/rtc */ - - rtc_fd = open("/dev/rtc",O_RDONLY); -if (rtc_fd == -1) { -fprintf(stderr, "%s: open() of /dev/rtc failed, errno = %s (%d).\n", - MYNAME, strerror(errno), errno); -exit(5); - } else { - /* Read the RTC time/date */ - - rc = ioctl(rtc_fd, RTC_RD_TIME, tm); - if (rc == -1) { - fprintf(stderr, "%s: ioctl() to /dev/rtc to read the time failed, " - "errno = %s (%d).\n", MYNAME, strerror(errno), errno); - exit(5); - } - close(rtc_fd); - } - tm->tm_isdst = -1; /* don't know whether it's daylight */ -} - - - -void -set_hardware_clock_rtc_ioctl(const struct tm new_broken_time, - const bool testing) { -/*---------------------------------------------------------------------------- - Set the Hardware Clock to the broken down time <new_broken_time>. - Use ioctls to "rtc" device /dev/rtc. -----------------------------------------------------------------------------*/ - int rc; - int rtc_fd; - - rtc_fd = open("/dev/rtc", O_RDONLY); - if (rtc_fd < 0) { - fprintf(stderr, "%s: Unable to open /dev/rtc, open() errno = %s (%d)\n", - MYNAME, strerror(errno), errno); - exit(5); - } else { - if (testing) - printf("Not setting Hardware Clock because running in test mode.\n"); - else { - rc = ioctl(rtc_fd, RTC_SET_TIME, &new_broken_time); - if (rc == -1) { - fprintf(stderr, - "%s: ioctl() (RTC_SET_TIME) to /dev/rtc to set time failed, " - "errno = %s (%d).\n", MYNAME, strerror(errno), errno); - exit(5); - } else { - if (debug) - printf("ioctl(RTC_SET_TIME) was successful.\n"); - } - } - close(rtc_fd); - } -} - - - -void -set_epoch(unsigned long epoch, const bool testing, int *retcode_p) { -/*---------------------------------------------------------------------------- - Set the Hardware Clock epoch in the kernel. -----------------------------------------------------------------------------*/ - if (epoch < 1900) - /* kernel would not accept this epoch value */ - fprintf(stderr, "%s: The epoch value may not be less than 1900. " - "You requested %ld\n", MYNAME, epoch); - else { - int rtc_fd; - - rtc_fd = open("/dev/rtc", O_RDONLY); - if (rtc_fd < 0) { - if (errno == ENOENT) - fprintf(stderr, - "%s: To manipulate the epoch value in the kernel, we must " - "access the Linux 'rtc' device driver via the device special " - "file /dev/rtc. This file does not exist on this system.\n", - MYNAME); - fprintf(stderr, "%s: Unable to open /dev/rtc, open() errno = %s (%d)\n", - MYNAME, strerror(errno), errno); - *retcode_p = 1; - } else { - if (debug) printf("setting epoch to %ld " - "with RTC_EPOCH_SET ioctl to /dev/rtc.\n", epoch); - if (testing) { - printf("Not setting epoch because running in test mode.\n"); - *retcode_p = 0; - } else { - int rc; /* return code from ioctl */ - rc = ioctl(rtc_fd, RTC_EPOCH_SET, epoch); - if (rc == -1) { - if (errno == EINVAL) - fprintf(stderr, "%s: The kernel (specifically, the device driver " - "for /dev/rtc) does not have the RTC_EPOCH_SET ioctl. " - "Get a newer driver.\n", MYNAME); - else - fprintf(stderr, "%s: ioctl(RTC_EPOCH_SET) to /dev/rtc failed, " - "errno = %s (%d).\n", MYNAME, strerror(errno), errno); - *retcode_p = 1; - } else *retcode_p = 0; - } - close(rtc_fd); - } - } -} - - - -void -see_if_rtc_works(bool * const rtc_works_p) { -/*---------------------------------------------------------------------------- - Find out if we are capable of accessing the Hardware Clock via the rtc - driver (via device file /dev/rtc). ------------------------------------------------------------------------------*/ - if (got_rtc) { - int rtc_fd = open("/dev/rtc", O_RDONLY); - if (rtc_fd > 0) { - *rtc_works_p = TRUE; - close(rtc_fd); - } else { - *rtc_works_p = FALSE; - if (debug) - printf("Open of /dev/rtc failed, errno = %s (%d). " - "falling back to more primitive clock access method.\n", - strerror(errno), errno); - } - } else { - if (debug) - printf("The Linux kernel for which this copy of hwclock() was built " - "is too old to have /dev/rtc\n"); - *rtc_works_p = FALSE; - } -} - - - - diff --git a/clockB/shhopt.c b/clockB/shhopt.c deleted file mode 100644 index 6d31225c8b..0000000000 --- a/clockB/shhopt.c +++ /dev/null @@ -1,467 +0,0 @@ -/* $Id: shhopt.c,v 2.2 1997/07/06 23:11:55 aebr Exp $ */ -/************************************************************************** - * - * FILE shhopt.c - * - * DESCRIPTION Functions for parsing command line arguments. Values - * of miscellaneous types may be stored in variables, - * or passed to functions as specified. - * - * REQUIREMENTS Some systems lack the ANSI C -function strtoul. If your - * system is one of those, you'll ned to write one yourself, - * or get the GNU liberty-library (from prep.ai.mit.edu). - * - * WRITTEN BY Sverre H. Huseby <sverrehu@ifi.uio.no> - * - **************************************************************************/ - -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <string.h> -#include <ctype.h> -#include <limits.h> -#include <errno.h> - -#include "shhopt.h" - -/************************************************************************** - * * - * P R I V A T E D A T A * - * * - **************************************************************************/ - -static void optFatalFunc(const char *, ...); -static void (*optFatal)(const char *format, ...) = optFatalFunc; - - - -/************************************************************************** - * * - * P R I V A T E F U N C T I O N S * - * * - **************************************************************************/ - -/*------------------------------------------------------------------------- - * - * NAME optFatalFunc - * - * FUNCTION Show given message and abort the program. - * - * INPUT format, ... - * Arguments used as with printf(). - * - * RETURNS Never returns. The program is aborted. - * - */ -void optFatalFunc(const char *format, ...) -{ - va_list ap; - - fflush(stdout); - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); - exit(99); -} - - - -/*------------------------------------------------------------------------- - * - * NAME optStructCount - * - * FUNCTION Get number of options in a optStruct. - * - * INPUT opt array of possible options. - * - * RETURNS Number of options in the given array. - * - * DESCRIPTION Count elements in an optStruct-array. The strcture must - * be ended using an element of type OPT_END. - * - */ -static int optStructCount(const optStruct opt[]) -{ - int ret = 0; - - while (opt[ret].type != OPT_END) - ++ret; - return ret; -} - - - -/*------------------------------------------------------------------------- - * - * NAME optMatch - * - * FUNCTION Find a matching option. - * - * INPUT opt array of possible options. - * s string to match, without `-' or `--'. - * lng match long option, otherwise short. - * - * RETURNS Index to the option if found, -1 if not found. - * - * DESCRIPTION Short options are matched from the first character in - * the given string. - * - */ -static int optMatch(const optStruct opt[], const char *s, int lng) -{ - int nopt, q, matchlen = 0; - char *p; - - nopt = optStructCount(opt); - if (lng) { - if ((p = strchr(s, '=')) != NULL) - matchlen = p - s; - else - matchlen = strlen(s); - } - for (q = 0; q < nopt; q++) { - if (lng) { - if (!opt[q].longName) - continue; - if (strncmp(s, opt[q].longName, matchlen) == 0) - return q; - } else { - if (!opt[q].shortName) - continue; - if (*s == opt[q].shortName) - return q; - } - } - return -1; -} - - - -/*------------------------------------------------------------------------- - * - * NAME optString - * - * FUNCTION Return a (static) string with the option name. - * - * INPUT opt the option to stringify. - * lng is it a long option? - * - * RETURNS Pointer to static string. - * - */ -static char *optString(const optStruct *opt, int lng) -{ - static char ret[31]; - - if (lng) { - strcpy(ret, "--"); - strncpy(ret + 2, opt->longName, 28); - } else { - ret[0] = '-'; - ret[1] = opt->shortName; - ret[2] = '\0'; - } - return ret; -} - - - -/*------------------------------------------------------------------------- - * - * NAME optNeedsArgument - * - * FUNCTION Check if an option requires an argument. - * - * INPUT opt the option to check. - * - * RETURNS Boolean value. - * - */ -static int optNeedsArgument(const optStruct *opt) -{ - return opt->type == OPT_STRING - || opt->type == OPT_INT - || opt->type == OPT_UINT - || opt->type == OPT_LONG - || opt->type == OPT_ULONG; -} - - - -/*------------------------------------------------------------------------- - * - * NAME argvRemove - * - * FUNCTION Remove an entry from an argv-array. - * - * INPUT argc pointer to number of options. - * argv array of option-/argument-strings. - * i index of option to remove. - * - * OUTPUT argc new argument count. - * argv array with given argument removed. - * - */ -static void argvRemove(int *argc, char *argv[], int i) -{ - if (i >= *argc) - return; - while (i++ < *argc) - argv[i - 1] = argv[i]; - --*argc; -} - - - -/*------------------------------------------------------------------------- - * - * NAME optExecute - * - * FUNCTION Perform the action of an option. - * - * INPUT opt array of possible options. - * arg argument to option, if it applies. - * lng was the option given as a long option? - * - * RETURNS Nothing. Aborts in case of error. - * - */ -void optExecute(const optStruct *opt, char *arg, int lng) -{ - switch (opt->type) { - case OPT_FLAG: - if (opt->flags & OPT_CALLFUNC) - ((void (*)(void)) opt->arg)(); - else - *((int *) opt->arg) = 1; - break; - - case OPT_STRING: - if (opt->flags & OPT_CALLFUNC) - ((void (*)(char *)) opt->arg)(arg); - else - *((char **) opt->arg) = arg; - break; - - case OPT_INT: - case OPT_LONG: { - long tmp; - char *e; - - tmp = strtol(arg, &e, 10); - if (*e) - optFatal("invalid number `%s'\n", arg); - if (errno == ERANGE - || (opt->type == OPT_INT && (tmp > INT_MAX || tmp < INT_MIN))) - optFatal("number `%s' to `%s' out of range\n", - arg, optString(opt, lng)); - if (opt->type == OPT_INT) { - if (opt->flags & OPT_CALLFUNC) - ((void (*)(int)) opt->arg)((int) tmp); - else - *((int *) opt->arg) = (int) tmp; - } else /* OPT_LONG */ { - if (opt->flags & OPT_CALLFUNC) - ((void (*)(long)) opt->arg)(tmp); - else - *((long *) opt->arg) = tmp; - } - break; - } - - case OPT_UINT: - case OPT_ULONG: { - unsigned long tmp; - char *e; - - tmp = strtoul(arg, &e, 10); - if (*e) - optFatal("invalid number `%s'\n", arg); - if (errno == ERANGE - || (opt->type == OPT_UINT && tmp > UINT_MAX)) - optFatal("number `%s' to `%s' out of range\n", - arg, optString(opt, lng)); - if (opt->type == OPT_UINT) { - if (opt->flags & OPT_CALLFUNC) - ((void (*)(unsigned)) opt->arg)((unsigned) tmp); - else - *((unsigned *) opt->arg) = (unsigned) tmp; - } else /* OPT_ULONG */ { - if (opt->flags & OPT_CALLFUNC) - ((void (*)(unsigned long)) opt->arg)(tmp); - else - *((unsigned long *) opt->arg) = tmp; - } - break; - } - - default: - break; - } -} - - - -/************************************************************************** - * * - * P U B L I C F U N C T I O N S * - * * - **************************************************************************/ - -/*------------------------------------------------------------------------- - * - * NAME optSetFatalFunc - * - * FUNCTION Set function used to display error message and exit. - * - * SYNOPSIS #include "shhmsg.h" - * void optSetFatalFunc(void (*f)(const char *, ...)); - * - * INPUT f function accepting printf()'like parameters, - * that _must_ abort the program. - * - */ -void optSetFatalFunc(void (*f)(const char *, ...)) -{ - optFatal = f; -} - - - -/*------------------------------------------------------------------------- - * - * NAME optParseOptions - * - * FUNCTION Parse commandline options. - * - * SYNOPSIS #include "shhopt.h" - * void optParseOptions(int *argc, char *argv[], - * const optStruct opt[], int allowNegNum); - * - * INPUT argc Pointer to number of options. - * argv Array of option-/argument-strings. - * opt Array of possible options. - * allowNegNum - * a negative number is not to be taken as - * an option. - * - * OUTPUT argc new argument count. - * argv array with arguments removed. - * - * RETURNS Nothing. Aborts in case of error. - * - * DESCRIPTION This function checks each option in the argv-array - * against strings in the opt-array, and `executes' any - * matching action. Any arguments to the options are - * extracted and stored in the variables or passed to - * functions pointed to by entries in opt. - * - * Options and arguments used are removed from the argv- - * array, and argc is decreased accordingly. - * - * Any error leads to program abortion. - * - */ -void optParseOptions(int *argc, char *argv[], - const optStruct opt[], int allowNegNum) -{ - int ai, /* argv index. */ - optarg, /* argv index of option argument, or -1 if none. */ - mi, /* Match index in opt. */ - done; - char *arg, /* Pointer to argument to an option. */ - *o, /* pointer to an option character */ - *p; - - /* - * Loop through all arguments. - */ - for (ai = 0; ai < *argc; ) { - /* - * "--" indicates that the rest of the argv-array does not - * contain options. - */ - if (strcmp(argv[ai], "--") == 0) { - argvRemove(argc, argv, ai); - break; - } - - if (allowNegNum && argv[ai][0] == '-' && isdigit(argv[ai][1])) { - ++ai; - continue; - } else if (strncmp(argv[ai], "--", 2) == 0) { - /* long option */ - /* find matching option */ - if ((mi = optMatch(opt, argv[ai] + 2, 1)) < 0) - optFatal("unrecognized option `%s'\n", argv[ai]); - - /* possibly locate the argument to this option. */ - arg = NULL; - if ((p = strchr(argv[ai], '=')) != NULL) - arg = p + 1; - - /* does this option take an argument? */ - optarg = -1; - if (optNeedsArgument(&opt[mi])) { - /* option needs an argument. find it. */ - if (!arg) { - if ((optarg = ai + 1) == *argc) - optFatal("option `%s' requires an argument\n", - optString(&opt[mi], 1)); - arg = argv[optarg]; - } - } else { - if (arg) - optFatal("option `%s' doesn't allow an argument\n", - optString(&opt[mi], 1)); - } - /* perform the action of this option. */ - optExecute(&opt[mi], arg, 1); - /* remove option and any argument from the argv-array. */ - if (optarg >= 0) - argvRemove(argc, argv, ai); - argvRemove(argc, argv, ai); - } else if (*argv[ai] == '-') { - /* A dash by itself is not considered an option. */ - if (argv[ai][1] == '\0') { - ++ai; - continue; - } - /* Short option(s) following */ - o = argv[ai] + 1; - done = 0; - optarg = -1; - while (*o && !done) { - /* find matching option */ - if ((mi = optMatch(opt, o, 0)) < 0) - optFatal("unrecognized option `-%c'\n", *o); - - /* does this option take an argument? */ - optarg = -1; - arg = NULL; - if (optNeedsArgument(&opt[mi])) { - /* option needs an argument. find it. */ - arg = o + 1; - if (!*arg) { - if ((optarg = ai + 1) == *argc) - optFatal("option `%s' requires an argument\n", - optString(&opt[mi], 0)); - arg = argv[optarg]; - } - done = 1; - } - /* perform the action of this option. */ - optExecute(&opt[mi], arg, 0); - ++o; - } - /* remove option and any argument from the argv-array. */ - if (optarg >= 0) - argvRemove(argc, argv, ai); - argvRemove(argc, argv, ai); - } else { - /* a non-option argument */ - ++ai; - } - } -} diff --git a/clockB/shhopt.h b/clockB/shhopt.h deleted file mode 100644 index 3f87c138b8..0000000000 --- a/clockB/shhopt.h +++ /dev/null @@ -1,33 +0,0 @@ -/* $Id: shhopt.h,v 1.2 1996/06/06 00:06:35 sverrehu Exp $ */ -#ifndef SHHOPT_H -#define SHHOPT_H - -/* constants for recognized option types. */ -typedef enum { - OPT_END, /* nothing. used as ending element. */ - OPT_FLAG, /* no argument following. sets variable to 1. */ - OPT_STRING, /* string argument. */ - OPT_INT, /* signed integer argument. */ - OPT_UINT, /* unsigned integer argument. */ - OPT_LONG, /* signed long integer argument. */ - OPT_ULONG, /* unsigned long integer argument. */ -} optArgType; - -/* flags modifying the default way options are handeled. */ -#define OPT_CALLFUNC 1 /* pass argument to a function. */ - -typedef struct { - char shortName; /* Short option name. */ - char *longName; /* Long option name, no including '--'. */ - optArgType type; /* Option type. */ - void *arg; /* Pointer to variable to fill with argument, - * or pointer to function if Type == OPT_FUNC. */ - int flags; /* Modifier flags. */ -} optStruct; - - -void optSetFatalFunc(void (*f)(const char *, ...)); -void optParseOptions(int *argc, char *argv[], - const optStruct opt[], const int allowNegNum); - -#endif diff --git a/clockB/util.c b/clockB/util.c deleted file mode 100644 index 8a1a6060c2..0000000000 --- a/clockB/util.c +++ /dev/null @@ -1,120 +0,0 @@ -/************************************************************************** - - This is a component of the hwclock program. - - This file contains the code for various basic utility routines - needed by the other modules. - -****************************************************************************/ - -#include <stdio.h> -#include <string.h> - -#include "hwclock.h" - -bool -is_in_cpuinfo(const char * const fmt, const char * const str) { -/*---------------------------------------------------------------------------- - Return true iff the /proc/cpuinfo file shows the value 'str' for the - keyword 'fmt'. Both arguments are null-terminated strings. - - If for any reason we can't read /proc/cpuinfo, return false. ------------------------------------------------------------------------------*/ - FILE *cpuinfo; - char field[256]; - char format[256]; - bool found; - - sprintf(format, "%s : %s", fmt, "%255s"); - - found = FALSE; /* initial value */ - if ((cpuinfo = fopen ("/proc/cpuinfo", "r")) != NULL) { - while (!feof(cpuinfo)) { - if (fscanf (cpuinfo, format, field) == 1) { - if (strncmp(field, str, strlen(str)) == 0) - found = TRUE; - break; - } - fgets (field, 256, cpuinfo); - } - fclose(cpuinfo); - } - return found; -} - - - -char * -ctime2(const time_t time) { -/*---------------------------------------------------------------------------- - Same as ctime() from the standard C library, except it takes a time_t - as an argument instead of a pointer to a time_t, so it is much more - useful. - - Also, don't include a newline at the end of the returned string. If - the user wants a newline, he can provide it himself. - - return value is in static storage within. ------------------------------------------------------------------------------*/ - static char retval[30]; - - strncpy(retval, ctime(&time), sizeof(retval)); - retval[sizeof(retval)-1] = '\0'; - - /* Now chop off the last character, which is the newline */ - if (strlen(retval) >= 1) /* for robustness */ - retval[strlen(retval)-1] = '\0'; - return(retval); - -} - - - -struct timeval -t2tv(time_t argument) { -/*---------------------------------------------------------------------------- - Convert from "time_t" format to "timeval" format. ------------------------------------------------------------------------------*/ - struct timeval retval; - - retval.tv_sec = argument; - retval.tv_usec = 0; - return(retval); -} - - - -float -time_diff(struct timeval subtrahend, struct timeval subtractor) { -/*--------------------------------------------------------------------------- - The difference in seconds between two times in "timeval" format. -----------------------------------------------------------------------------*/ - return( (subtrahend.tv_sec - subtractor.tv_sec) - + (subtrahend.tv_usec - subtractor.tv_usec) / 1E6 ); -} - - -struct timeval -time_inc(struct timeval addend, float increment) { -/*---------------------------------------------------------------------------- - The time, in "timeval" format, which is <increment> seconds after - the time <addend>. Of course, <increment> may be negative. ------------------------------------------------------------------------------*/ - struct timeval newtime; - - newtime.tv_sec = addend.tv_sec + (int) increment; - newtime.tv_usec = addend.tv_usec + (increment - (int) increment) * 1E6; - - /* Now adjust it so that the microsecond value is between 0 and 1 million */ - if (newtime.tv_usec < 0) { - newtime.tv_usec += 1E6; - newtime.tv_sec -= 1; - } else if (newtime.tv_usec >= 1E6) { - newtime.tv_usec -= 1E6; - newtime.tv_sec += 1; - } - return(newtime); -} - - - |
