home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / settime / settime.c < prev   
Encoding:
C/C++ Source or Header  |  1992-03-09  |  9.5 KB  |  376 lines

  1. /*
  2.  
  3.     Set  UNIX  system  clock  from  the  atomic  clock at the National
  4.     Institute of Standards and Technology (NIST) in Boulder  Colorado.
  5.  
  6.     Designed and implemented in May of 1991 by John Walker.
  7.  
  8.     This  program,  which should be run when in super-user mode, dials
  9.     into the telephone time service maintained by the NIST in Boulder,
  10.     obtains  the  time    from  the  atomic  clock  there (obtaining two
  11.     consecutive valid readings to guard against communication errors),
  12.     then sets the system clock at the moment the time hack is received
  13.     for the next second.  While waiting for the tick, the program goes
  14.     into  a high-priority CPU loop waiting for the tick to be received
  15.     to obtain the best accuracy.
  16.  
  17.     The modem port, modem dial codes and telephone number, and various
  18.     other  parameters  are  set on the command line.  The defaults are
  19.     equivalent to the following:
  20.  
  21.     settime
  22.         -M/dev/cua0           -- Modem port
  23.         -NATDT13034944774          -- Dial command and phone number
  24.         -T120              -- Two minute timeout
  25.  
  26.     If you specify the "-E" option, all modem input and output will be
  27.     echoed to standard output.
  28.  
  29.     You  don't  have  to be super-user to run the progrmam, but if you
  30.     aren't the system time and date won't  be  changed,  as  only  the
  31.     super-user may change them.
  32.  
  33. */
  34.  
  35. #define REVDATE "13th May 1991"
  36.  
  37. #include <sys/types.h>
  38. #include <sys/time.h>
  39. #include <stdio.h>
  40. #include <fcntl.h>
  41. #include <termio.h>
  42. #include <time.h>
  43.  
  44. #define     FALSE   0
  45. #define     TRUE    1
  46.  
  47. #define     EOS     '\0'
  48.  
  49. struct termio newmode, oldmode;
  50.  
  51. static char *modem = "/dev/cua0";     /* Modem file name */
  52. static char *dialstring = "ATDT13034944774"; /* Dial command */
  53. static int echoin = FALSE;          /* Echo modem info if nonzero */
  54. static int port;              /* Modem file descriptor */
  55. static char line[132];              /* Input buffer */
  56. static int jdate1970 = 2440588;       /* Julian date of January 1, 1970 */
  57. static long usdelay;              /* Line delay in microseconds */
  58. static time_t bailout;              /* Time to give up if no success */
  59.  
  60. /*  TIMEDOUT  --  Check for timeout expired.  */
  61.  
  62. static int timedout()
  63. {
  64.     return time(NULL) > bailout;
  65. }
  66.  
  67. /*  MREAD  --  Read next character from modem. */
  68.  
  69. static int mread(buf)
  70.   char *buf;
  71. {
  72.     return read(port, buf, 1);
  73. }
  74.  
  75.  
  76. /*  INLINE  --    Obtain a newline terminated input line from the modem.    */
  77.  
  78. static int inline()
  79. {
  80.     int l = 0;
  81.     char buf;
  82.  
  83.     while (TRUE) {
  84.     if (mread(&buf) > 0) {
  85.         if (echoin) {
  86.         putchar(buf);
  87.         }
  88.             if (buf == '\n') {
  89.         line[l] = EOS;
  90.         break;
  91.         }
  92.             if (buf >= ' ' && l < 131) {
  93.         line[l++] = buf;
  94.         }
  95.     } else {
  96.         if (timedout()) {
  97.                 strcpy(line, "BUSY");
  98.         return FALSE;
  99.         }
  100.         usleep(4000L);
  101.     }
  102.     }
  103.     return TRUE;
  104. }
  105.  
  106. /*  JDATE  --  Convert internal GMT date and time to Julian day
  107.            and fraction.  */
  108.  
  109. static long jdate(t)
  110.   struct tm *t;
  111. {
  112.     long c, m, y;
  113.  
  114.     y = t->tm_year + 1900;
  115.     m = t->tm_mon + 1;
  116.     if (m > 2)
  117.        m = m - 3;
  118.     else {
  119.        m = m + 9;
  120.        y--;
  121.     }
  122.     c = y / 100L;           /* Compute century */
  123.     y -= 100L * c;
  124.     return t->tm_mday + (c * 146097L) / 4 + (y * 1461L) / 4 +
  125.         (m * 153L + 2) / 5 + 1721119L;
  126. }
  127.  
  128. /*  PARSE_NIST  --  Parse NIST time string into a "tm" structure.  */
  129.  
  130. static int parse_nist()
  131. {
  132.     int jd, yr, mo, da, hh, mm, ss, dst, ls, msadvw, msadvf;
  133.     char dut1[3], tname[20];
  134.     time_t gt;
  135.     struct tm t;
  136.  
  137.     if (sscanf(line, "%5d %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3c %3d.%1d %s",
  138.     &jd, &yr, &mo, &da, &hh, &mm, &ss, &dst, &ls, dut1, &msadvw,
  139.     &msadvf, tname) < 13)
  140.     return 0;
  141.  
  142.     if (strncmp(tname, "UTC(NIST)", 9) != 0)
  143.     return 0;
  144.  
  145.     t.tm_sec = ss;
  146.     t.tm_min = mm;
  147.     t.tm_hour = hh;
  148.     t.tm_mday = da;
  149.     t.tm_mon = mo - 1;
  150.     t.tm_year = (yr > 90) ? yr : (yr + 100);
  151.     t.tm_isdst = (dst > 0) && (dst <= 50);
  152.     t.tm_wday = ((jd + 3) % 7);
  153.  
  154.     usdelay = ((msadvw * 10) + msadvf) * 100;
  155.  
  156.     gt = ((((jdate(&t) - jdate1970) * 24 + t.tm_hour) * 60) +
  157.         t.tm_min) * 60 + t.tm_sec;
  158.  
  159. #ifdef DEBUG
  160. { char *cp = asctime(gmtime(>));
  161. printf("Clock %d (%d) Date and time: %s Delay = %d\n", gt, time(NULL), cp,
  162.     usdelay);
  163. }
  164. #endif
  165.     return gt;
  166. }
  167.  
  168. int main(argc, argv)
  169.   int argc; char *argv[];
  170. {
  171.     int i, success;
  172.     char *cp, opt;
  173.     time_t ptime, ntime;
  174.     struct timeval tv;
  175.     int timeout = 120;              /* How long to attempt to get time */
  176.  
  177. #ifdef TEST1
  178.     strcpy(line, "48388 91-05-12 20:48:58 50 0 +.3 045.0 UTC(NIST) *");
  179.     parse_nist(&ntime);
  180.     return 0;
  181. #endif
  182.  
  183.     for (i = 1; i < argc; i++) {
  184.     cp = argv[i];
  185.         if (*cp == '-') {
  186.         opt = *(++cp);
  187.         if (islower(opt))
  188.         opt = toupper(opt);
  189.         switch (opt) {
  190.  
  191.                 case 'E':
  192.             echoin = TRUE;
  193.             break;
  194.  
  195.                 case 'M':
  196.             modem = cp + 1;
  197.             break;
  198.  
  199.                 case 'N':
  200.             dialstring = cp + 1;
  201.             break;
  202.  
  203.                 case 'T':
  204.             timeout = atoi(cp + 1);
  205.             break;
  206.  
  207.                 case '?':
  208.                 case 'U':
  209.     fprintf(stderr,"\nSETTIME  --  Set time from NIST time service.  Call");
  210.     fprintf(stderr,
  211.        "\n             with settime [options]");
  212.     fprintf(stderr,"\n");
  213.     fprintf(stderr,"\n         Options:");
  214.     fprintf(stderr,"\n           -E       Echo modem input and output");
  215.     fprintf(stderr,"\n           -Mdev    Use modem device dev");
  216.     fprintf(stderr,"\n           -Ncmd    Use cmd as modem dial command");
  217.     fprintf(stderr,"\n           -Tn      Set timeout to n seconds");
  218.     fprintf(stderr,"\n");
  219.     fprintf(stderr,"\n          (P) Throoput Ltd.  %s", REVDATE);
  220.     fprintf(stderr,"\n                All Rights Reversed");
  221.     fprintf(stderr,"\n");
  222.             return 0;
  223.         }
  224.     } else {
  225.     }
  226.     }
  227.  
  228.     if ((port = open(modem, O_RDWR | O_NDELAY)) == -1) {
  229.         fprintf(stderr, "Cannot open modem port %s\n", modem);
  230.     return 2;
  231.     }
  232.     if (ioctl(port, TCGETA, &oldmode) == -1) {       /* Get current tty mode */
  233.         perror(modem);
  234.         return 2;
  235.     }
  236.     newmode = oldmode;              /* Save it to restore port later */
  237.     bailout = time(NULL) + timeout;   /* Set time to give up */
  238.  
  239.     if (echoin) {
  240.     setbuf(stdout, NULL);          /* Make echoed output unbuffered */
  241.     }
  242.  
  243.     /* Set the modem port to the proper modes for calling the time
  244.        service. */
  245.  
  246.     newmode.c_iflag = IGNBRK | IGNPAR | ISTRIP;
  247.     newmode.c_oflag = 0;
  248.     newmode.c_cflag = B1200 | CS7 | CREAD | PARENB | HUPCL;
  249.     newmode.c_lflag = 0;
  250.     if (ioctl(port, TCSETA, &newmode) == -1) {
  251.         fprintf(stderr, "Cannot set modem to desired modes\n");
  252.         perror(modem);
  253.         return 2;
  254.     }
  255.  
  256.     /* Dial the time service. */
  257.  
  258.     if (fcntl(port, F_SETFL, FNDELAY) == -1) {
  259.         fprintf(stderr, "Cannot set port to non-delayed response\n");
  260.         perror(modem);
  261.         return 2;
  262.     }
  263.     if (write(port, "\r\r\r", 3) != 3) {
  264.         fprintf(stderr, "Error sending CR's to modem\n");
  265.         perror(modem);
  266.         return 2;
  267.     }
  268.     sleep(1);
  269.     while (read(port, &opt, 1) > 0) ; /* Flush input */
  270.     if (echoin) {
  271.         printf("%s\n", dialstring);
  272.     }
  273.     if (write(port, dialstring, strlen(dialstring)) != strlen(dialstring) ||
  274.         write(port, "\r", 1) != 1) {
  275.         fprintf(stderr, "Error sending dial string to modem\n");
  276.         perror(modem);
  277.     }
  278.  
  279.     /* Wait until we're connected.  If we get a busy signal, give up
  280.        and report an error status.  */
  281.  
  282.     while (inline()) {
  283.         if ((strncmp(line, "CONNECT", 7) == 0) || (line[0] == '5')) {
  284.         break;
  285.     }
  286.         if (strncmp(line, "BUSY", 4) == 0 ||
  287.             strncmp(line, "NO ", 3) == 0 ||
  288.             strncmp(line, "ERROR", 5) == 0 ||
  289.             line[0] == '3' || line[0] == '4' || line[0] == '6' ||
  290.             line[0] == '7' || line[0] == '8') {
  291.             fprintf(stderr, "Unable to connect.\n");
  292.         return 1;
  293.     }
  294.     }
  295.  
  296.     /*  At this point we're connected.  Ignore all data from the line
  297.     until we receive two consecutive valid time signals one second
  298.         apart.  Once we've received these two signals, prepare the
  299.     time setting for the next second and spin until we receive
  300.         the "*" that indicates the correct instant to set the clock.
  301.     At that moment, change the system clock.  */
  302.  
  303.     ptime = 0;
  304.     success = FALSE;
  305.     while (!success && !timedout() && inline()) {
  306.     if (ptime == 0) {
  307.         ptime = parse_nist();
  308.         ntime = 0;
  309.     } else if (ntime == 0) {
  310.         ntime = parse_nist();
  311.         if (ntime == (ptime + 1)) {
  312.         int rpyet = 0;
  313.  
  314.         tv.tv_sec = ntime + 1;
  315.         tv.tv_usec = 0;
  316.         while (TRUE) {
  317.             char buf;
  318.             int ticker = 0;   /* CPU loop timeout check */
  319.  
  320.             if (mread(&buf) > 0) {
  321.             if (echoin) {
  322.                 putchar(buf);
  323.             }
  324.                         if ((buf == '*') || (buf == '#')) {
  325.                 settimeofday(&tv, NULL);
  326.                 success = TRUE;
  327.                 nice(10);       /* Drop to normal priority */
  328.                 break;
  329.                         } else if (buf == ')') {
  330.                 rpyet = 1;
  331.                 nice(-10);       /* Set high priority */
  332.                         } else if (buf < ' ') {
  333.                 ptime = 0;
  334.                 if (rpyet) {
  335.                 nice(10);  /* Drop to normal priority */
  336.                 }
  337.             }
  338.             } else {
  339.             if (!rpyet) {
  340.                 usleep(2000L);
  341.                 if (timedout()) {
  342.                 break;
  343.                 }
  344.             } else {
  345.                 if (((++ticker) % 100) == 0) {
  346.                 if (timedout()) {
  347.                     break;
  348.                 }
  349.                 }
  350.             }
  351.             }
  352.         }
  353.         } else {
  354.         ptime = 0;
  355.         }
  356.     }
  357.     }
  358.  
  359.     if (echoin) {
  360.         putchar('\n');
  361.     }
  362.     if (success) {
  363.         printf("Universal time: %s", asctime(gmtime(&tv.tv_sec)));
  364.         printf("Local time:     %s", asctime(localtime(&tv.tv_sec)));
  365.     }
  366.  
  367.     /* Restore port to original modes and close, disconnecting the
  368.        modem. */
  369.  
  370.     ioctl(port, TCSETAW, &oldmode);
  371.     ioctl(port, TCFLSH, 2);
  372.     close(port);
  373.  
  374.     return 0;
  375. }
  376.