elks-enhanced
public
Read
Owner: themaster
Branch: master
Commits: 6893
Updated: 2026-04-19 00:15
Git CLI clone URL
git clone https://www.xt-emporium.com/git/elks-enhanced.git
Fullscreen desktop URL
Code
Commits
History
Branches
Bug Reports
Discussions
Compare
Settings
elks-enhanced
/
elkscmd
/
busyelks
/
cmd
/
clock.c
File editor
#include <stdio.h> #include <stdlib.h> #include <getopt.h> #include <errno.h> #include <unistd.h> #include <time.h> #include <sys/time.h> #include <string.h> /* 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. * ****** * V1.4.ELKS * * Converted by Shane Kerr <kerr@wizard.net>, 1998-01-03 * * Removed the adjustment option, because it used floating * point math, and because I don't use it. If necessary, * it can certainly be added back in. * * Deleted the commented out options for debug code, because * they were cluttering up this already cluttered source file. * * Switched from using mktime(), with the associated time zone * baggage, to the hand-crafted utc_mktime(), getting time * zone information from gettimeofday() rather than the * environment variables. * * Removed the code to set the timezone with settimeofday. * This should probably be added back in at some point... * * I had to remove the code that waits for the falling edge * of the update flag -- it never seems to actually happen! * And since the long code is so slow, the program just locks. */ #define VERSION "1.4.ELKS" /* Globals */ int readit = 0; int writeit = 0; int setit = 0; int universal = 0; void clock_usage() { fprintf(stderr, "clock [-u] -r|w|s|v\n"); fprintf(stderr, " r: read and print CMOS clock\n"); fprintf(stderr, " w: write CMOS clock from system time\n"); fprintf(stderr, " s: set system time from CMOS clock\n"); fprintf(stderr, " u: CMOS clock is in universal time\n"); fprintf(stderr, " v: print version (%s) and exit\n", VERSION); exit(1); } #asm _inb: push bp mov bp, sp push dx xor ax, ax mov dx, 4[bp] in al, dx pop dx pop bp ret #endasm #asm _outb: push bp mov bp, sp push ax push dx xor ax, ax mov al, 4[bp] mov dx, 6[bp] out dx, al pop dx pop ax pop bp ret #endasm unsigned char cmos_read(reg) unsigned char reg; { register unsigned char ret; #asm cli #endasm outb (reg | 0x80, 0x70); ret = inb (0x71); #asm sti #endasm return ret; } void cmos_write(reg, val) unsigned char reg; unsigned char val; { outb (reg | 0x80, 0x70); outb (val, 0x71); } int cmos_read_bcd (addr) int addr; { int b; b = cmos_read (addr); return (b & 15) + (b >> 4) * 10; } void cmos_write_bcd (addr, value) int addr; int value; { cmos_write (addr, ((value / 10) << 4) + value % 10); } int clock_main (argc, argv) int argc; char **argv; { struct tm tm; time_t systime; time_t last_time; int arg; double factor; double not_adjusted; 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 'v': fprintf(stderr, "clock %s\n", VERSION); exit(0); default: usage (); } } if (readit + writeit + setit > 1) usage (); /* only allow one of these */ if (!(readit | writeit | setit )) /* default to read */ readit = 1; if (readit || setit) { #if 0 long i; /* read RTC exactly on falling edge of update flag */ /* Wait for rise.... (may take upto 1 second) */ for (i = 0; i < 10000000L; i++) if (cmos_read (10) & 0x80) break; /* Wait for fall.... (must try at least 2.228 ms) */ for (i = 0; i < 1000000L; i++) if (!(cmos_read (10) & 0x80)) break; #endif /* 0 */ /* 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 (readit || setit) { /* * utc_mktime() assumes we're in Greenwich, England. If the CMOS * clock isn't in GMT, we need to adjust. We'll just use the * time zone information returned in gettimeofday(). */ systime = utc_mktime(&tm); if (!universal) { struct timezone tz; gettimeofday(NULL, &tz); systime += tz.tz_minuteswest * 60L; } #if 0 /* * 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); } #endif /* 0 */ } if (readit) { printf ("%s", ctime (&systime )); } if (setit) { 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 time\n"); exit (2); } #ifndef KEEP_OFF tv.tv_sec = systime; tv.tv_usec = 0; #if 0 tz.tz_minuteswest = timezone / 60; tz.tz_dsttime = daylight; if (settimeofday (&tv, &tz) != 0) #else if (settimeofday (&tv, NULL) != 0) #endif { fprintf (stderr, "Unable to set time -- probably you are not root\n"); exit (1); } #endif } if (writeit) { struct tm *tmp; systime = time (NULL); if (universal) tmp = gmtime (&systime); else tmp = localtime (&systime); #ifndef KEEP_OFF #asm cli #endasm 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 sti #endasm #endif } exit (0); }
Commit message
This repository is read-only for this account.
Repository snapshot
Current branch
master
Visibility
public
Your access
Read
Remote
Configured
File activity
View file history