home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / x / xntp3.zip / xntpd / ntp_unixclock. < prev    next >
Text File  |  1992-08-29  |  15KB  |  616 lines

  1. /*
  2.  * ntp_unixclock.c - routines for reading and adjusting a 4BSD-style
  3.  *             system clock
  4.  */
  5.  
  6. #include <stdio.h>
  7. #include <nlist.h>
  8. #include <sys/types.h>
  9. #include <sys/time.h>
  10. #include <sys/file.h>
  11. #include <sys/stat.h>
  12. #include <netinet/in.h>
  13. #if defined(HPUX)
  14. #include <sys/param.h>
  15. #include <utmp.h>
  16. #endif
  17.  
  18. #ifdef USELIBKVM
  19. #include <kvm.h>
  20. #include <limits.h>
  21.  
  22. #ifndef _POSIX2_LINE_MAX
  23. #define _POSIX2_LINE_MAX 2048
  24. #endif
  25. #endif
  26.  
  27. #include "ntp_syslog.h"
  28. #include "ntp_fp.h"
  29. #include "ntp_unixtime.h"
  30. #include "ntp.h"
  31.  
  32. #ifdef RS6000
  33. #undef hz
  34. #endif /* RS6000 */
  35.  
  36. extern int debug;
  37.  
  38. /*
  39.  * These routines (init_systime, get_systime, step_systime, adj_systime)
  40.  * implement an interface between the (more or less) system independent
  41.  * bits of NTP and the peculiarities of dealing with the Unix system
  42.  * clock.  These routines will run with good precision fairly independently
  43.  * of your kernel's value of tickadj.  I couldn't tell the difference
  44.  * between tickadj==40 and tickadj==5 on a microvax, though I prefer
  45.  * to set tickadj == 500/hz when in doubt.  At your option you
  46.  * may compile this so that your system's clock is always slewed to the
  47.  * correct time even for large corrections.  Of course, all of this takes
  48.  * a lot of code which wouldn't be needed with a reasonable tickadj and
  49.  * a willingness to let the clock be stepped occasionally.  Oh well.
  50.  */
  51.  
  52. /*
  53.  * Clock variables.  We round calls to adjtime() to adj_precision
  54.  * microseconds, and limit the adjustment to tvu_maxslew microseconds
  55.  * (tsf_maxslew fractional sec) in one adjustment interval.  As we are
  56.  * thus limited in the speed and precision with which we can adjust the
  57.  * clock, we compensate by keeping the known "error" in the system time
  58.  * in sys_offset.  This is added to timestamps returned by get_systime().
  59.  * We also remember the clock precision we computed from the kernel in
  60.  * case someone asks us.
  61.  */
  62. long adj_precision;    /* adj precision in usec (tickadj) */
  63. long tvu_maxslew;    /* maximum adjust doable in 1<<CLOCK_ADJ sec (usec) */
  64.  
  65. unsigned long tsf_maxslew;    /* same as above, as long format */
  66.  
  67. static l_fp sys_offset;        /* correction for current system time */
  68.  
  69. /*
  70.  * Import sys_clock (it is updated in get_systime)
  71.  */
  72. extern long sys_clock;
  73.  
  74.  
  75. /*
  76.  * Tables for converting between time stamps and struct timeval's
  77.  * are in the library.  Reference them here.
  78.  */
  79. extern u_long ustotslo[];
  80. extern u_long ustotsmid[];
  81. extern u_long ustotshi[];
  82.  
  83. extern long tstoushi[];
  84. extern long tstousmid[];
  85. extern long tstouslo[];
  86.  
  87. /*
  88.  * init_systime - initialize the system clock support code, return
  89.  *          clock precision.
  90.  *
  91.  * Note that this code obtains to kernel variables related to the local
  92.  * clock, tickadj and tick.  The code knows how the Berkeley adjtime
  93.  * call works, and assumes these two variables are obtainable and are
  94.  * used in the same manner.  Tick is supposed to be the number of
  95.  * microseconds which are added to the system clock at clock interrupt
  96.  * time when the time isn't being slewed.  Tickadj is supposed to be
  97.  * the number of microseconds which are added or subtracted from tick when
  98.  * the time is being slewed.
  99.  *
  100.  * If either of these two variables is missing, or is there but is used
  101.  * for a purpose different than that described, you are SOL and may have
  102.  * to do some custom kludging.
  103.  *
  104.  * This really shouldn't be in here.
  105.  */
  106. void
  107. init_systime()
  108. {
  109.     u_long tickadj;
  110.     u_long tick;
  111.     u_long hz;
  112.     void clock_parms();
  113.  
  114.     /*
  115.      * Obtain the values
  116.      */
  117.     clock_parms(&tickadj, &tick);
  118. #ifdef    DEBUG
  119.     if (debug)
  120.         printf("kernel vars: tickadj = %d, tick = %d\n", tickadj, tick);
  121. #endif
  122.  
  123.     /*
  124.      * If tickadj or hz wasn't found, we're doomed.  If hz is
  125.      * unreasonably small, forget it.
  126.      */
  127.     if (tickadj == 0 || tick == 0) {
  128.         syslog(LOG_ERR, "tickadj or tick unknown, exiting");
  129.         exit(3);
  130.     }
  131.     if (tick > 65535) {
  132.         syslog(LOG_ERR, "tick value of %lu is unreasonably large",
  133.             tick);
  134.         exit(3);
  135.     }
  136.  
  137.     /*
  138.      * Estimate hz from tick
  139.      */
  140.     hz = 1000000L / tick;
  141.  
  142.     /*
  143.      * Set adj_precision and the maximum slew based on this.  Note
  144.      * that maxslew is set slightly shorter than it needs to be as
  145.      * insurance that all slews requested will complete in 1<<CLOCK_ADJ
  146.      * seconds.
  147.      */
  148. #ifdef ADJTIME_IS_ACCURATE
  149.     adj_precision = 1;
  150. #else
  151.     adj_precision = tickadj;
  152. #endif /* ADJTIME_IS_ACCURATE */
  153.     tvu_maxslew = tickadj * (hz-1) * (1<<CLOCK_ADJ);
  154.     if (tvu_maxslew > 999990) {
  155.         /*
  156.          * Don't let the maximum slew exceed 1 second in 4.  This
  157.          * simplifies calculations a lot since we can then deal
  158.          * with less-than-one-second fractions.
  159.          */
  160.         tvu_maxslew = (999990/adj_precision) * adj_precision;
  161.     }
  162.     TVUTOTSF(tvu_maxslew, tsf_maxslew);
  163. #ifdef DEBUG
  164.     if (debug)
  165.         printf(
  166.     "adj_precision = %d, tvu_maxslew = %d, tsf_maxslew = 0.%08x\n",
  167.             adj_precision, tvu_maxslew, tsf_maxslew);
  168. #endif
  169.  
  170.     /*
  171.      * Set the current offset to 0
  172.      */
  173.     sys_offset.l_ui = sys_offset.l_uf = 0;
  174. }
  175.  
  176.  
  177. /*
  178.  * get_systime - return the system time in timestamp format
  179.  * As a side effect, update sys_clock.
  180.  */
  181. void
  182. get_systime(ts)
  183.     l_fp *ts;
  184. {
  185.     struct timeval tv;
  186.  
  187. #if !defined(SLEWALWAYS) && !defined(LARGETICKADJ)
  188.     /*
  189.      * Quickly get the time of day and convert it
  190.      */
  191.     (void) gettimeofday(&tv, (struct timezone *)NULL);
  192.     TVTOTS(&tv, ts);
  193.     ts->l_uf += TS_ROUNDBIT;    /* guaranteed not to overflow */
  194. #else
  195.     /*
  196.      * Get the time of day, convert to time stamp format
  197.      * and add in the current time offset.  Then round
  198.      * appropriately.
  199.      */
  200.     (void) gettimeofday(&tv, (struct timezone *)NULL);
  201.     TVTOTS(&tv, ts);
  202.     L_ADD(ts, &sys_offset);
  203.     if (ts->l_uf & TS_ROUNDBIT)
  204.         L_ADDUF(ts, (unsigned long) TS_ROUNDBIT);
  205. #endif    /* !defined(SLEWALWAYS) && !defined(LARGETICKADJ) */
  206.     ts->l_ui += JAN_1970;
  207.     ts->l_uf &= TS_MASK;
  208.  
  209.     sys_clock = ts->l_ui;
  210. }
  211.  
  212.  
  213. /*
  214.  * step_systime - do a step adjustment in the system time (at least from
  215.  *          NTP's point of view.
  216.  */
  217. void
  218. step_systime(ts)
  219.     l_fp *ts;
  220. {
  221. #ifndef SLEWALWAYS
  222.     struct timeval timetv, adjtv;
  223.     int isneg = 0;
  224. #if defined(HPUX)
  225.     struct utmp ut;
  226.     time_t oldtime;
  227. #endif
  228.     extern char *lfptoa();
  229.     extern char *tvtoa();
  230.     extern char *utvtoa();
  231.  
  232.     /*
  233.      * We can afford to be sloppy here since if this is called
  234.      * the time is really screwed and everything is being reset.
  235.      */
  236.     L_ADD(&sys_offset, ts);
  237.  
  238.     if (L_ISNEG(&sys_offset)) {
  239.         isneg = 1;
  240.         L_NEG(&sys_offset);
  241.     }
  242.     TSTOTV(&sys_offset, &adjtv);
  243.  
  244.     (void) gettimeofday(&timetv, (struct timezone *)NULL);
  245. #if defined(HPUX)
  246.     oldtime = timetv.tv_sec;
  247. #endif
  248. #ifdef DEBUG
  249.     if (debug > 3)
  250.         printf("step: %s, sys_offset = %s, adjtv = %s, timetv = %s\n",
  251.             lfptoa(ts, 9), lfptoa(&sys_offset, 9), tvtoa(&adjtv),
  252.             utvtoa(&timetv));
  253. #endif
  254.     if (isneg) {
  255.         timetv.tv_sec -= adjtv.tv_sec;
  256.         timetv.tv_usec -= adjtv.tv_usec;
  257.         if (timetv.tv_usec < 0) {
  258.             timetv.tv_sec--;
  259.             timetv.tv_usec += 1000000;
  260.         }
  261.     } else {
  262.         timetv.tv_sec += adjtv.tv_sec;
  263.         timetv.tv_usec += adjtv.tv_usec;
  264.         if (timetv.tv_usec >= 1000000) {
  265.             timetv.tv_sec++;
  266.             timetv.tv_usec -= 1000000;
  267.         }
  268.     }
  269.     if (settimeofday(&timetv, (struct timezone *)NULL) != 0)
  270.         syslog(LOG_ERR, "Can't set time of day: %m");
  271. #ifdef DEBUG
  272.     if (debug > 3)
  273.         printf("step: new timetv = %s\n", utvtoa(&timetv));
  274. #endif
  275.     sys_offset.l_ui = sys_offset.l_uf = 0;
  276. #if defined(HPUX)
  277.     _clear_adjtime();
  278.     /*
  279.      * Write old and new time entries in utmp and wtmp if step adjustment
  280.      * is greater than one second.
  281.      */
  282.     if (oldtime != timetv.tv_sec) {
  283.         bzero((char *)&ut, sizeof(ut));
  284.         ut.ut_type = OLD_TIME;
  285.         ut.ut_time = oldtime;
  286.         (void)strcpy(ut.ut_line, OTIME_MSG);
  287.         pututline(&ut);
  288.         setutent();
  289.         ut.ut_type = NEW_TIME;
  290.         ut.ut_time = timetv.tv_sec;
  291.         (void)strcpy(ut.ut_line, NTIME_MSG);
  292.         pututline(&ut);
  293.         utmpname(WTMP_FILE);
  294.         ut.ut_type = OLD_TIME;
  295.         ut.ut_time = oldtime;
  296.         (void)strcpy(ut.ut_line, OTIME_MSG);
  297.         pututline(&ut);
  298.         ut.ut_type = NEW_TIME;
  299.         ut.ut_time = timetv.tv_sec;
  300.         (void)strcpy(ut.ut_line, NTIME_MSG);
  301.         pututline(&ut);
  302.         endutent();
  303.     }
  304. #endif
  305.     
  306. #else
  307.     /*
  308.      * Just add adjustment into the current offset.  The update
  309.      * routine will take care of bringing the system clock into
  310.      * line.
  311.      */
  312.     L_ADD(&sys_offset, ts);
  313. #endif    /* SLEWALWAYS */
  314. }
  315.  
  316.  
  317. /*
  318.  * adj_systime - called once every 1<<CLOCK_ADJ seconds to make system time
  319.  *         adjustments.
  320.  */
  321. void
  322. adj_systime(adj)
  323.     long adj;
  324. {
  325.     register unsigned long offset_i, offset_f;
  326.     register long temp;
  327.     register unsigned long residual;
  328.     register int isneg = 0;
  329.     struct timeval adjtv, oadjtv;
  330.     extern char *mfptoa();
  331.     extern char *umfptoa();
  332.     extern char *utvtoa();
  333.     extern char *tvtoa();
  334.  
  335.     /*
  336.      * Move the current offset into the registers
  337.      */
  338.     offset_i = sys_offset.l_ui;
  339.     offset_f = sys_offset.l_uf;
  340.  
  341.     /*
  342.      * Add the new adjustment into the system offset.  Adjust the
  343.      * system clock to minimize this.
  344.      */
  345.     M_ADDF(offset_i, offset_f, adj);
  346.     if (M_ISNEG(offset_i, offset_f)) {
  347.         isneg = 1;
  348.         M_NEG(offset_i, offset_f);
  349.     }
  350. #ifdef DEBUG
  351.     if (debug > 4)
  352.         printf("adj_systime(%s): offset = %s%s\n",
  353.             mfptoa((adj<0?-1:0), adj, 9), isneg?"-":"",
  354.             umfptoa(offset_i, offset_f, 9));
  355. #endif
  356.  
  357.     adjtv.tv_sec = 0;
  358.     if (offset_i > 0 || offset_f >= tsf_maxslew) {
  359.         /*
  360.          * Slew is bigger than we can complete in
  361.          * the adjustment interval.  Make a maximum
  362.          * sized slew and reduce sys_offset by this
  363.          * much.
  364.          */
  365.         M_SUBUF(offset_i, offset_f, tsf_maxslew);
  366.         if (!isneg) {
  367.             adjtv.tv_usec = tvu_maxslew;
  368.         } else {
  369.             adjtv.tv_usec = -tvu_maxslew;
  370.             M_NEG(offset_i, offset_f);
  371.         }
  372.  
  373. #ifdef DEBUG
  374.         if (debug > 4)
  375.             printf(
  376.                 "maximum slew: %s%s, remainder = %s\n",
  377.                 isneg?"-":"", umfptoa(0, tsf_maxslew, 9),
  378.                 mfptoa(offset_i, offset_f, 9));
  379. #endif
  380.     } else {
  381.         /*
  382.          * We can do this slew in the time period.  Do our
  383.          * best approximation (rounded), save residual for
  384.          * next adjustment.
  385.          *
  386.          * Note that offset_i is guaranteed to be 0 here.
  387.          */
  388.         TSFTOTVU(offset_f, temp);
  389. #ifndef ADJTIME_IS_ACCURATE
  390.         /*
  391.          * Round value to be an even multiple of adj_precision
  392.          */
  393.         residual = temp % adj_precision;
  394.         temp -= residual;
  395.         if (residual << 1 >= adj_precision)
  396.             temp += adj_precision;
  397. #endif /* ADJTIME_IS_ACCURATE */
  398.         TVUTOTSF(temp, residual);
  399.         M_SUBUF(offset_i, offset_f, residual);
  400.         if (isneg) {
  401.             adjtv.tv_usec = -temp;
  402.             M_NEG(offset_i, offset_f);
  403.         } else {
  404.             adjtv.tv_usec = temp;
  405.         }
  406. #ifdef DEBUG
  407.         if (debug > 4)
  408.             printf(
  409.         "slew adjtv = %s, adjts = %s, sys_offset = %s\n",
  410.                 tvtoa(&adjtv), umfptoa(0, residual, 9),
  411.                 mfptoa(offset_i, offset_f, 9));
  412. #endif
  413.     }
  414.  
  415.     sys_offset.l_ui = offset_i;
  416.     sys_offset.l_uf = offset_f;
  417.  
  418.     if (adjtv.tv_usec == 0)
  419.         return;
  420.     if (adjtime(&adjtv, &oadjtv) != 0)
  421.         syslog(LOG_ERR, "Can't do time adjustment: %m");
  422. #ifdef DEBUGRS6000
  423.     syslog(LOG_ERR, "adj_systime(%s): offset = %s%s\n",
  424.             mfptoa((adj<0?-1:0), adj, 9), isneg?"-":"",
  425.             umfptoa(offset_i, offset_f, 9));
  426.     syslog(LOG_ERR, "%d %d %d %d\n", (int) adjtv.tv_sec,
  427.         (int) adjtv.tv_usec, (int) oadjtv.tv_sec, (int)
  428.         oadjtv.tv_usec);
  429. #endif /* DEBUGRS6000 */
  430.     if (oadjtv.tv_sec != 0 || oadjtv.tv_usec != 0) {
  431.         syslog(LOG_ERR, "Previous time adjustment didn't complete");
  432. #ifdef DEBUG
  433.         if (debug > 4)
  434.             printf(
  435.                 "Previous adjtime() incomplete, residual = %s\n",
  436.                 tvtoa(&oadjtv));
  437. #endif
  438.     }
  439. }
  440.  
  441. #ifdef USELIBKVM
  442. /*
  443.  * clock_parms - return the local clock tickadj and tick parameters
  444.  *
  445.  * This version uses the SunOS libkvm (or the bsd compatability version).
  446.  */
  447. void
  448. clock_parms(tickadj, tick)
  449.     u_long *tickadj;
  450.     u_long *tick;
  451. {
  452.     register int n;
  453.     register kvm_t *kd;
  454.  
  455.     static struct nlist nl[] = {
  456. #define N_TICKADJ 0
  457.         { "_tickadj" },
  458. #define N_TICK 1
  459.         { "_tick" },
  460.         { "" },
  461.     };
  462.  
  463.     if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL) {
  464.         syslog(LOG_ERR, "kvm_open failed");
  465.         exit(3);
  466.     }
  467.     if (kvm_nlist(kd, nl) != 0) {
  468.         syslog(LOG_ERR, "kvm_nlist failed");
  469.         exit(3);
  470.     }
  471.     if (kvm_read(kd, nl[N_TICKADJ].n_value, tickadj, sizeof(*tickadj)) !=
  472.         sizeof(*tickadj)) {
  473.         syslog(LOG_ERR, "kvm_read tickadj failed");
  474.         exit(3);
  475.     }
  476.     if (kvm_read(kd, nl[N_TICK].n_value, tick, sizeof(*tick)) !=
  477.         sizeof(*tick)) {
  478.         syslog(LOG_ERR, "kvm_read tick failed");
  479.         exit(3);
  480.     }
  481.     if (kvm_close(kd) < 0) {
  482.         syslog(LOG_ERR, "kvm_close failed");
  483.         exit(3);
  484.     }
  485. #undef N_TICKADJ
  486. #undef N_TICK
  487. }
  488. #endif /* USELIBKVM */
  489.  
  490.  
  491. #ifdef READKMEM
  492. /*
  493.  * clock_parms - return the local clock tickadj and tick parameters
  494.  *
  495.  * Note that this version grovels about in /dev/kmem to determine
  496.  * these values.  This probably should be elsewhere.
  497.  */
  498. void
  499. clock_parms(tickadj, tick)
  500.     u_long *tickadj;
  501.     u_long *tick;
  502. {
  503.     register int i;
  504.     int kmem;
  505. #ifdef    NeXT
  506. #define N_NAME n_un.n_name
  507.     static struct nlist nl[] =
  508.     {    {{"_tickadj"}},
  509.         {{"_tick"}},
  510.         {{""}},
  511.     };
  512. #else
  513. #define N_NAME n_name
  514.     static struct nlist nl[] =
  515.     {    {"_tickadj"},
  516.         {"_tick"},
  517.         {""},
  518.     };
  519. #endif
  520.     static char *kernelnames[] = {
  521.         "/vmunix",
  522.         "/unix",
  523.         "/mach",
  524.         "/386bsd",
  525. #ifdef    KERNELFILE
  526.         KERNELFILE,
  527. #endif
  528.         NULL
  529.     };
  530.     struct stat stbuf;
  531.     int vars[2];
  532.  
  533. #define    K_TICKADJ    0
  534. #define    K_TICK        1
  535.     /*
  536.      * Read clock parameters from kernel
  537.      */
  538.     kmem = open("/dev/kmem", O_RDONLY);
  539.     if (kmem < 0) {
  540.         syslog(LOG_ERR, "Can't open /dev/kmem for reading: %m");
  541. #ifdef    DEBUG
  542.         if (debug)
  543.             perror("/dev/kmem");
  544. #endif
  545.         exit(3);
  546.     }
  547.  
  548.     for (i = 0; kernelnames[i] != NULL; i++) {
  549.         if (stat(kernelnames[i], &stbuf) == -1)
  550.             continue;
  551.         if (nlist(kernelnames[i], nl) >= 0)
  552.             break;
  553.     }
  554.     if (kernelnames[i] == NULL) {
  555.         syslog(LOG_ERR,
  556.           "Clock init couldn't find kernel as either /vmunix or /unix");
  557.         exit(3);
  558.     }
  559.  
  560.     for (i = 0; i < (sizeof(vars)/sizeof(vars[0])); i++) {
  561.         off_t where;
  562.  
  563.         vars[i] = 0;
  564.         if ((where = nl[i].n_value) == 0) {
  565.             syslog(LOG_ERR, "Unknown kernal var %s",
  566.                    nl[i].N_NAME);
  567.             continue;
  568.         }
  569.         if (lseek(kmem, where, L_SET) == -1) {
  570.             syslog(LOG_ERR, "lseek for %s fails: %m",
  571.                    nl[i].N_NAME);
  572.             continue;
  573.         }
  574.         if (read(kmem, &vars[i], sizeof(int)) != sizeof(int)) {
  575.             syslog(LOG_ERR, "read for %s fails: %m",
  576.                    nl[i].N_NAME);
  577.         }
  578.     }
  579.     close(kmem);
  580.  
  581.     *tickadj = (u_long)vars[K_TICKADJ];
  582.     *tick = (u_long)vars[K_TICK];
  583.  
  584. #undef    K_TICKADJ
  585. #undef    K_TICK
  586. #undef    N_NAME
  587. }
  588. #endif /* READKMEM */
  589.  
  590. #ifdef NOKMEM
  591. /*
  592.  * clock_parms - return the local clock tickadj and tick parameters
  593.  *
  594.  * Note that this version uses static values!
  595.  */
  596. void
  597. clock_parms(tickadj, tick)
  598.     u_long *tickadj;
  599.     u_long *tick;
  600. {
  601. #ifndef HZ
  602. #define HZ    60
  603. #endif
  604. #ifdef RS6000
  605.     *tickadj = 1000;
  606. #else
  607.     *tickadj = 500 / HZ;
  608. #endif /*RS6000*/
  609.     *tick = 1000000L / HZ;
  610. #ifdef    DEBUG
  611.     if (debug)
  612.         printf("NOTE: Using preset values for tick and tickadj !!\n");
  613. #endif
  614. }
  615. #endif /*NOKMEM*/
  616.