diff options
Diffstat (limited to 'sys-utils/clock.c')
-rw-r--r-- | sys-utils/clock.c | 490 |
1 files changed, 0 insertions, 490 deletions
diff --git a/sys-utils/clock.c b/sys-utils/clock.c deleted file mode 100644 index 15020c260..000000000 --- a/sys-utils/clock.c +++ /dev/null @@ -1,490 +0,0 @@ -#include <stdio.h> -#include <errno.h> -#include <stdlib.h> -#include <unistd.h> -#include <getopt.h> -#include <time.h> -#include <sys/time.h> -#include <string.h> - -#define USE_INLINE_ASM_IO - -#ifdef USE_INLINE_ASM_IO -#include <asm/io.h> -#endif - -/* V1.0 - * CMOS clock manipulation - Charles Hedrick, hedrick@cs.rutgers.edu, Apr 1992 - * - * clock [-u] -r - read cmos clock - * clock [-u] -w - write cmos clock from system time - * clock [-u] -s - set system time from cmos clock - * clock [-u] -a - set system time from cmos clock, adjust the time to - * correct for systematic error, and put it back to the cmos. - * -u indicates cmos clock is kept in universal time - * - * The program is designed to run setuid, since we need to be able to - * write the CMOS port. - * - * I don't know what the CMOS clock will do in 2000, so this program - * probably won't work past the century boundary. - * - ********************* - * V1.1 - * Modified for clock adjustments - Rob Hooft, hooft@chem.ruu.nl, Nov 1992 - * Also moved error messages to stderr. The program now uses getopt. - * Changed some exit codes. Made 'gcc 2.3 -Wall' happy. - * - * I think a small explanation of the adjustment routine should be given - * here. The problem with my machine is that its CMOS clock is 10 seconds - * per day slow. With this version of clock.c, and my '/etc/rc.local' - * reading '/etc/clock -au' instead of '/etc/clock -u -s', this error - * is automatically corrected at every boot. - * - * To do this job, the program reads and writes the file '/etc/adjtime' - * to determine the correction, and to save its data. In this file are - * three numbers: - * - * 1) the correction in seconds per day (So if your clock runs 5 - * seconds per day fast, the first number should read -5.0) - * 2) the number of seconds since 1/1/1970 the last time the program was - * used. - * 3) the remaining part of a second which was leftover after the last - * adjustment - * - * Installation and use of this program: - * - * a) create a file '/etc/adjtime' containing as the first and only line: - * '0.0 0 0.0' - * b) run 'clock -au' or 'clock -a', depending on whether your cmos is in - * universal or local time. This updates the second number. - * c) set your system time using the 'date' command. - * d) update your cmos time using 'clock -wu' or 'clock -w' - * e) replace the first number in /etc/adjtime by your correction. - * f) put the command 'clock -au' or 'clock -a' in your '/etc/rc.local' - * - * If the adjustment doesn't work for you, try contacting me by E-mail. - * - ****** - * V1.2 - * - * Applied patches by Harald Koenig (koenig@nova.tat.physik.uni-tuebingen.de) - * Patched and indented by Rob Hooft (hooft@EMBL-Heidelberg.DE) - * - * A free quote from a MAIL-message (with spelling corrections): - * - * "I found the explanation and solution for the CMOS reading 0xff problem - * in the 0.99pl13c (ALPHA) kernel: the RTC goes offline for a small amount - * of time for updating. Solution is included in the kernel source - * (linux/kernel/time.c)." - * - * "I modified clock.c to fix this problem and added an option (now default, - * look for USE_INLINE_ASM_IO) that I/O instructions are used as inline - * code and not via /dev/port (still possible via #undef ...)." - * - * With the new code, which is partially taken from the kernel sources, - * the CMOS clock handling looks much more "official". - * Thanks Harald (and Torsten for the kernel code)! - * - ****** - * V1.3 - * Canges from alan@spri.levels.unisa.edu.au (Alan Modra): - * a) Fix a few typos in comments and remove reference to making - * clock -u a cron job. The kernel adjusts cmos time every 11 - * minutes - see kernel/sched.c and kernel/time.c set_rtc_mmss(). - * This means we should really have a cron job updating - * /etc/adjtime every 11 mins (set last_time to the current time - * and not_adjusted to ???). - * b) Swapped arguments of outb() to agree with asm/io.h macro of the - * same name. Use outb() from asm/io.h as it's slightly better. - * c) Changed CMOS_READ and CMOS_WRITE to inline functions. Inserted - * cli()..sti() pairs in appropriate places to prevent possible - * errors, and changed ioperm() call to iopl() to allow cli. - * d) Moved some variables around to localise them a bit. - * e) Fixed bug with clock -ua or clock -us that cleared environment - * variable TZ. This fix also cured the annoying display of bogus - * day of week on a number of machines. (Use mktime(), ctime() - * rather than asctime() ) - * f) Use settimeofday() rather than stime(). This one is important - * as it sets the kernel's timezone offset, which is returned by - * gettimeofday(), and used for display of MSDOS and OS2 file - * times. - * g) faith@cs.unc.edu added -D flag for debugging - * - * V1.4: alan@SPRI.Levels.UniSA.Edu.Au (Alan Modra) - * Wed Feb 8 12:29:08 1995, fix for years > 2000. - * faith@cs.unc.edu added -v option to print version. - * - */ - -#define VERSION "1.4" - -/* Here the information for time adjustments is kept. */ -#define ADJPATH "/etc/adjtime" - - -/* used for debugging the code. */ -/*#define KEEP_OFF */ - -/* Globals */ -int readit = 0; -int adjustit = 0; -int writeit = 0; -int setit = 0; -int universal = 0; -int debug = 0; - -volatile void -usage () -{ - fprintf (stderr, - "clock [-u] -r|w|s|a|v\n" - " r: read and print CMOS clock\n" - " w: write CMOS clock from system time\n" - " s: set system time from CMOS clock\n" - " a: get system time and adjust CMOS clock\n" - " u: CMOS clock is in universal time\n" - " v: print version (" VERSION ") and exit\n" - ); - exit (1); -} - -#ifndef USE_INLINE_ASM_IO -int cmos_fd; -#endif - -static inline unsigned char cmos_read(unsigned char reg) -{ - register unsigned char ret; - __asm__ volatile ("cli"); - outb (reg | 0x80, 0x70); - ret = inb (0x71); - __asm__ volatile ("sti"); - return ret; -} - -static inline void cmos_write(unsigned char reg, unsigned char val) -{ - outb (reg | 0x80, 0x70); - outb (val, 0x71); -} - -#ifndef outb -static inline void -outb (char val, unsigned short port) -{ -#ifdef USE_INLINE_ASM_IO - __asm__ volatile ("out%B0 %0,%1"::"a" (val), "d" (port)); -#else - lseek (cmos_fd, port, 0); - write (cmos_fd, &val, 1); -#endif -} -#endif - -#ifndef inb -static inline unsigned char -inb (unsigned short port) -{ - unsigned char ret; -#ifdef USE_INLINE_ASM_IO - __asm__ volatile ("in%B0 %1,%0":"=a" (ret):"d" (port)); -#else - lseek (cmos_fd, port, 0); - read (cmos_fd, &ret, 1); -#endif - return ret; -} -#endif - -void -cmos_init () -{ -#ifdef USE_INLINE_ASM_IO - if (iopl (3)) - { - fprintf(stderr,"clock: unable to get I/O port access\n"); - exit (1); - } -#else - cmos_fd = open ("/dev/port", 2); - if (cmos_fd < 0) - { - perror ("unable to open /dev/port read/write : "); - exit (1); - } - if (lseek (cmos_fd, 0x70, 0) < 0 || lseek (cmos_fd, 0x71, 0) < 0) - { - perror ("unable to seek port 0x70 in /dev/port : "); - exit (1); - } -#endif -} - -static inline int -cmos_read_bcd (int addr) -{ - int b; - b = cmos_read (addr); - return (b & 15) + (b >> 4) * 10; -} - -static inline void -cmos_write_bcd (int addr, int value) -{ - cmos_write (addr, ((value / 10) << 4) + value % 10); -} - -int -main (int argc, char **argv, char **envp) -{ - struct tm tm; - time_t systime; - time_t last_time; - char arg; - double factor; - double not_adjusted; - int adjustment = 0; - unsigned char save_control, save_freq_select; - - while ((arg = getopt (argc, argv, "rwsuaDv")) != -1) - { - switch (arg) - { - case 'r': - readit = 1; - break; - case 'w': - writeit = 1; - break; - case 's': - setit = 1; - break; - case 'u': - universal = 1; - break; - case 'a': - adjustit = 1; - break; - case 'D': - debug = 1; - break; - case 'v': - fprintf( stderr, "clock " VERSION "\n" ); - exit(0); - default: - usage (); - } - } - - if (readit + writeit + setit + adjustit > 1) - usage (); /* only allow one of these */ - - if (!(readit | writeit | setit | adjustit)) /* default to read */ - readit = 1; - - cmos_init (); - - if (adjustit) - { /* Read adjustment parameters first */ - FILE *adj; - if ((adj = fopen (ADJPATH, "r")) == NULL) - { - perror (ADJPATH); - exit (2); - } - if (fscanf (adj, "%lf %d %lf", &factor, &last_time, ¬_adjusted) < 0) - { - perror (ADJPATH); - exit (2); - } - fclose (adj); - if (debug) printf ("Last adjustment done at %d seconds after 1/1/1970\n", last_time); - } - - if (readit || setit || adjustit) - { - int i; - -/* read RTC exactly on falling edge of update flag */ -/* Wait for rise.... (may take upto 1 second) */ - - for (i = 0; i < 10000000; i++) - if (cmos_read (10) & 0x80) - break; - -/* Wait for fall.... (must try at least 2.228 ms) */ - - for (i = 0; i < 1000000; i++) - if (!(cmos_read (10) & 0x80)) - break; - -/* The purpose of the "do" loop is called "low-risk programming" */ -/* In theory it should never run more than once */ - do - { - tm.tm_sec = cmos_read_bcd (0); - tm.tm_min = cmos_read_bcd (2); - tm.tm_hour = cmos_read_bcd (4); - tm.tm_wday = cmos_read_bcd (6); - tm.tm_mday = cmos_read_bcd (7); - tm.tm_mon = cmos_read_bcd (8); - tm.tm_year = cmos_read_bcd (9); - } - while (tm.tm_sec != cmos_read_bcd (0)); - if (tm.tm_year < 70) - tm.tm_year += 100; /* 70..99 => 1970..1999, 0..69 => 2000..2069 */ - tm.tm_mon--; /* DOS uses 1 base */ - tm.tm_wday -= 3; /* DOS uses 3 - 9 for week days */ - tm.tm_isdst = -1; /* don't know whether it's daylight */ - if (debug) printf ("Cmos time : %d:%d:%d\n", tm.tm_hour, tm.tm_min, tm.tm_sec); - } - - if (readit || setit || adjustit) - { -/* - * mktime() assumes we're giving it local time. If the CMOS clock - * is in GMT, we have to set up TZ so mktime knows it. tzset() gets - * called implicitly by the time code, but only the first time. When - * changing the environment variable, better call tzset() explicitly. - */ - if (universal) - { - char *zone; - zone = (char *) getenv ("TZ"); /* save original time zone */ - (void) putenv ("TZ="); - tzset (); - systime = mktime (&tm); - /* now put back the original zone */ - if (zone) - { - - char *zonebuf; - zonebuf = malloc (strlen (zone) + 4); - strcpy (zonebuf, "TZ="); - strcpy (zonebuf+3, zone); - putenv (zonebuf); - free (zonebuf); - } - else - { /* wasn't one, so clear it */ - putenv ("TZ"); - } - tzset (); - } - else - { - systime = mktime (&tm); - } - if (debug) printf ("Number of seconds since 1/1/1970 is %d\n", systime); - } - - if (readit) - { - printf ("%s", ctime (&systime )); - } - - if (setit || adjustit) - { - struct timeval tv; - struct timezone tz; - -/* program is designed to run setuid, be secure! */ - - if (getuid () != 0) - { - fprintf (stderr, "Sorry, must be root to set or adjust time\n"); - exit (2); - } - - if (adjustit) - { /* the actual adjustment */ - double exact_adjustment; - - exact_adjustment = ((double) (systime - last_time)) - * factor / (24 * 60 * 60) - + not_adjusted; - if (exact_adjustment > 0) - adjustment = (int) (exact_adjustment + 0.5); - else - adjustment = (int) (exact_adjustment - 0.5); - not_adjusted = exact_adjustment - (double) adjustment; - systime += adjustment; - if (debug) { - printf ("Time since last adjustment is %d seconds\n", - (int) (systime - last_time)); - printf ("Adjusting time by %d seconds\n", - adjustment); - printf ("remaining adjustment is %.3f seconds\n", - not_adjusted); - } - } -#ifndef KEEP_OFF - tv.tv_sec = systime; - tv.tv_usec = 0; - tz.tz_minuteswest = timezone / 60; - tz.tz_dsttime = daylight; - - if (settimeofday (&tv, &tz) != 0) - { - fprintf (stderr, - "Unable to set time -- probably you are not root\n"); - exit (1); - } - - if (debug) { - printf( "Called settimeofday:\n" ); - printf( "\ttv.tv_sec = %ld, tv.tv_usec = %ld\n", - tv.tv_sec, tv.tv_usec ); - printf( "\ttz.tz_minuteswest = %d, tz.tz_dsttime = %d\n", - tz.tz_minuteswest, tz.tz_dsttime ); - } -#endif - } - - if (writeit || (adjustit && adjustment != 0)) - { - struct tm *tmp; - systime = time (NULL); - if (universal) - tmp = gmtime (&systime); - else - tmp = localtime (&systime); - -#ifndef KEEP_OFF - __asm__ volatile ("cli"); - save_control = cmos_read (11); /* tell the clock it's being set */ - cmos_write (11, (save_control | 0x80)); - save_freq_select = cmos_read (10); /* stop and reset prescaler */ - cmos_write (10, (save_freq_select | 0x70)); - - cmos_write_bcd (0, tmp->tm_sec); - cmos_write_bcd (2, tmp->tm_min); - cmos_write_bcd (4, tmp->tm_hour); - cmos_write_bcd (6, tmp->tm_wday + 3); - cmos_write_bcd (7, tmp->tm_mday); - cmos_write_bcd (8, tmp->tm_mon + 1); - cmos_write_bcd (9, tmp->tm_year); - - cmos_write (10, save_freq_select); - cmos_write (11, save_control); - __asm__ volatile ("sti"); -#endif - if (debug) printf ("Set to : %d:%d:%d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec); - } - else - if (debug) printf ("CMOS clock unchanged.\n"); - /* Save data for next 'adjustit' call */ - if (adjustit) - { - FILE *adj; - if ((adj = fopen (ADJPATH, "w")) == NULL) - { - perror (ADJPATH); - exit (2); - } - fprintf (adj, "%f %d %f\n", factor, systime, not_adjusted); - fclose (adj); - } - exit (0); -} |