home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #31 / NN_1992_31.iso / spool / aus / sources / 32 next >
Encoding:
Text File  |  1992-12-22  |  13.3 KB  |  522 lines

  1. Newsgroups: aus.sources
  2. 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
  3. From: michi@ptcburp.ptcbu.oz.au (Michael Henning)
  4. Subject: AOTC atomic clock
  5. Message-ID: <1992Dec22.041148.24383@ptcburp.ptcbu.oz.au>
  6. Summary: Code to set system date automatically
  7. Keywords: AOTC, time, date, system time, clock
  8. Organization: Pyramid Technology Corporation
  9. Distribution: aus
  10. Date: Tue, 22 Dec 1992 04:11:48 GMT
  11. Lines: 509
  12.  
  13.  
  14. I've just written a hack to automatically set the system time on our
  15. machine from AOTC's atomic clock. For those who don't know, AOTC offer
  16. a time service at 1200 Baud. The number for Brisbane is (07) 221-7033.
  17. I don't know about other cities, but I guess directory assistance
  18. may be able to help.
  19.  
  20. I am using the MHSnet VCcall program to invoke the code that
  21. sets the system time (details below).
  22. If you don't have MHSnet, ACSnet will work if you write yourself
  23. a call script that invokes the setclock program.
  24. If you can get your UUCP to invoke a program from a chat script,
  25. you should be able to use that too.
  26.  
  27. Anyway, below is some code that can set the time for you. I've included
  28. some explanations for how to configure it into MHSnet. You should be
  29. able to get going with that. The usual disclaimers apply. If my code
  30. sets your machine on fire - tough luck.
  31.  
  32. The whole process from matching the date string (at 1200 Baud) to finally
  33. setting the system clock takes less than 0.75 seconds on our machine.
  34. For our purposes, that is accurate enough.
  35.  
  36.                             Cheers,
  37.  
  38.                                 Michi.
  39.  
  40.  
  41. To get MHSnet to make the call for you, you need a link for which to
  42. establish a call. The easiest way to do this is to add a pseudo host
  43. and create a link to it. The following entries in _state/commandsfile
  44. do just that.
  45.  
  46. #
  47. # Pseudo host to use the MHSnet modem dialer to set the time of day.
  48. #
  49. add        N=setclock.O=ptcbu.P=oz.C=au
  50. link        N=ptcburp.O=ptcbu.P=oz.C=au,
  51.         N=setclock.O=ptcbu.P=oz.C=au
  52.         +foreign,+nochange,+dead
  53. linkname    N=setclock.O=ptcbu.P=oz.C=au
  54.         setclock
  55.  
  56. The host is called "setclock" and placed into the local domain. The
  57. +foreign,+nochange,+dead flags prevent any messages from being sent
  58. to that host. Please use your machine and domain to do this, not
  59. ptcburp.ptcbu.oz.au :-)
  60.  
  61. Do a "netstate -wrsxC" after changing your commandsfile.
  62.  
  63. Next, add an entry to _lib/initfile:
  64.  
  65. call-setclock.ptcbu.oz.au "49 8 * * 1"          setclock/call
  66.  
  67. This tries to establish a connection to the setclock host once a week.
  68.  
  69. In $SPOOLDIR, create a directory "setclock", owned by daemon, group daemon,
  70. with permissions 750.
  71.  
  72. Create a file called "call", owned by daemon, group daemon, with
  73. permissions 750. It contains the following script:
  74.  
  75.  
  76. #!/bin/sh
  77.  
  78. DEVICE=/dev/term/0
  79. TRACE=0
  80. if [ "$TRACE" -gt 0 ]
  81. then
  82.     mdmtrace='M1S61=255'    # Switch speaker on
  83. else
  84.     mdmtrace='M0'        # Be silent
  85. fi
  86. /usr/spool/MHSnet/_lib/VCcall \
  87.     -T${TRACE} \
  88.     -v \
  89.     -D "dialstr=AT${mdmtrace}S50=3DT0,07,2217033" \
  90.     -D "linespeed=19200" \
  91.     -D "modemdev=$DEVICE" \
  92.     -D "timecmd=/usr/local/bin/setclock" \
  93.     -S setclock.cs \
  94.     -X /usr/spool/MHSnet/setclock/call.log \
  95.     setclock.ptcbu.oz.au
  96. exit $?
  97.  
  98. This simply invokes VCcall with the appropriate arguments.
  99. The dial string here is for a Trailblazer, so you need to adjust it if
  100. you have a different modem.
  101.  
  102.  
  103. The call script that calls the clock server and picks out the date
  104. string goes into _call/setclock.cs. I modified one of the generic
  105. call scripts which come with MHSnet to invoke a program to set the date.
  106.  
  107.  
  108. /*
  109. **    Call script to set the system time.
  110. **
  111. **    Imports:
  112. **        dialstr        string to cause modem to dial remote site
  113. **        linespeed    speed for modemdev
  114. **        modemdev    device attached to modem
  115. **        timecmd        command to set time
  116. */
  117.  
  118.     set dialcount 6;    /* dial attempts */
  119.     set timecount 10;    /* number of tries at reading time */
  120.  
  121.     /*
  122.     **    Check imported strings.
  123.     */
  124.  
  125. chkargs:
  126.     match dialstr UNDEFINED paramerr;
  127.     match linespeed UNDEFINED paramerr;
  128.     match modemdev UNDEFINED paramerr;
  129.  
  130. start1:
  131.     timeout 60;        /* Wait 1 minute between/for open attempts */
  132.     retry 60;        /* Allow 60 attempts at open => 1 hour max */
  133.     open "dial" modemdev "uucplock" "ondelay" "local";    /* Open with O_NDELAY */
  134.     match RESULT DEVOK openok;
  135.     set reason "Could not open \"" modemdev "\" reason: " RESULT;
  136.  
  137. openfail:
  138.     fail reason;
  139.  
  140. paramerr:
  141.     fail "missing some of:\n"
  142.         "\t-D \"dialstr=<ATD command for modem>\"\n"
  143.         "\t-D \"linespeed=<bits/second>\"\n"
  144.         "\t-D \"modemdev=<device pathname for modem>\""
  145.         ;
  146.  
  147. openok:
  148.     device "offdelay";        /* Otherwise reads will return 0 */
  149.     device "speed" linespeed;
  150.     monitor 0;            /* Turn on I/O tracing */
  151.  
  152.     sleep 1;            /* Because some terminal i/o is slow! */
  153.     write "AT\r";            /* Make sure modem latches on */
  154.     sleep 1;
  155.     write "AT\r";
  156.     sleep 1;
  157.     write "AT\r";
  158.     sleep 1;
  159.     device "flush";
  160.     write "ATZ\r";
  161.     timeout 10;
  162.     expect
  163.         "OK" try_initstr,
  164.         EOF eof,
  165.         TERMINATE terminate,
  166.         TIMEOUT rstmodem;
  167.  
  168. rstmodem:
  169.     sleep 2;
  170.     write "+++";
  171. clrmodem:
  172.     sleep 2;
  173.     write "ATZ\r";
  174.     timeout 10;
  175.     expect
  176.         "OK" try_initstr,
  177.         EOF eof,
  178.         TERMINATE terminate,
  179.         TIMEOUT nomodem;
  180.  
  181. try_initstr:
  182.     match initstr UNDEFINED dialing;
  183.     timeout 0;
  184.     sleep 1;
  185.     write initstr "\r";
  186.     timeout 10;
  187.     expect
  188.         "OK" dialing,
  189.         EOF eof,
  190.         TERMINATE terminate,
  191.         TIMEOUT dialing;
  192.  
  193. nomodem:
  194.     set reason "modem \"" modemdev "\" not responding";
  195.     next nextmodem;
  196.  
  197. dialing:
  198.     test dialcount dfail;
  199.     timeout 0;
  200.     sleep 1;
  201.     write dialstr "\r";
  202.     timeout 60;
  203.     expect
  204.         "BUSY" busy,
  205.         "NO ANSWER" busy,
  206.         "NO CARRIER" clrmodem,
  207.         "NO DIALTONE" nodial,
  208.         "CONNECT 2400" gettime,
  209.         "CONNECT 1200" gettime,
  210.         "CONNECT" gettime,
  211.         EOF eof,
  212.         TERMINATE terminate,
  213.         TIMEOUT rstmodem;
  214.  
  215. busy:
  216.     trace "number \"" dialstr "\" busy";
  217.     sleep 30;        /* Give them time to get off the phone */
  218.     next clrmodem;
  219.  
  220. gettime:
  221.     set DELIMCHARS "\n\r";
  222.     timeout 4;
  223. try_again:
  224.     test timecount no_time;
  225.     device "flush";
  226.     expect
  227.         "[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,
  228.         TIMEOUT try_again,
  229.         TERMINATE terminate,
  230.         EOF endmodem2;
  231.  
  232. settime:
  233.     shell timecmd " '"INPUT"'";
  234.     match RESULT "0" endmodem;
  235.     match RESULT "1" bad_format;
  236.     match RESULT "2" bad_date;
  237.     match RESULT "3" too_large;
  238.     match RESULT "4" stime_failed;
  239.     set reason timecmd " '"INPUT"' returned weird exit status: " RESULT;
  240.  
  241. endmodem:
  242.     device "local";                /* Ignore modem signals */
  243.     sleep 2;
  244.     device "flush";
  245.     write "+++";
  246.     timeout 3;
  247.     expect
  248.         "NO CARRIER" endmodem1,
  249.         "OK" endmodem1,
  250.         EOF endmodem2,
  251.         TERMINATE endmodem0,
  252.         TIMEOUT endmodem1;
  253.  
  254. endmodem0:
  255.     sleep 2;
  256. endmodem1:
  257.     write "ATH0Z\r";
  258.     timeout 2;
  259.     expect
  260.         "OK" endmodem2,
  261.         EOF endmodem2,
  262.         TERMINATE endmodem2,
  263.         TIMEOUT endmodem2;
  264.  
  265. endmodem2:
  266.     match finishstr UNDEFINED endmodem3;
  267.     timeout 0;
  268.     write finishstr "\r";
  269.     sleep 1;
  270. endmodem3:
  271.     match reason UNDEFINED out;
  272.     fail reason;
  273. out:
  274.     return;
  275.  
  276. dfail:
  277.     set reason "too many dial attempts";
  278.     next endmodem;
  279.  
  280. eof:
  281.     set reason "remote disconnect";
  282.     next endmodem;
  283.  
  284. nodial:
  285.     set reason "modem \"" modemdev "\" has no dialtone";
  286.     match modemdev2 UNDEFINED endmodem;
  287.     next nextmodem;
  288.  
  289. terminate:
  290.     set reason "system termination";
  291.     next endmodem;
  292.  
  293. no_time:
  294.     set reason "cannot get time from connection";
  295.     next endmodem;
  296.  
  297. bad_format:
  298.     set reason timecmd " '"INPUT"': bad format";
  299.     next endmodem;
  300.  
  301. bad_date:
  302.     set reason timecmd " '"INPUT"': meaningless date";
  303.     next endmodem;
  304.  
  305. too_large:
  306.     set reason timecmd " '"INPUT"': time difference too large";
  307.     next endmodem;
  308.  
  309. stime_failed:
  310.     set reason timecmd " '"INPUT"': stime(2) call failed";
  311.     next endmodem;
  312.  
  313.  
  314. Finally, you need to compile and install the code below (set-uid root).
  315. It sets the new date after doing some sanity checks. I added a limit
  316. to prevent the system time being changed by more than 300 seconds.
  317. I figure if my clock is wrong by as much as that, I'd like to know why :-)
  318.  
  319. If you don't call the program /usr/local/bin/setclock, remember to
  320. change the path passed to the call script from setclock/call.
  321.  
  322.  
  323. /*
  324.  * setclock - set the system time using AOTC's atomic clock service.
  325.  *
  326.  * This program is meant to be invoked from a daemon which obtains
  327.  * the current date from AOTC's dial-up atomic clock. The clock produces
  328.  * lines of the form
  329.  *
  330.  *    yyyy-mm-dd hh:mm:ss[*#]
  331.  *
  332.  * The output from the dial-up clock looks like
  333.  *
  334.  * 1992-12-10 10:01:40*
  335.  * 1992-12-10 10:01:41*
  336.  * 1992-12-10 10:01:42*
  337.  * 1992-12-10 10:01:43*
  338.  * 1992-12-10 10:01:44*
  339.  *
  340.  * Each line is printed up to the '*', followed by a brief pause,
  341.  * followed by the '*', CR and LF. The exact mark for the time is
  342.  * the stop-bit/start-bit transition between the CR and LF.
  343.  *
  344.  * For daylight saving time, the '*' is replaced by a '#'.
  345.  *
  346.  * The single argument must be one complete line. Any trailing characters
  347.  * (such as CR/LF) after the '*' or '#' are ignored.
  348.  *
  349.  * The argument is broken into its components, sanity checked and
  350.  * converted into seconds since The Epoch. If the resulting time does
  351.  * not differ from the current system time by more than MAX_ADJ seconds,
  352.  * the system clock is set to the new time.
  353.  *
  354.  * This program must be set-uid root, and should be installed with
  355.  * permissions 4710, owner root, group daemon.
  356.  */
  357.  
  358. /*****************************************************************************/
  359.  
  360. #include    <stdlib.h>
  361. #include    <unistd.h>
  362. #include    <libgen.h>
  363. #include    <string.h>
  364. #include    <time.h>
  365.  
  366. /*****************************************************************************/
  367.  
  368. #define    FORMAT    "yyyy-mm-dd hh:mm:ss"        /* Format of input date */
  369. #define    USAGE    "Usage: %s \"%s[*#]\"\n"    /* Usage message */
  370.  
  371. #define    MAX_ADJ    300                /* Max adjust in seconds */
  372.  
  373. #define    OK        0            /* Exit status if OK */
  374. #define    BAD_FORM    1            /* Bad format of argument */
  375. #define    BAD_DATE    2            /* Meaningless date */
  376. #define    TOO_LARGE    3            /* MAX_ADJ exceeded */
  377. #define    BAD_STIME    4            /* stime(2) failed */
  378.  
  379. /*****************************************************************************/
  380.  
  381. static const char    *progname;        /* For error messages */
  382.  
  383. /*****************************************************************************/
  384.  
  385. /*
  386.  * Print usage message and exit. If the given date is non-NULL,
  387.  * also complain about an invalid date, showing the invalid string.
  388.  */
  389.  
  390. static void
  391. usage(int status, const char *date)
  392. {
  393.     if (date != NULL) {
  394.         (void) fprintf(
  395.             stderr, "%s: invalid date: \"%s\"\n", progname, date
  396.         );
  397.     }
  398.     (void) fprintf(stderr, USAGE, progname, FORMAT);
  399.     exit(status);
  400. }
  401.  
  402. /*****************************************************************************/
  403.  
  404. int
  405. main(int argc, char *argv[])
  406. {
  407.     char        buf[sizeof(FORMAT) + 1];    /* +1 for '*' or '#' */
  408.     char        *year;
  409.     char        *month;
  410.     char        *day;
  411.     char        *hour;
  412.     char        *minute;
  413.     char        *second;
  414.     struct tm    stm;
  415.     time_t        new_time;
  416.  
  417. /*
  418.  *    Set program name for error messages.
  419.  */
  420.     progname = basename(argv[0]);
  421. /*
  422.  *    Check usage.
  423.  */
  424.     if (argc != 2)
  425.         usage(BAD_FORM, NULL);
  426. /*
  427.  *    Get rid of any trailing garbage in argument (the AOTC atomic
  428.  *    clock service terminates each line with CR/LF). We clean this
  429.  *    up in argv[1] instead of a copy of it, so we can print nicer
  430.  *    error messages (without any trailing newline).
  431.  */
  432.     /* LINTED */
  433.     if (strlen(argv[1]) < strlen(FORMAT) + 1)
  434.         usage(BAD_FORM, argv[1]);
  435.     argv[1][strlen(FORMAT) + 1] = '\0';
  436. /*
  437.  *    Break up the date into components.
  438.  */
  439.     (void) strncpy(buf, argv[1], strlen(FORMAT) + 1);
  440.     year = buf;
  441.     if ((month = strchr(year, '-')) == NULL)
  442.         usage(BAD_FORM, argv[1]);
  443.     *month++ = '\0';
  444.     if ((day = strchr(month, '-')) == NULL)
  445.         usage(BAD_FORM, argv[1]);
  446.     *day++ = '\0';
  447.     if ((hour = strchr(day, ' ')) == NULL)
  448.         usage(BAD_FORM, argv[1]);
  449.     *hour++ = '\0';
  450.     if ((minute = strchr(hour, ':')) == NULL)
  451.         usage(BAD_FORM, argv[1]);
  452.     *minute++ = '\0';
  453.     if ((second = strchr(minute, ':')) == NULL)
  454.         usage(BAD_FORM, argv[1]);
  455.     *second++ = '\0';
  456. /*
  457.  *    Check fields for validity and build struct tm.
  458.  */
  459.     if (second[2] != '*' && second[2] != '#')
  460.         usage(BAD_FORM, argv[1]);
  461.     stm.tm_isdst = second[2] == '#';
  462.     second[2] = '\0';
  463.     stm.tm_year = atoi(year);
  464.     if (stm.tm_year < 1900)
  465.         usage(BAD_DATE, argv[1]);
  466.     stm.tm_year -= 1900;        /* Origin is the year 1900 */
  467.     stm.tm_mon = atoi(month);
  468.     if (stm.tm_mon < 1 || stm.tm_mon > 12)
  469.         usage(BAD_DATE, argv[1]);
  470.     stm.tm_mon--;            /* Origin is 0 (January) */
  471.     stm.tm_mday = atoi(day);
  472.     if (stm.tm_mday < 1 || stm.tm_mday > 31)
  473.         usage(BAD_DATE, argv[1]);
  474.     stm.tm_hour = atoi(hour);
  475.     if (stm.tm_hour < 0 || stm.tm_hour > 23)
  476.         usage(BAD_DATE, argv[1]);
  477.     stm.tm_min = atoi(minute);
  478.     if (stm.tm_min < 0 || stm.tm_min > 59)
  479.         usage(BAD_DATE, argv[1]);
  480.     stm.tm_sec = atoi(second);
  481.     if (stm.tm_sec < 0 || stm.tm_sec > 59)
  482.         usage(BAD_DATE, argv[1]);
  483. /*
  484.  *    Convert time to seconds since The Epoch.
  485.  */
  486.     if ((new_time = mktime(&stm)) == -1)
  487.         usage(BAD_DATE, argv[1]);
  488. /*
  489.  *    Don't allow adjustment of time by more than MAX_ADJ seconds.
  490.  */
  491.     if (abs(time(NULL) - new_time) > MAX_ADJ) {
  492.         (void) fprintf(
  493.             stderr,
  494.             "%s: will not adjust time by more than %d seconds\n",
  495.             progname, MAX_ADJ
  496.         );
  497.         exit(TOO_LARGE);
  498.     }
  499. /*
  500.  *    Set the system time.
  501.  */
  502.     if (stime(&new_time) == -1) {
  503.         extern int    errno;
  504.  
  505.         (void) fprintf(
  506.             stderr, "%s: cannot set time: %s\n",
  507.             progname, strerror(errno)
  508.         );
  509.         exit(BAD_STIME);
  510.     }
  511. /*
  512.  *    Done.
  513.  */
  514.     exit(OK);
  515.     /* NOTREACHED */
  516. }
  517. -- 
  518.       -m------- Michael Henning                 +61 75 950255
  519.     ---mmm----- Pyramid Technology              +61 75 722475 FAX
  520.   -----mmmmm--- Research Park, Bond University  michi@ptcburp.ptcbu.oz.au
  521. -------mmmmmmm- Gold Coast, Q 4229, AUSTRALIA   uunet!munnari!ptcburp.oz!michi
  522.