home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: aus.sources
- Path: sparky!uunet!zaphod.mps.ohio-state.edu!darwin.sura.net!spool.mu.edu!umn.edu!math.fu-berlin.de!informatik.tu-muenchen.de!regent!monu1.cc.monash.edu.au!monu6!bruce.cs.monash.edu.au!munnari.oz.au!bunyip.cc.uq.oz.au!kirk!ptcburp!michi
- From: michi@ptcburp.ptcbu.oz.au (Michael Henning)
- Subject: AOTC atomic clock
- Message-ID: <1992Dec22.041148.24383@ptcburp.ptcbu.oz.au>
- Summary: Code to set system date automatically
- Keywords: AOTC, time, date, system time, clock
- Organization: Pyramid Technology Corporation
- Distribution: aus
- Date: Tue, 22 Dec 1992 04:11:48 GMT
- Lines: 509
-
-
- I've just written a hack to automatically set the system time on our
- machine from AOTC's atomic clock. For those who don't know, AOTC offer
- a time service at 1200 Baud. The number for Brisbane is (07) 221-7033.
- I don't know about other cities, but I guess directory assistance
- may be able to help.
-
- I am using the MHSnet VCcall program to invoke the code that
- sets the system time (details below).
- If you don't have MHSnet, ACSnet will work if you write yourself
- a call script that invokes the setclock program.
- If you can get your UUCP to invoke a program from a chat script,
- you should be able to use that too.
-
- Anyway, below is some code that can set the time for you. I've included
- some explanations for how to configure it into MHSnet. You should be
- able to get going with that. The usual disclaimers apply. If my code
- sets your machine on fire - tough luck.
-
- The whole process from matching the date string (at 1200 Baud) to finally
- setting the system clock takes less than 0.75 seconds on our machine.
- For our purposes, that is accurate enough.
-
- Cheers,
-
- Michi.
-
-
- To get MHSnet to make the call for you, you need a link for which to
- establish a call. The easiest way to do this is to add a pseudo host
- and create a link to it. The following entries in _state/commandsfile
- do just that.
-
- #
- # Pseudo host to use the MHSnet modem dialer to set the time of day.
- #
- add N=setclock.O=ptcbu.P=oz.C=au
- link N=ptcburp.O=ptcbu.P=oz.C=au,
- N=setclock.O=ptcbu.P=oz.C=au
- +foreign,+nochange,+dead
- linkname N=setclock.O=ptcbu.P=oz.C=au
- setclock
-
- The host is called "setclock" and placed into the local domain. The
- +foreign,+nochange,+dead flags prevent any messages from being sent
- to that host. Please use your machine and domain to do this, not
- ptcburp.ptcbu.oz.au :-)
-
- Do a "netstate -wrsxC" after changing your commandsfile.
-
- Next, add an entry to _lib/initfile:
-
- call-setclock.ptcbu.oz.au "49 8 * * 1" setclock/call
-
- This tries to establish a connection to the setclock host once a week.
-
- In $SPOOLDIR, create a directory "setclock", owned by daemon, group daemon,
- with permissions 750.
-
- Create a file called "call", owned by daemon, group daemon, with
- permissions 750. It contains the following script:
-
-
- #!/bin/sh
-
- DEVICE=/dev/term/0
- TRACE=0
- if [ "$TRACE" -gt 0 ]
- then
- mdmtrace='M1S61=255' # Switch speaker on
- else
- mdmtrace='M0' # Be silent
- fi
- /usr/spool/MHSnet/_lib/VCcall \
- -T${TRACE} \
- -v \
- -D "dialstr=AT${mdmtrace}S50=3DT0,07,2217033" \
- -D "linespeed=19200" \
- -D "modemdev=$DEVICE" \
- -D "timecmd=/usr/local/bin/setclock" \
- -S setclock.cs \
- -X /usr/spool/MHSnet/setclock/call.log \
- setclock.ptcbu.oz.au
- exit $?
-
- This simply invokes VCcall with the appropriate arguments.
- The dial string here is for a Trailblazer, so you need to adjust it if
- you have a different modem.
-
-
- The call script that calls the clock server and picks out the date
- string goes into _call/setclock.cs. I modified one of the generic
- call scripts which come with MHSnet to invoke a program to set the date.
-
-
- /*
- ** Call script to set the system time.
- **
- ** Imports:
- ** dialstr string to cause modem to dial remote site
- ** linespeed speed for modemdev
- ** modemdev device attached to modem
- ** timecmd command to set time
- */
-
- set dialcount 6; /* dial attempts */
- set timecount 10; /* number of tries at reading time */
-
- /*
- ** Check imported strings.
- */
-
- chkargs:
- match dialstr UNDEFINED paramerr;
- match linespeed UNDEFINED paramerr;
- match modemdev UNDEFINED paramerr;
-
- start1:
- timeout 60; /* Wait 1 minute between/for open attempts */
- retry 60; /* Allow 60 attempts at open => 1 hour max */
- open "dial" modemdev "uucplock" "ondelay" "local"; /* Open with O_NDELAY */
- match RESULT DEVOK openok;
- set reason "Could not open \"" modemdev "\" reason: " RESULT;
-
- openfail:
- fail reason;
-
- paramerr:
- fail "missing some of:\n"
- "\t-D \"dialstr=<ATD command for modem>\"\n"
- "\t-D \"linespeed=<bits/second>\"\n"
- "\t-D \"modemdev=<device pathname for modem>\""
- ;
-
- openok:
- device "offdelay"; /* Otherwise reads will return 0 */
- device "speed" linespeed;
- monitor 0; /* Turn on I/O tracing */
-
- sleep 1; /* Because some terminal i/o is slow! */
- write "AT\r"; /* Make sure modem latches on */
- sleep 1;
- write "AT\r";
- sleep 1;
- write "AT\r";
- sleep 1;
- device "flush";
- write "ATZ\r";
- timeout 10;
- expect
- "OK" try_initstr,
- EOF eof,
- TERMINATE terminate,
- TIMEOUT rstmodem;
-
- rstmodem:
- sleep 2;
- write "+++";
- clrmodem:
- sleep 2;
- write "ATZ\r";
- timeout 10;
- expect
- "OK" try_initstr,
- EOF eof,
- TERMINATE terminate,
- TIMEOUT nomodem;
-
- try_initstr:
- match initstr UNDEFINED dialing;
- timeout 0;
- sleep 1;
- write initstr "\r";
- timeout 10;
- expect
- "OK" dialing,
- EOF eof,
- TERMINATE terminate,
- TIMEOUT dialing;
-
- nomodem:
- set reason "modem \"" modemdev "\" not responding";
- next nextmodem;
-
- dialing:
- test dialcount dfail;
- timeout 0;
- sleep 1;
- write dialstr "\r";
- timeout 60;
- expect
- "BUSY" busy,
- "NO ANSWER" busy,
- "NO CARRIER" clrmodem,
- "NO DIALTONE" nodial,
- "CONNECT 2400" gettime,
- "CONNECT 1200" gettime,
- "CONNECT" gettime,
- EOF eof,
- TERMINATE terminate,
- TIMEOUT rstmodem;
-
- busy:
- trace "number \"" dialstr "\" busy";
- sleep 30; /* Give them time to get off the phone */
- next clrmodem;
-
- gettime:
- set DELIMCHARS "\n\r";
- timeout 4;
- try_again:
- test timecount no_time;
- device "flush";
- expect
- "[12][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9][ ][0-2][0-9]:[0-5][0-9]:[0-5][0-9]\*" settime,
- TIMEOUT try_again,
- TERMINATE terminate,
- EOF endmodem2;
-
- settime:
- shell timecmd " '"INPUT"'";
- match RESULT "0" endmodem;
- match RESULT "1" bad_format;
- match RESULT "2" bad_date;
- match RESULT "3" too_large;
- match RESULT "4" stime_failed;
- set reason timecmd " '"INPUT"' returned weird exit status: " RESULT;
-
- endmodem:
- device "local"; /* Ignore modem signals */
- sleep 2;
- device "flush";
- write "+++";
- timeout 3;
- expect
- "NO CARRIER" endmodem1,
- "OK" endmodem1,
- EOF endmodem2,
- TERMINATE endmodem0,
- TIMEOUT endmodem1;
-
- endmodem0:
- sleep 2;
- endmodem1:
- write "ATH0Z\r";
- timeout 2;
- expect
- "OK" endmodem2,
- EOF endmodem2,
- TERMINATE endmodem2,
- TIMEOUT endmodem2;
-
- endmodem2:
- match finishstr UNDEFINED endmodem3;
- timeout 0;
- write finishstr "\r";
- sleep 1;
- endmodem3:
- match reason UNDEFINED out;
- fail reason;
- out:
- return;
-
- dfail:
- set reason "too many dial attempts";
- next endmodem;
-
- eof:
- set reason "remote disconnect";
- next endmodem;
-
- nodial:
- set reason "modem \"" modemdev "\" has no dialtone";
- match modemdev2 UNDEFINED endmodem;
- next nextmodem;
-
- terminate:
- set reason "system termination";
- next endmodem;
-
- no_time:
- set reason "cannot get time from connection";
- next endmodem;
-
- bad_format:
- set reason timecmd " '"INPUT"': bad format";
- next endmodem;
-
- bad_date:
- set reason timecmd " '"INPUT"': meaningless date";
- next endmodem;
-
- too_large:
- set reason timecmd " '"INPUT"': time difference too large";
- next endmodem;
-
- stime_failed:
- set reason timecmd " '"INPUT"': stime(2) call failed";
- next endmodem;
-
-
- Finally, you need to compile and install the code below (set-uid root).
- It sets the new date after doing some sanity checks. I added a limit
- to prevent the system time being changed by more than 300 seconds.
- I figure if my clock is wrong by as much as that, I'd like to know why :-)
-
- If you don't call the program /usr/local/bin/setclock, remember to
- change the path passed to the call script from setclock/call.
-
-
- /*
- * setclock - set the system time using AOTC's atomic clock service.
- *
- * This program is meant to be invoked from a daemon which obtains
- * the current date from AOTC's dial-up atomic clock. The clock produces
- * lines of the form
- *
- * yyyy-mm-dd hh:mm:ss[*#]
- *
- * The output from the dial-up clock looks like
- *
- * 1992-12-10 10:01:40*
- * 1992-12-10 10:01:41*
- * 1992-12-10 10:01:42*
- * 1992-12-10 10:01:43*
- * 1992-12-10 10:01:44*
- *
- * Each line is printed up to the '*', followed by a brief pause,
- * followed by the '*', CR and LF. The exact mark for the time is
- * the stop-bit/start-bit transition between the CR and LF.
- *
- * For daylight saving time, the '*' is replaced by a '#'.
- *
- * The single argument must be one complete line. Any trailing characters
- * (such as CR/LF) after the '*' or '#' are ignored.
- *
- * The argument is broken into its components, sanity checked and
- * converted into seconds since The Epoch. If the resulting time does
- * not differ from the current system time by more than MAX_ADJ seconds,
- * the system clock is set to the new time.
- *
- * This program must be set-uid root, and should be installed with
- * permissions 4710, owner root, group daemon.
- */
-
- /*****************************************************************************/
-
- #include <stdlib.h>
- #include <unistd.h>
- #include <libgen.h>
- #include <string.h>
- #include <time.h>
-
- /*****************************************************************************/
-
- #define FORMAT "yyyy-mm-dd hh:mm:ss" /* Format of input date */
- #define USAGE "Usage: %s \"%s[*#]\"\n" /* Usage message */
-
- #define MAX_ADJ 300 /* Max adjust in seconds */
-
- #define OK 0 /* Exit status if OK */
- #define BAD_FORM 1 /* Bad format of argument */
- #define BAD_DATE 2 /* Meaningless date */
- #define TOO_LARGE 3 /* MAX_ADJ exceeded */
- #define BAD_STIME 4 /* stime(2) failed */
-
- /*****************************************************************************/
-
- static const char *progname; /* For error messages */
-
- /*****************************************************************************/
-
- /*
- * Print usage message and exit. If the given date is non-NULL,
- * also complain about an invalid date, showing the invalid string.
- */
-
- static void
- usage(int status, const char *date)
- {
- if (date != NULL) {
- (void) fprintf(
- stderr, "%s: invalid date: \"%s\"\n", progname, date
- );
- }
- (void) fprintf(stderr, USAGE, progname, FORMAT);
- exit(status);
- }
-
- /*****************************************************************************/
-
- int
- main(int argc, char *argv[])
- {
- char buf[sizeof(FORMAT) + 1]; /* +1 for '*' or '#' */
- char *year;
- char *month;
- char *day;
- char *hour;
- char *minute;
- char *second;
- struct tm stm;
- time_t new_time;
-
- /*
- * Set program name for error messages.
- */
- progname = basename(argv[0]);
- /*
- * Check usage.
- */
- if (argc != 2)
- usage(BAD_FORM, NULL);
- /*
- * Get rid of any trailing garbage in argument (the AOTC atomic
- * clock service terminates each line with CR/LF). We clean this
- * up in argv[1] instead of a copy of it, so we can print nicer
- * error messages (without any trailing newline).
- */
- /* LINTED */
- if (strlen(argv[1]) < strlen(FORMAT) + 1)
- usage(BAD_FORM, argv[1]);
- argv[1][strlen(FORMAT) + 1] = '\0';
- /*
- * Break up the date into components.
- */
- (void) strncpy(buf, argv[1], strlen(FORMAT) + 1);
- year = buf;
- if ((month = strchr(year, '-')) == NULL)
- usage(BAD_FORM, argv[1]);
- *month++ = '\0';
- if ((day = strchr(month, '-')) == NULL)
- usage(BAD_FORM, argv[1]);
- *day++ = '\0';
- if ((hour = strchr(day, ' ')) == NULL)
- usage(BAD_FORM, argv[1]);
- *hour++ = '\0';
- if ((minute = strchr(hour, ':')) == NULL)
- usage(BAD_FORM, argv[1]);
- *minute++ = '\0';
- if ((second = strchr(minute, ':')) == NULL)
- usage(BAD_FORM, argv[1]);
- *second++ = '\0';
- /*
- * Check fields for validity and build struct tm.
- */
- if (second[2] != '*' && second[2] != '#')
- usage(BAD_FORM, argv[1]);
- stm.tm_isdst = second[2] == '#';
- second[2] = '\0';
- stm.tm_year = atoi(year);
- if (stm.tm_year < 1900)
- usage(BAD_DATE, argv[1]);
- stm.tm_year -= 1900; /* Origin is the year 1900 */
- stm.tm_mon = atoi(month);
- if (stm.tm_mon < 1 || stm.tm_mon > 12)
- usage(BAD_DATE, argv[1]);
- stm.tm_mon--; /* Origin is 0 (January) */
- stm.tm_mday = atoi(day);
- if (stm.tm_mday < 1 || stm.tm_mday > 31)
- usage(BAD_DATE, argv[1]);
- stm.tm_hour = atoi(hour);
- if (stm.tm_hour < 0 || stm.tm_hour > 23)
- usage(BAD_DATE, argv[1]);
- stm.tm_min = atoi(minute);
- if (stm.tm_min < 0 || stm.tm_min > 59)
- usage(BAD_DATE, argv[1]);
- stm.tm_sec = atoi(second);
- if (stm.tm_sec < 0 || stm.tm_sec > 59)
- usage(BAD_DATE, argv[1]);
- /*
- * Convert time to seconds since The Epoch.
- */
- if ((new_time = mktime(&stm)) == -1)
- usage(BAD_DATE, argv[1]);
- /*
- * Don't allow adjustment of time by more than MAX_ADJ seconds.
- */
- if (abs(time(NULL) - new_time) > MAX_ADJ) {
- (void) fprintf(
- stderr,
- "%s: will not adjust time by more than %d seconds\n",
- progname, MAX_ADJ
- );
- exit(TOO_LARGE);
- }
- /*
- * Set the system time.
- */
- if (stime(&new_time) == -1) {
- extern int errno;
-
- (void) fprintf(
- stderr, "%s: cannot set time: %s\n",
- progname, strerror(errno)
- );
- exit(BAD_STIME);
- }
- /*
- * Done.
- */
- exit(OK);
- /* NOTREACHED */
- }
- --
- -m------- Michael Henning +61 75 950255
- ---mmm----- Pyramid Technology +61 75 722475 FAX
- -----mmmmm--- Research Park, Bond University michi@ptcburp.ptcbu.oz.au
- -------mmmmmmm- Gold Coast, Q 4229, AUSTRALIA uunet!munnari!ptcburp.oz!michi
-