home *** CD-ROM | disk | FTP | other *** search
- From: turner@ksr.com (James M. Turner)
- Newsgroups: comp.protocols.time.ntp,alt.sources
- Subject: Set time from WWV clock
- Message-ID: <2967@litchi.bbn.com>
- Date: 6 Nov 90 13:52:38 GMT
-
- [ NOTE: RICH SALZ DID NOT WRITE THIS; HE IS JUST POSTING IT!!! ]
-
- After I finally got sick of hand-setting my master-machine to the
- talking clock, and decided I wanted a little more accuracy and
- automation, I wrote the following. It works under SunOS 4.0.3, no
- idea how it will do on other machines. If you get it working on a new
- architecture, send the fixes to me and I'll incorporate them in the next
- release.
-
- Place the file below in utcclock.c. Instructions for compilation and
- use are in the file itself.
- ------------------------------ Cut Here ------------------------------
-
- /* UTCCLOCK.C - Set time from US naval Observatory via modem
- Copyright (C) 1990, James M. Turner
-
- ***
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- ***
-
- utcclock allows us mortals without cessium clocks and WWV radio
- receivers to keep our time reasonable close to UTC.
-
- Experiments on a Sun 3/60 had local time within 50 milliseconds of
- UTC as provided by the US Naval Observatory. Not perfect, but better than
- trying to set it by hand.
-
- Usage: utcclock [-S] [-s speed] [-l device] [-n number]
-
- Where speed is the baud rate to use, device is the device to call on,
- and number is the phone number to dial. If the -S option is used, the
- time will be set, otherwise only the delta difference is reported. You
- must be root to set the time. Because it uses the UUCP locking mechanism,
- it needs write permission to /usr/spool/uucp. I installed it setuid with
- owner = uucp on my system. If you install it setuid to root, anyone will
- be able to set the time.
-
- On SunOS 4.0.3, compile with:
-
- cc -O -o utcclock utcclock.c
-
- I suggest installing it in /usr/local/etc
-
- Have fun, and happy ticking.
-
- James Turner
- (turner@ksr.com)
- 617-895-9480
-
- $Log: utcclock.c,v $
- * Revision 1.4 90/11/05 16:27:56 turner
- * Added GPL notice
- *
- * Revision 1.3 90/11/05 16:06:06 turner
- * Added exclusive locking of device, better leap-year computation.
- *
- */
-
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/timeb.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <signal.h>
- #include <termio.h>
-
- char version[] = "$Id: utcclock.c,v 1.4 90/11/05 16:27:56 turner Exp $";
-
- /* PLEASE CHECK AND SET THE VALUES BELOW THIS LINE !!! */
-
- /* Default device to use. Should be exclusive-locking if possible */
- #define TTYPORT "cua0"
-
- /* Phone number to dial. Please verify that you receive tone on this number
- (we wouldnt want to harrass little old ladies with modem tones, now would
- we?)
- */
- #define NUMBER "12026530351" /* US Naval Observatory */
-
- /* Format of string to send to modem with number in %s */
- #define DIALSTRING "ATDT%s\r"
-
- /* Default baud rate to use on line */
- #define BAUDRATE B1200
-
- /* Number of seconds of data to read */
- #define NCOUNT 5
-
- /* Locking file for UUCP. %s will be replaced by "ntp" and the device name */
- #define LOCKFILE "/usr/spool/uucp/LCK..%s"
-
- /* END OF USER CONFIGURABLE SECTION */
-
- #define FIRST_YEAR 1988
- #define FIRST_VALUE 47160 /* The day value of Dec 31, FIRST_YEAR-1 */
-
- int months[] = {
- 31, 28,31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-
- int modem; /* Device Descriptor for Modem Line */
- char *timeout_reason=NULL; /* If we timeout, where are we in the code? */
-
- char lck1[80], lck2[80];
-
- /* This code handles the cleanup (shutting down the modem) if you
- abort for some reason */
-
- void
- exit_handler(sig, code, scp, addr)
- int sig, code;
- struct sigcontext *scp;
- char *addr;
- {
- int flags = (TIOCM_DTR | TIOCM_LE);
- ioctl(modem, TIOCMBIC, &flags);
- if (sig == SIGALRM)
- printf("utcclock: timeout while %s\n", timeout_reason);
- if (timeout_reason != NULL) {
- unlink(lck1);
- unlink(lck2);
- }
- exit(0);
- }
-
- struct sigvec vec = {
- exit_handler,
- 0,
- 0};
-
- /* Returns number of days in the given year.
- * Remember, a leap year is a year which is evenly divisible by 4,
- * but not a century, unless it is a century divisible by 400
- */
- static
- daysinyear(thisyear)
- int thisyear;
- {
- if ((thisyear % 4) == 0 && (((thisyear % 100) != 0) ||
- ((thisyear % 400) == 0)))
- return(366);
- return(365);
- }
-
- main(argc, argv)
- int argc;
- char **argv;
- {
- extern char *optarg;
- extern int optind;
- int fd1, flags;
- struct sigvec ovec;
- struct termio tty;
- char c, *OptLine, *OptNumber, option, device[50];
- struct tm tm;
- struct timeval del, del1;
- struct timezone tzp;
- struct timeb tp;
- time_t i;
- long utc_time;
- int hh, mm, ss, dd, yy, dummy, milli[3], m, n = 0, r, k=0, des,
- yydd, year, month, OptSetme = 0, OptSpeed = BAUDRATE;
- char buffer[80];
-
-
- /* Make sure that timeouts and terminations clean up after themselves */
- if (sigvec(SIGTERM, &vec, &ovec) != 0) {
- perror("Sigvec:");
- exit(-1);
- }
- if (sigvec(SIGINT, &vec, &ovec) != 0) {
- perror("Sigvec:");
- exit(-1);
- }
- if (sigvec(SIGALRM, &vec, &ovec) != 0) {
- perror("Sigvec:");
- exit(-1);
- }
-
- OptLine = TTYPORT;
- OptNumber = NUMBER;
-
- /* Read in the options */
- while ((option = getopt(argc, argv, "Sl:s:n:")) != -1) {
- switch (option) {
- case 'S':
- OptSetme = 1;
- break;
- case 'l':
- OptLine = optarg;
- break;
- case 'n':
- OptNumber = optarg;
- break;
- case 's':
- OptSpeed = atoi(optarg);
- switch (OptSpeed) {
- case 300:
- OptSpeed = B300;
- break;
- case 1200:
- OptSpeed = B1200;
- break;
- case 2400:
- OptSpeed = B2400;
- break;
- case 4800:
- OptSpeed = B4800;
- break;
- case 9600:
- OptSpeed = B9600;
- break;
- case 19200:
- OptSpeed = B19200;
- break;
- otherwise:
- fprintf(stderr, "utcclock: Illegal baud rate\n");
- exit(2);
- }
- break;
- case '?':
- fprintf(stderr, "Usage: utcclock [-S] [-s speed] [-l line] [n number]\n");
- exit(2);
- break;
- }
- }
-
- sprintf(device, "/dev/%s", OptLine);
- /* 60 Seconds to get the mess worked out */
- alarm(60);
- sprintf(lck1, LOCKFILE, "ntp");
- sprintf(lck2, LOCKFILE, OptLine);
-
- if ((n = open(lck1, O_CREAT|O_EXCL)) == -1) {
- fprintf(stderr, "utcclock: utcclock already running\n");
- exit(2);
- }
- close(n);
- if ((m = open(lck2, O_CREAT|O_EXCL)) == -1) {
- fprintf(stderr, "utcclock: port in use\n");
- unlink(lck1);
- exit(2);
- }
- close(m);
-
- timeout_reason = "opening device";
- if ((modem = open(device, O_RDWR|O_NDELAY|O_EXCL)) == -1) {
- perror("utcclock");
- exit(2);
- }
- timeout_reason = "setting line parameters";
- if (ioctl(des, TCGETA, &tty) == 0) {
- tty.c_cflag &= ~CBAUD;
- tty.c_cflag |= (CLOCAL | OptSpeed);
- }
- ioctl(des, TCSETA, &tty);
- fcntl(modem,F_SETFL,0); /* Turn off NDELAY. */
- /* Mom says we have to reopen for effect */
- fd1 = open(device, O_RDWR|O_EXCL);
- close(modem); /* Close old line */
- modem = fd1;
-
- /* dial utc */
- timeout_reason = "dialing";
- ioctl(modem, TCFLSH, 0); /* Flush any old characters */
- sprintf(buffer, DIALSTRING, OptNumber); /* Change for non-hayes */
- write(modem, buffer, strlen(buffer));
-
- /* Now we read characters until we see the first * from the remote
- side. I hope your modem doesn't report messages with a *
- */
- timeout_reason = "Looking for *";
- for (n = 0, buffer[0] = ' '; (n > -1), (buffer[0] != '*');
- n = read(modem, buffer, 1));
- if (n == -1) exit(2);
-
- yy = 0; /* Set year = 0 */
- n = 0; /* Set character counter = 0 */
- while (k < NCOUNT) { /* Until we get NCOUNT seconds of data */
- i = read(modem, buffer+n, 1); /* Read a character */
- if (i <= 0) exit(-1); /* On EOF, punt */
- if (buffer[n] == '*') { /* If second-marker */
- if (yy != 0) { /* Have we got legal data? */
- if (k == (NCOUNT-1)) { /* And have we seen NCOUNT seconds? */
- ftime(&tp); /* Get our local time for delta */
- if (OptSetme) { /* If we set time */
- r = settimeofday(&del1, &tzp); /* Set to calculated value */
- if (r < 0) /* Did we get an error? */
- perror("settimeofday");
- }
- if (del1.tv_sec > tp.time) { /* If value is negative */
- if (tp.millitm != 0) { /* And some part of it is millitm */
- tp.time++; /* Then delta is one second less */
- tp.millitm = 1000 - tp.millitm; /* Plus 1.0 - millitm */
- }
- printf("Our clock slow by %d.%03d seconds\n",
- del1.tv_sec - tp.time, tp.millitm);
- } else {
- printf("Our clock fast by %d.%03d seconds\n",
- tp.time - del1.tv_sec, tp.millitm);
- }
- printf("Universal Coordinated Time exactly %d/%d/%d %d:%02d:%02d\n",
- month, dd, yy, hh, mm, ss);
- }
- /* If we aren't ready to set the time yet, add one to the timestamp
- and one to the good marker count */
- del1.tv_sec++;
- k++;
- }
- } else {
- /* If we got a CR, we figure out the time sent */
- if (buffer[n] == '\n' || buffer[n] == 'r') {
- buffer[n] = 0; /* Terminate buffer */
- /* If we haven't read a good value, and there are characters in the
- buffer, and the string parses correctly, then calculate time
- */
- if ((yy == 0) &&
- (n != 0) &&
- (sscanf(buffer, "%d %d %2d%2d%2d UTC", &yydd, &dd, &hh, &mm, &ss)
- == 5)) {
-
- yydd -= FIRST_VALUE; /* Subtract day value of Dec 31st */
- for (year = FIRST_YEAR; (yydd > daysinyear(year)); year++)
- /* Decrement until under 1 year left */
- yydd -= daysinyear(year);
- yy = year - 1900; /* Set year number */
- if (daysinyear(year) == 366)
- months[1] = 29; /* Handle leap year */
- for (month = 0; (dd > months[month]); month++)
- dd -= months[month];
-
- tm.tm_sec = ss; /* Stuff seconds in second field */
- tm.tm_min = mm; /* Stuff minutes in minute field */
- tm.tm_hour = hh; /* Stuff hours in hour field */
- tm.tm_year = yy; /* Stuff year in year field */
- tm.tm_mday = dd; /* Stuff day of month */
- tm.tm_mon = month; /* Stuff month */
- utc_time = ((long)timegm(&tm)); /* Get gmtime version of time */
- gettimeofday(&del1, &tzp); /* Get time zone information */
- del1.tv_usec = 0; /* Always exactly on second */
- del1.tv_sec = utc_time; /* * is for *NEXT* second */
- }
- n = 0; /* Reset character counter */
- } else {
- n++; /* Store character */
- buffer[n] = 0; /* Terminate */
- }
- }
- }
- flags = TIOCM_DTR; /* Clean up after success */
- ioctl(modem, TIOCMBIC, &flags);
- unlink(lck1);
- unlink(lck2);
- exit(0);
- }
- --
- Name: James M. Turner * Great Moments in Aviation #21: While on a
- Company: Kendall Square Research * NDB approach into Hanscom, pilot Ted Hertz
- Email: turner@ksr.com, ksr!turner * accidently tunes WEEI on his NAV instead
- Phone: (617) 895-9400 * of the LOM, and lands on top of the Pru.
-