Main Page | Class List | File List | Class Members | File Members

adjtimex.c

Go to the documentation of this file.
00001 /*
00002 #define DEBUG
00003 
00004         adjtimex - display or set the kernel time variables
00005 
00006 
00007         AUTHORS
00008                 ssd@nevets.oau.org (Steven S. Dick)
00009                 jrv at comcast.net (Jim Van Zandt)
00010 
00011 */
00012 
00013 #define _GNU_SOURCE             /* strptime is a GNU extension */
00014 
00015 #include <ctype.h>
00016 #include <errno.h>
00017 #include <fcntl.h>
00018 #include <getopt.h>
00019 #include <mat.h>
00020 #include <math.h>
00021 #include <netdb.h>
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <sys/socket.h>
00026 #include <sys/stat.h>
00027 #include <sys/time.h>
00028 #include <sys/timex.h>
00029 #include <sys/types.h>
00030 #include <syscall.h>
00031 #include <time.h>
00032 #include <unistd.h>
00033 #include <utmp.h>
00034 #include <fcntl.h>
00035 #include <sys/ioctl.h>
00036 #include <linux/rtc.h>
00037 
00038 #ifdef __alpha__
00039 extern int adjtimex(struct timex *);
00040 #else
00041 #ifdef __ia64__
00042 extern int adjtimex(struct timex *);
00043 #else
00044 _syscall1(int, adjtimex, struct timex *, txcp)
00045 #endif
00046 #endif
00047 int F_print = 0;
00048 
00049 #ifndef LOG_PATH
00050 #define LOG_PATH "/var/log/clocks.log"
00051 #endif
00052 #ifndef WTMP_PATH
00053 #define WTMP_PATH "/var/log/wtmp"
00054 #endif
00055 
00056 static unsigned long epoch = 1900; /* year corresponding to 0x00   */
00057  
00058  /* Here the information for CMOS clock adjustments is kept. */
00059 #define ADJPATH "/etc/adjtime"
00060 
00061  /* used for debugging the code. */
00062  /* #define DEBUG */
00063 
00064 #define RTC_JITTER .000025      /* assumed error in reading CMOS clock (sec) */
00065 #define SECONDSPERDAY 86400
00066 #define BUFLEN 128
00067 
00068 static int cmos_fd = -1;
00069 
00070 /* to enable use of /dev/rtc interface, we would initialize
00071    using_dev_rtc to -1.  However, reading /dev/rtc does not wait until
00072    the beginning of the next second.  It only returns the current
00073    timer value, so it's only accurate to 1 sec which isn't good enough
00074    for us.  I see this comment in drivers/char/rtc.c, function
00075    rtc_get_rtc_time(), in the kernel sources:
00076 
00077          * read RTC once any update in progress is done. The update
00078          * can take just over 2ms. We wait 10 to 20ms. There is no need to
00079          * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP.
00080          * If you need to know *exactly* when a second has started, enable
00081          * periodic update complete interrupts, (via ioctl) and then 
00082          * immediately read /dev/rtc which will block until you get the IRQ.
00083          * Once the read clears, read the RTC time (again via ioctl). Easy.
00084 
00085    However it doesn't say how to restore the interrupt setup.  Until I
00086    find out about that, I'll continue to use the poll-wait.  */
00087 static int using_dev_rtc = 0;
00088 
00089 struct hack {
00090   double ref;                   /* reference time for time hack */
00091   double sigma_ref;             /* expected error in above (or zero if
00092                                    no reference time available) */
00093   time_t log;                   /* reference time as time_t */
00094   double sys;                   /* system time */
00095   int tick;                     /* "tick" system parameter */
00096   int freq;                     /* "freq" system parameter */
00097   char sys_ok;                  /* nonzero if system clock undisturbed
00098                                    during previous period */
00099   double cmos;                  /* CMOS time */
00100   char cmos_ok;                 /* nonzero if cmos clock undisturbed
00101                                    during previous period */
00102   char valid;                   /* bit flags (see below) */
00103   double sys_rate, sys_sigma;
00104   double cmos_rate, cmos_sigma;
00105   double relative_rate, relative_sigma;
00106 } prev;
00107 
00108 /* constants for `valid' member of struct hack */
00109 #define CMOS_VALID 1
00110 #define SYS_VALID 2
00111 #define REF_VALID 4
00112 
00113 struct cmos_adj
00114 {
00115   double ca_factor;
00116   long ca_adj_time;
00117   double ca_remainder;
00118 } ca;
00119 
00120 struct timex txc;
00121 
00122 #define HELP 131
00123 
00124 struct option longopt[]=
00125 {
00126   {"adjust", 2, NULL, 'a'},
00127   {"compare", 2, NULL, 'c'},
00128   {"log", 2, NULL, 'l'},
00129   {"esterror", 1, NULL, 'e'},
00130   {"frequency", 1, NULL, 'f'},
00131   {"host", 1, NULL, 'h'},
00132   {"help", 0, NULL, HELP},
00133   {"interval", 1, NULL, 'i'},
00134   {"maxerror", 1, NULL, 'm'},
00135   {"offset", 1, NULL, 'o'},
00136   {"print", 0, NULL, 'p'},
00137   {"review", 2, NULL, 'r'},
00138   {"singleshot", 1, NULL, 's'},
00139   {"status", 1, NULL, 'S'},
00140   {"reset", 0, NULL, 'R'},
00141   {"timeconstant", 1, NULL, 'T'},
00142   {"tick", 1, NULL, 't'},
00143   {"utc", 0, NULL, 'u'},
00144   {"version", 0, NULL, 'v'},
00145   {"watch", 0, NULL, 'w'},
00146   {0,0,0,0}
00147 };
00148 
00149 int adjusting = 0;
00150 int comparing = 0;
00151 int logging = 0;
00152 int reviewing = 0;
00153 int resetting = 0;              /* nonzero if need to call
00154                                    reset_time_status() */
00155 int interval = 10;
00156 int count = 8;
00157 int marked;
00158 int universal = 0;
00159 int watch;                      /* nonzero if time specified on command line */
00160 int undisturbed_sys = 0;
00161 int undisturbed_cmos = 0;
00162 char *log_path = LOG_PATH;
00163 
00164 char *timeserver;               /* points to name of timeserver */
00165 
00166 void compare(void);
00167 void failntpdate();
00168 void reset_time_status(void);
00169 static double compare_cmos_sys(void);
00170 struct cmos_adj *get_cmos_adjustment(void);
00171 void log_times(void);
00172 int valid_system_rate(double ftime_sys, double ftime_ref, double sigma_ref);
00173 int valid_cmos_rate(double ftime_cmos, double ftime_ref, double sigma_ref);
00174 void sethackent(void);
00175 void endhackent(void);
00176 struct hack *gethackent(void);
00177 void puthackent(struct hack *ph);
00178 time_t mkgmtime(struct tm *tp);
00179 int compare_tm(struct tm *first, struct tm *second);
00180 static void *xmalloc(int n);
00181 static void *xrealloc(void *pv, int n);
00182 void review(void);
00183 void kalman_update(double *x, int xr, double *p, double *h,
00184                    double *z, int zr, double *r);
00185 
00186 void
00187 usage(void)
00188 {
00189   char msg[]=
00190 "\n"
00191 "Usage: adjtimex  [OPTION]... \n"
00192 "Mandatory or optional arguments to long options are mandatory or optional\n"
00193 "for short options too.\n"
00194 "\n"
00195 "Get/Set Kernel Time Parameters:\n"
00196 "       -p, --print               print values of kernel time variables\n"
00197 "       -t, --tick val            set the kernel tick interval in usec\n"
00198 "       -f, --frequency newfreq   set system clock frequency offset\n"
00199 "       -s, --singleshot adj      slew the system clock by adj usec\n"
00200 "       -S, --status val          set kernel clock status\n"
00201 "       -R, --reset               reset status after setting parameters\n"
00202 "                                 (needed for early kernels)\n"
00203 "       -o, --offset adj          add a time offset of adj usec\n"
00204 "       -m, --maxerror val        set maximum error (usec)\n"
00205 "       -e, --esterror val        set estimated error (usec)\n"
00206 "       -T, --timeconstant val    set phase locked loop time constant\n"
00207 "       -a, --adjust[=count]      set system clock parameters per CMOS \n"
00208 "                                 clock or (with --review) log file\n"
00209 "\n"
00210 "Estimate Systematic Drifts:\n"
00211 "       -c, --compare[=count]     compare system and CMOS clocks\n"
00212 "       -i, --interval tim        set clock comparison interval (sec)\n"
00213 "       -l, --log[=file]          log current times to file\n"
00214 "           --host timeserver     query the timeserver\n"
00215 "       -w, --watch               get current time from user\n"
00216 "       -r, --review[=file]       review clock log file, estimate drifts\n"
00217 "       -u, --utc                 the CMOS clock is set to UTC\n"
00218 "\n"
00219 "Informative Output:\n"
00220 "           --help                print this help, then exit\n"
00221 "       -v, --version             print adjtimex program version, then exit\n"
00222 ;
00223 
00224   fputs(msg, stdout);
00225   exit(0);
00226 }
00227 
00228 /* return apparent value of USER_HZ in HZ, minimum nominal and maximum
00229    values for tick in TICK_MIN TICK_MID and TICK_MAX, and maximum
00230    frequency offset in MAXFREQ */
00231 void probe_time(int *hz, int *tick_min, int *tick_mid, int *tick_max,
00232                 long *maxfreq)
00233 {
00234   struct timex txc;
00235   int tick_orig, tick_lo, tick_try, tick_hi, i;
00236 
00237   txc.modes = 0;
00238   adjtimex(&txc);
00239   *maxfreq = txc.tolerance;
00240   tick_orig = tick_hi = txc.tick;
00241   tick_lo = tick_hi*2/3;
00242   for (i = 0; i < 15; i++)
00243     {                   /* conduct binary search for minimum
00244                            accepted tick value */
00245 //      printf(" %d < minimum accepted tick value <= %d\n", tick_lo, tick_hi);
00246       txc.tick = tick_try = (tick_lo + tick_hi)/2;
00247       txc.modes = ADJ_TICK;
00248       if (adjtimex(&txc) == -1) tick_lo = tick_try;
00249       else tick_hi = tick_try;
00250     }
00251   *tick_min = tick_hi;
00252   tick_lo = tick_orig;
00253   tick_hi = tick_lo*4/3;
00254   for (i = 0; i < 15; i++)
00255     {                   /* conduct binary search for maximum
00256                            accepted tick value */
00257 //      printf(" %d <= maximum accepted tick value < %d\n", tick_lo, tick_hi);
00258       txc.tick = tick_try = (tick_lo + tick_hi)/2;
00259       txc.modes = ADJ_TICK;
00260       if (adjtimex(&txc) == -1) tick_hi = tick_try;
00261       else tick_lo = tick_try;
00262     }
00263   *tick_max = tick_lo;
00264   *tick_mid = (*tick_min + *tick_max)/2;
00265   *hz = (900000/ *tick_min + 1100000/ *tick_max)/2;
00266 
00267   txc.tick = tick_orig;
00268   txc.modes = ADJ_TICK;
00269   adjtimex(&txc);               /* reset to original value */
00270 }
00271 
00272 int
00273 main(int argc, char *argv[])
00274 {
00275     int ret, saveerr, changes;
00276     extern char *optarg;
00277     int c;
00278 
00279     txc.modes = 0;
00280 
00281     while((c = getopt_long_only(argc, argv, 
00282                                 "a::c::l::e:f:h:i:m:o:prPPsPS:RT:t:uvw", 
00283                                 longopt, NULL)) != -1)
00284       {
00285         switch(c)
00286           {
00287           case 'a':
00288             adjusting = 1;
00289             if (optarg)
00290               count = atoi(optarg);
00291             break;
00292           case 'c':
00293             comparing = 1;
00294             if (optarg)
00295               count = atoi(optarg);
00296             break;
00297           case 'l':
00298             logging = 1;
00299             if (optarg)
00300               log_path = strdup(optarg);
00301             if (!log_path)
00302               {
00303                 fprintf (stderr, "insufficient memory\n");
00304                 exit(1);
00305               }
00306             break;
00307           case 'h':
00308             timeserver = strdup(optarg);
00309             if (!timeserver)
00310               {
00311                 fprintf (stderr, "insufficient memory\n");
00312                 exit(1);
00313               }
00314             logging = 1;
00315             break;
00316           case 'r':
00317             reviewing = 1;
00318             if (optarg)
00319               log_path = strdup(optarg);
00320             if (!log_path)
00321               {
00322                 fprintf (stderr, "insufficient memory\n");
00323                 exit(1);
00324               }
00325             break;
00326           case 'i':
00327             interval = atoi (optarg);
00328             if (interval <= 1 || interval > 1000)
00329               {
00330                 fprintf (stderr, "repeat interval out of range\n");
00331                 exit (1);
00332               }
00333             break;
00334           case 'p':
00335             F_print = 1;
00336             break;
00337           case 'o':
00338             txc.offset = atol(optarg);
00339             txc.modes |= ADJ_OFFSET;
00340             break;
00341           case 's':
00342             txc.offset = atol(optarg);
00343             txc.modes |= ADJ_OFFSET_SINGLESHOT;
00344             break;
00345           case 'S':
00346             txc.status = atol(optarg);
00347             txc.modes |= ADJ_STATUS;
00348             break;
00349           case 'R': resetting = 1; break;
00350           case 'f':
00351             txc.freq = atol(optarg);
00352             txc.modes |= ADJ_FREQUENCY;
00353             break;
00354           case 'm':
00355             txc.maxerror = atol(optarg);
00356             txc.modes |= ADJ_MAXERROR;
00357             break;
00358           case 'e':
00359             txc.esterror = atol(optarg);
00360             txc.modes |= ADJ_ESTERROR;
00361             break;
00362           case 'T':
00363             txc.constant = atol(optarg);
00364             txc.modes |= ADJ_TIMECONST;
00365             break;
00366           case 't':
00367             txc.tick = atol(optarg);
00368             txc.modes |= ADJ_TICK;
00369             break;
00370           case 'u':
00371             universal = 1;
00372             break;
00373           case 'v':
00374             {
00375               printf("adjtimex %s\n", VERSION);
00376               exit(0);
00377             }
00378           case 'w':
00379             watch = 1;
00380             logging = 1;
00381             break;
00382           case HELP:
00383             usage();
00384             break;
00385           case '?':
00386           default:
00387             fprintf(stderr, "For valid options, try 'adjtimex --help'\n");
00388             exit(1);
00389           }
00390         }
00391 
00392     changes = txc.modes;
00393 
00394     if (count <= 0 ) {
00395       fprintf(stderr, "loop count out of range\n");
00396       exit(1);
00397     }
00398 
00399     if (reviewing)
00400       {
00401         review();
00402         exit(0);
00403       }
00404 
00405     if (adjusting || comparing)
00406       {
00407         if (changes || F_print)
00408           {
00409             fprintf(stderr, 
00410 "-adjust or -compare cannot be used with any other options that"
00411 " set values\n");
00412             exit(1);
00413           }
00414         compare();
00415       }    
00416 
00417     if (logging)
00418       {
00419         /*
00420         if (geteuid() != 0)
00421           {
00422             fprintf(stderr, "sorry, only root can record clock comparisons\n");
00423             exit(1);
00424           }
00425           */
00426         log_times();
00427         exit(0);
00428       }
00429 
00430     if ((txc.modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT && 
00431         txc.modes != ADJ_OFFSET_SINGLESHOT)
00432       {
00433         fprintf(stderr, "-singleshot cannot be used with "
00434                 "any other option except -print\n");
00435         usage();
00436       }
00437 
00438     errno = 0;
00439     ret = adjtimex(&txc);
00440     saveerr = errno;
00441     if (F_print) {
00442         printf("         mode: %d\n"
00443                "       offset: %ld\n"
00444                "    frequency: %ld\n"
00445                "     maxerror: %ld\n"
00446                "     esterror: %ld\n"
00447                "       status: %d\n"
00448                "time_constant: %ld\n"
00449                "    precision: %ld\n"
00450                "    tolerance: %ld\n"
00451                "         tick: %ld\n"
00452                "     raw time:  %ds %dus = %d.%06d\n",
00453                txc.modes,
00454                txc.offset,
00455                txc.freq,
00456                txc.maxerror,
00457                txc.esterror,
00458                txc.status,
00459                txc.constant,
00460                txc.precision,
00461                txc.tolerance,
00462                txc.tick,
00463                (int)txc.time.tv_sec, 
00464                (int)txc.time.tv_usec,
00465                (int)txc.time.tv_sec, 
00466                (int)txc.time.tv_usec);
00467         if (saveerr == 0 && ret != 0)
00468             printf(" return value = %d\n", ret);
00469     }
00470     if (ret != 0 && saveerr != 0) {
00471         if (ret != -1)
00472             fprintf(stderr, "%d ", ret);
00473         errno = saveerr;
00474         perror("adjtimex");
00475         {
00476           int hz, tick_min, tick_mid, tick_max;
00477           long maxfreq;
00478           probe_time(&hz, &tick_min, &tick_mid, &tick_max, &maxfreq);
00479           
00480           printf("for this kernel:\n"
00481                  "   USER_HZ = %d (nominally %d ticks per second)\n"
00482                  "   %d <= tick <= %d\n"
00483                  "   %ld <= frequency <= %ld\n",
00484                  hz, hz, tick_min, tick_max, -maxfreq, maxfreq);
00485         }
00486         exit(1);
00487     }
00488     if (changes && resetting)
00489       reset_time_status();
00490 
00491     return 0;
00492 }
00493 
00494 static inline void
00495 outb (short port, char val)
00496 {
00497 #ifdef USE_INLINE_ASM_IO
00498   __asm__ volatile ("out%B0 %0,%1"::"a" (val), "d" (port));
00499 
00500 #else
00501   lseek (cmos_fd, port, 0);
00502   write (cmos_fd, &val, 1);
00503 #endif
00504 }
00505 
00506 static inline unsigned char
00507 inb (short port)
00508 {
00509   unsigned char ret;
00510 
00511 #ifdef USE_INLINE_ASM_IO
00512   __asm__ volatile ("in%B0 %1,%0":"=a" (ret):"d" (port));
00513 
00514 #else
00515   lseek (cmos_fd, port, 0);
00516   read (cmos_fd, &ret, 1);
00517 #endif
00518   return ret;
00519 }
00520 
00521 void
00522 cmos_init ()
00523 {
00524   if (using_dev_rtc < 0)
00525     {
00526       cmos_fd = open ("/dev/rtc", O_RDONLY);
00527       if (cmos_fd >= 0)
00528         {
00529           using_dev_rtc = 1;
00530           return;
00531         }
00532       using_dev_rtc = 0;
00533     }
00534   else if (using_dev_rtc > 0)
00535     return;
00536 
00537 #ifdef USE_INLINE_ASM_IO
00538   if (ioperm (0x70, 2, 1))
00539     {
00540       fprintf (stderr, "clock: unable to get I/O port access\n");
00541       exit (1);
00542     }
00543 #else
00544   if (cmos_fd < 0) 
00545     cmos_fd = open ("/dev/port", O_RDWR);
00546   if (cmos_fd < 0)
00547     {
00548       perror ("unable to open /dev/port read/write : ");
00549       exit (1);
00550     }
00551   if (lseek (cmos_fd, 0x70, 0) < 0 || lseek (cmos_fd, 0x71, 0) < 0)
00552     {
00553       perror ("unable to seek port 0x70 in /dev/port : ");
00554       exit (1);
00555     }
00556 #endif
00557 }
00558 
00559 #define CMOS_READ(addr)      ({outb(0x70,(addr)|0x80); inb(0x71);})
00560 
00561 static inline int
00562 cmos_read_bcd (int addr)
00563 {
00564   int b;
00565 
00566   b = CMOS_READ (addr);
00567   return (b & 15) + (b >> 4) * 10;
00568 }
00569 
00570 static void
00571 cmos_read_time (struct tm *tm)
00572 {
00573   if (using_dev_rtc > 0)
00574     ioctl (cmos_fd, RTC_RD_TIME, tm);
00575   else
00576     {
00577       long i;
00578 
00579       /* read RTC exactly on falling edge of update flag */
00580       /* Wait for rise.... (may take up to 1 second) */
00581 
00582       for (i = 0; i < 10000000; i++)
00583         if (CMOS_READ (10) & 0x80)
00584           break;
00585 
00586       /* Wait for fall.... (must try at least 2.228 ms) */
00587 
00588       for (i = 0; i < 1000000; i++)
00589         if (!(CMOS_READ (10) & 0x80))
00590           break;
00591 
00592       /* The "do" loop is "low-risk programming" */
00593       /* In theory it should never run more than once */
00594       do
00595         {
00596           tm->tm_sec = cmos_read_bcd (0);
00597           tm->tm_min = cmos_read_bcd (2);
00598           tm->tm_hour = cmos_read_bcd (4);
00599           tm->tm_wday = cmos_read_bcd (6);
00600           tm->tm_mday = cmos_read_bcd (7);
00601           tm->tm_mon = cmos_read_bcd (8);
00602           tm->tm_year = cmos_read_bcd (9);
00603         }
00604       while (tm->tm_sec != cmos_read_bcd (0));
00605     }
00606 }
00607 
00608 static inline void 
00609 xusleep(long microseconds)
00610 {
00611   struct timespec ts;
00612 
00613   ts.tv_sec = microseconds/1000000;
00614   ts.tv_nsec = (microseconds - ts.tv_sec*1000000) * 1000;
00615 
00616   while (nanosleep (&ts, &ts) < 0)
00617     continue;
00618 }
00619 
00620 /* compare the system and CMOS clocks.  If "adjusting" is nonzero,
00621    adjust sytem time to match the CMOS clock. */
00622 void
00623 compare()
00624 {
00625   struct timex txc;
00626   struct tm tm;
00627   time_t cmos_time;
00628   time_t last_time;
00629   double cmos_sec, system_sec, dif, dif_prev = 0.;
00630   FILE *adj;
00631   double factor;
00632   double cmos_adjustment;
00633   double not_adjusted;
00634   int loops = 0;
00635   extern char *optarg;
00636   struct timeval now;
00637   int wrote_to_log = 0;
00638   int hz, tick_min, tick_mid, tick_max;
00639   long maxfreq;
00640 
00641   probe_time(&hz, &tick_min, &tick_mid, &tick_max, &maxfreq);
00642 
00643 
00644       /* Read adjustment parameters first */
00645   if ((adj = fopen (ADJPATH, "r")) == NULL)
00646     {
00647       perror (ADJPATH);
00648       exit (2);
00649     }
00650   if (fscanf (adj, "%lf %ld %lf", &factor, &last_time, &not_adjusted) < 0)
00651     {
00652       perror (ADJPATH);
00653       exit (2);
00654     }
00655   fclose (adj);
00656 #ifdef DEBUG
00657   /*
00658 cmos clock last adjusted at Tue Aug 26 11:43:57 1997 (= 872610237)
00659           current cmos time Tue Aug 26 21:27:05 1997 EDT (= 872645225)
00660 */
00661   {
00662     struct tm bdt;
00663     if (universal)
00664       {
00665         bdt = *gmtime(&last_time);
00666         (void)mkgmtime(&bdt);   /* set tzname */
00667       }
00668     else
00669       {
00670         bdt = *localtime(&last_time);
00671         (void)mktime(&bdt);     /* set tzname */
00672       }
00673     printf ("cmos clock last adjusted %.24s %s "
00674             "(= %ld)\n", 
00675             ctime(&last_time), tzname[tm.tm_isdst?1:0], (long) last_time);
00676   }
00677 #endif
00678 
00679   cmos_init ();
00680 
00681   while (count != 0)
00682     {
00683       if (count > 0) count--;
00684 
00685       cmos_read_time (&tm);
00686 
00687       /* fetch system time immediately */
00688       gettimeofday (&now, NULL);
00689 
00690       tm.tm_mon--;              /* DOS uses 1 base */
00691       tm.tm_wday -= 3;          /* DOS uses 3 - 9 for week days */
00692       tm.tm_isdst = -1;         /* don't know whether it's daylight */
00693       if ((tm.tm_year += (epoch - 1900)) <= 69)
00694         tm.tm_year += 100;
00695 
00696       if (universal)
00697         cmos_time = mkgmtime(&tm);
00698       else
00699         cmos_time = mktime (&tm);
00700       /* printf ("%s", asctime (&tm)); */
00701 
00702       system_sec = now.tv_sec + .000001*now.tv_usec;
00703 
00704       /* If we're adjusting time parameters, we want to make a log
00705          entry only for the first two comparisons (before we change
00706          the parameters).  Otherwise, we want to log the first and last
00707          comparisons in order to maximize the duration. */
00708       if (logging && (wrote_to_log ^ (adjusting?(loops==0):(count!=0))))
00709         {
00710           struct hack h;
00711 
00712           h.ref = 0;
00713           h.sigma_ref = 0;
00714           h.log = (time_t)system_sec;
00715           h.sys = system_sec;
00716           txc.modes = 0;
00717           adjtimex(&txc);
00718           h.tick = txc.tick;
00719           h.freq = txc.freq;
00720           h.sys_ok = wrote_to_log;
00721           h.cmos = cmos_time;
00722           h.cmos_ok = wrote_to_log;
00723           puthackent(&h);
00724           wrote_to_log = 1;
00725         }
00726 
00727 #ifdef DEBUG
00728       printf ("       current cmos time %.24s %s (= %ld)\n", 
00729               asctime(&tm), tzname[tm.tm_isdst?1:0], (long) cmos_time);
00730 #endif
00731       cmos_adjustment = ((double) (cmos_time - last_time))
00732         * factor / SECONDSPERDAY
00733         + not_adjusted;
00734       cmos_sec = cmos_time + cmos_adjustment;
00735 #ifdef DEBUG
00736       printf (
00737 "           time since last adjustment %10.6f days (= %9d sec)\n",
00738               (int) (cmos_time - last_time) / (double)SECONDSPERDAY,
00739               (int) (cmos_time - last_time));
00740       printf (
00741 "                               factor %10.6f sec/day\n",
00742               factor);
00743       printf (
00744 "                           adjustment %10.6f + %7.6f = %7.6f sec\n",
00745                ((double) (cmos_time - last_time))*factor/SECONDSPERDAY,
00746                not_adjusted, cmos_adjustment);
00747 #endif
00748       dif = system_sec - cmos_sec;
00749 
00750       txc.modes = 0;
00751       if (adjtimex (&txc) < 0) {perror ("adjtimex"); exit(1);}
00752 /*
00753                                        --- current ---   -- suggested --
00754 cmos time     system-cmos  error_ppm   tick      freq    tick      freq
00755 1094939320  -14394.974188
00756 1094939330  -14394.971203      298.5  10001   1290819
00757 1094939340  -14394.968203      300.0  10001   1290819    9998   1289097
00758 */
00759 
00760       if (! marked++ )
00761         {
00762           if (interval)
00763             printf (
00764 "                                      --- current ---   -- suggested --\n"
00765 "cmos time     system-cmos  error_ppm   tick      freq    tick      freq\n");
00766           else
00767             printf (
00768 "cmos time     system-cmos  error_ppm   tick      freq\n");
00769         }
00770       printf ("%9ld  %11.6f",
00771               (long) cmos_sec,
00772               dif);
00773       if (++loops > 1)
00774         {                       /* print difference in rates */
00775 #define SHIFT (1<<16)
00776           double second_diff, error_ppm;
00777           second_diff = dif - dif_prev;
00778           error_ppm = second_diff/interval*1000000;
00779           printf ("%11.1f %6ld %9ld",
00780                   error_ppm,
00781                   txc.tick,
00782                   txc.freq);
00783           if (loops > 2)
00784             {                   /* print suggested values */
00785               long tick_delta = 0, freq_delta = 0;
00786               
00787               tick_delta = ceil((-error_ppm + txc.freq/SHIFT - hz)/hz);
00788               error_ppm += tick_delta*hz;
00789               freq_delta = -error_ppm*SHIFT;
00790               printf("  %6ld %9ld\n",
00791                      txc.tick + tick_delta, txc.freq + freq_delta);
00792               if (loops > 4 && adjusting)
00793                 {
00794                   txc.modes = ADJ_FREQUENCY | ADJ_TICK;
00795                   txc.tick += tick_delta;
00796                   txc.freq += freq_delta;
00797                   if (adjtimex (&txc) < 0) 
00798                     {
00799                       perror ("adjtimex"); 
00800                       exit(1);
00801                     }
00802                   if (resetting)
00803                     reset_time_status();
00804                   loops -= 3;
00805                 }
00806             }
00807           else
00808             printf("\n");
00809             }
00810       else
00811         printf("\n");
00812       dif_prev = dif;
00813       if (interval == 0)
00814         break;
00815       if (count)                /* if it is not the last round */
00816         xusleep (interval*1000000L - 900000); /* reading RTC takes 1 sec */
00817     }
00818 }
00819 
00820 void reset_time_status()
00821 {
00822   /* Using the adjtimex(2) system call to set any time parameter makes
00823      an early kernel (2.0.40 and 2.4.18 or later are reportedly okay)
00824      think the clock is synchronized with an external time source, so
00825      it sets the kernel variable time_status to TIME_OK.  Thereafter,
00826      it will periodically adjust the CMOS clock to match.  We prevent
00827      this by setting the clock, because that has the side effect of
00828      resetting time_status to TIME_BAD.  We try not to actually change
00829      the clock setting. */
00830   struct timeval tv1, tv2;
00831   long carry_sec, overhead_usec;
00832   if (gettimeofday(&tv1, NULL)) {perror("adjtimex"); exit(1);}
00833   if (gettimeofday(&tv2, NULL)) {perror("adjtimex"); exit(1);}
00834   overhead_usec = tv2.tv_usec - tv1.tv_usec + 
00835     1000000*(tv2.tv_sec - tv1.tv_sec);
00836   tv2.tv_usec += overhead_usec;
00837   carry_sec = tv2.tv_usec/1000000;
00838   tv2.tv_sec += carry_sec;
00839   tv2.tv_usec -= 1000000*carry_sec;
00840   if (settimeofday(&tv2, NULL)) {perror("adjtimex"); exit(1);}
00841 }  
00842 
00843 void log_times()
00844 {
00845 #ifdef NET_TIME_CLIENT
00846   struct protoent proto;
00847   int sockfd, val, len, c;
00848   struct sockaddr sa={AF_INET, "127.0.0.1"};
00849   struct hostent he;
00850 #endif
00851   double sigma_ref, cmos_ahead;
00852   char ch, buf[64], *s;
00853   int n, ret;
00854   struct timeval tv_sys;
00855   struct timezone tz;
00856   struct tm bdt;
00857   time_t when, tref;
00858   double ftime_ref, ftime_sys, ftime_cmos;
00859 
00860   if (watch)
00861     {
00862       while(1) {
00863         printf("Please press <enter> when you know the time of day: ");
00864         ch = getchar();
00865         gettimeofday(&tv_sys, &tz);
00866         when = tv_sys.tv_sec;
00867         bdt = *localtime(&when); /* set default values for most fields */
00868         strftime(buf, sizeof(buf), "%Z", &bdt);
00869         printf("  system time then was %.24s %s\n", asctime(&bdt), buf);
00870         ftime_sys = tv_sys.tv_sec + tv_sys.tv_usec*.000001;
00871         printf("What time was that according to your reference (%s)?\n"
00872                "(hh:mm:ss, or `r' to retry, or `q' to quit): ", buf);
00873         if (fgets(buf, sizeof(buf), stdin) == NULL) exit(1);
00874         s = buf;
00875         while(isspace(*s)) s++;
00876         if (*s == 'q') exit(0);
00877         if (*s == 'r') continue;
00878         
00879         strptime(buf, "%T", &bdt); /* set time fields from user input */
00880         printf("      reference time is %s", asctime(&bdt));
00881         tref = ftime_ref = mktime(&bdt); /* construct a time_t
00882                                             corresponding to the user
00883                                             input */
00884         printf(" mktime returns time of %s", ctime(&tref));
00885 
00886         printf("reference time - system time = %12.3f - %12.3f "
00887                "= %4.3f sec\n", 
00888                ftime_ref, ftime_sys, ftime_ref - ftime_sys);
00889 
00890         printf("How big could the error be in this, in seconds?\n"
00891                "(or `r' to retry, or `q' to quit) [.5] : ");
00892         if (fgets(buf, sizeof(buf), stdin) == NULL) exit(1);
00893         s = buf;
00894         while(isspace(*s)) s++;
00895         if (*s == 'q') exit(0);
00896         if (*s == 'r') continue;
00897 
00898         n = sscanf(buf, "%lf", &sigma_ref);
00899         if (n < 1)
00900           sigma_ref = .5;
00901         else if (sigma_ref < .02)
00902           {
00903             printf("You get credit for .02 sec.\n");
00904             sigma_ref = .02;
00905           }
00906         break;
00907       }
00908     }
00909   else if (timeserver)
00910     {
00911       FILE *ifile;
00912       char command[128];
00913       char buf[BUFLEN];
00914       char *str[5];
00915       int i, n=0;
00916       double d, mean=0, val, var=0, num=0;
00917 
00918 #ifdef NTPDATE_STUB
00919       ifile = fopen("../ntpdate-sample", "r");
00920 #else
00921       sprintf(command, "ntpdate -d %.32s ", timeserver);
00922       ifile = popen(command, "r");
00923 #endif
00924       if (ifile == NULL) failntpdate("call to ntpdate failed");
00925 
00926       /* read and save the significant lines, which should look like this:
00927 filter offset: -0.02800 -0.01354 -0.01026 -0.01385
00928 offset -0.013543
00929  1 Sep 11:51:23 ntpdate[598]: adjust time server 1.2.3.4 offset -0.013543 sec
00930  */
00931       while(fgets(buf, BUFLEN, ifile))
00932         if (strstr(buf, "offset") && n < 4)
00933           str[n++] = strdup(buf);
00934       fclose(ifile);
00935       if (n != 3) failntpdate("cannot understand ntpdate output");
00936       gettimeofday(&tv_sys, &tz);
00937       ftime_sys = tv_sys.tv_sec;
00938 
00939 
00940       /* ntpdate selects the offset from one of its samples (the one
00941          with the shortest round-trip delay?) */
00942       ftime_ref = ftime_sys + atof(strstr(str[2], "offset") + 6);
00943 
00944       {
00945         time_t now = (time_t)ftime_ref;
00946         bdt = *gmtime(&now);
00947         printf("      reference time is %s", ctime(&now));
00948         printf("reference time - system time = %12.3f - %12.3f "
00949                "= %4.3f sec\n", 
00950                ftime_ref, ftime_sys, ftime_ref - ftime_sys);
00951       }
00952 
00953       /* The first saved line shows the offsets for each of ntpdate's
00954          samples.  Find their variance, which we will use to indicate
00955          the accuracy of the offset we're using.  This is probably
00956          conservative, since the offset we're using is probably not
00957          close to the mean. */
00958       s = strstr(str[0], ":");
00959       if (s++ == NULL) failntpdate("cannot understand ntpdate output");
00960       for (i = 0; i < 4; i++)
00961         {
00962           val = strtod(s, &s);
00963           d = val - mean;
00964           num += 1.;
00965           var = (num-1)/num*(var + d*d/num);
00966           mean = ((num-1.)*mean + val)/num;
00967         }
00968       sigma_ref = sqrt(var);
00969 
00970 #ifdef OWN_IMPLEMENTATION
00971       /* this is not even close to working yet */
00972       proto = *getprotobyname("UDP");
00973       sockfd = socket(AF_INET, SOCK_DGRAM, proto.p_proto);
00974       he = *gethostbyname("localhost");
00975       len = he.h_length;
00976       memcpy(sa.sa_data, he.h_addr_list[0], len);
00977 
00978 #ifdef SEND
00979       val = connect(sockfd, &sa, len);
00980       if (val == -1) {perror("connect"); exit(1);}
00981       /*
00982         int  connect(int  sockfd, struct sockaddr *serv_addr, int addrlen );
00983         int  send(int  s,  const void *msg, int len , unsigned int flags);
00984         int sendto(int s, const void *msg, int  len  unsigned  int
00985         flags, const struct sockaddr *to, int tolen);
00986         */
00987 #endif /* SEND */
00988       val = sendto(sockfd, (const void *)" ", 1, 0, &sa, len);
00989       if (val == -1) {perror("sendto"); exit(1);}
00990       sigma_ref = .010;
00991 #endif /* OWN_IMPLEMENTATION */
00992 
00993     }
00994   else                          /* no absolute time reference */
00995     {
00996       time_t now;
00997       gettimeofday(&tv_sys, &tz);
00998       now = (time_t)tv_sys.tv_sec;
00999       bdt = *gmtime(&now);
01000       ftime_sys = tv_sys.tv_sec + tv_sys.tv_usec*.000001;
01001       ftime_ref = 0;
01002       sigma_ref = 0;
01003     }
01004 
01005   cmos_ahead = compare_cmos_sys();
01006   ftime_cmos = ftime_sys + cmos_ahead;
01007 
01008   /*
01009                  -reference-time- --------system-time---------- --cmos-time----
01010 1997-06-14 20:22 888888888.888 -3 888888888.888 10000 6666666 n 888888888.888 y
01011 */
01012 
01013   {
01014     struct hack h;
01015     h.ref = ftime_ref;
01016     h.sigma_ref = sigma_ref;
01017     h.log = (time_t)ftime_ref;
01018     h.sys = ftime_sys;
01019     txc.modes = 0;
01020     ret = adjtimex(&txc);
01021     h.tick = txc.tick;
01022     h.freq = txc.freq;
01023     h.sys_ok = valid_system_rate(ftime_sys, ftime_ref, sigma_ref);
01024     h.cmos = ftime_cmos;
01025     h.cmos_ok = valid_cmos_rate(ftime_cmos, ftime_ref, sigma_ref);
01026     puthackent(&h);
01027   }
01028 }
01029 
01030 void failntpdate(char *s)
01031 {
01032   fprintf(stderr, "%s\n", s);
01033   exit(1);
01034 }
01035 
01036 int valid_system_rate(double ftime_sys, double ftime_ref, double sigma_ref)
01037 {
01038   int n;
01039   int default_answer;
01040   int ch;
01041   char buf[BUFLEN];
01042   struct hack *ph;
01043   struct cmos_adj *pca = get_cmos_adjustment();
01044 
01045   sethackent();
01046   for (n = 0; (ph = gethackent()); n++)
01047     prev = *ph;                 /* fetch last line from logfile */
01048   endhackent();
01049   if (n == 0)
01050     {
01051       printf("No previous clock comparison in log file\n");
01052       return 0;
01053     }
01054   undisturbed_sys = undisturbed_cmos = 1;
01055 
01056   printf("Last clock comparison was at %.24s\n", ctime(&prev.log));
01057   
01058   if (txc.tick == prev.tick && txc.freq == prev.freq)
01059     printf("Kernel time variables are unchanged - good.\n");
01060   else
01061     {
01062       printf("Kernel time variables have changed - bad.\n");
01063       undisturbed_sys = 0;
01064     }
01065   if (txc.status & 64)
01066     printf("System clock is currently not disciplined - good.\n");
01067   else
01068     {
01069       printf("System clock is synchronized (by ntpd?) - bad.\n");
01070       undisturbed_sys = 0;
01071     }
01072 
01073   {
01074     time_t lastboot, newtime;
01075     struct utmp *up;
01076     
01077     lastboot = newtime = prev.sys - 1;
01078     utmpname(WTMP_PATH);
01079     setutent();
01080     printf("Checking wtmp file...\n");
01081     while((up = getutent()))
01082       {
01083         if (up->ut_type == BOOT_TIME) 
01084           lastboot = up->ut_time;
01085         if (up->ut_type == NEW_TIME) 
01086           newtime = up->ut_time;
01087       }
01088     endutent();
01089     if (lastboot < prev.sys)
01090       printf("System has not booted since %.24s - good.\n", 
01091              ctime(&prev.log));
01092     else
01093       {
01094         printf("System was booted at %.24s - bad.\n", ctime(&lastboot));
01095         undisturbed_sys = 0;
01096       }
01097     if (newtime < prev.sys)
01098       printf("System time has not been changed since %.24s - good.\n", 
01099              ctime(&prev.log));
01100     else
01101       {
01102         printf("System time was reset at %.24s - bad.\n", ctime(&newtime));
01103         undisturbed_sys = 0;
01104       }
01105   }
01106 
01107   if (pca)
01108     {
01109       printf("Checking %s...\n", ADJPATH);
01110       if (pca->ca_adj_time < prev.log)
01111         printf(
01112 "/sbin/clock has not set system time and adjusted the cmos clock \n"
01113 "since %.24s - good.\n", 
01114 ctime(&prev.log));
01115       else
01116         {
01117           printf("/sbin/clock set system time and adjusted the cmos clock \n"
01118                  "at %.24s - bad.\n", 
01119                  ctime(&pca->ca_adj_time));
01120           undisturbed_sys = undisturbed_cmos = 0;
01121         }
01122     }
01123 
01124   do 
01125     {
01126       default_answer = undisturbed_sys?'y':'n';
01127       printf("\nAre you sure that, since %.24s,\n", ctime(&prev.log));
01128       printf("  the system clock has run continuously,\n");
01129       printf("  it has not been reset with `date' or `/sbin/clock`,\n");
01130       printf("  the kernel time variables have not been changed, and\n");
01131       printf("  the computer has not been suspended? (y/n) [%c] ", 
01132              default_answer);
01133       fgets(buf, BUFLEN, stdin);
01134       ch = buf[0];
01135       if (ch == '\n') ch = default_answer;
01136     } while (ch != 'n' && ch != 'y');
01137 
01138   if ((undisturbed_sys = (ch == 'y')))
01139     {
01140       double drift_sys_ppm, err_sys_ppm;
01141       int digits;
01142       drift_sys_ppm = ((ftime_sys - ftime_ref) - (prev.sys - prev.ref))*
01143         1.e6/(ftime_ref - prev.ref);
01144       err_sys_ppm = (prev.sigma_ref + sigma_ref)*1.e6/
01145         (ftime_ref - prev.ref);
01146       digits = -(int)floor(log(.5*sigma_ref)/log(10.));
01147       if (digits < 0) digits = 0;
01148       
01149       printf("The estimated error in system time is %.*f +- %.*f ppm\n", 
01150              digits, drift_sys_ppm, digits, err_sys_ppm);
01151     }
01152 
01153   return undisturbed_sys;
01154 }
01155 
01156 int valid_cmos_rate(double ftime_cmos, double ftime_ref, double sigma_ref)
01157 {
01158   int default_answer;
01159   int ch;
01160   char buf[BUFLEN];
01161 
01162   default_answer = undisturbed_cmos?'y':'n';
01163   do
01164     {
01165       printf("\nAre you sure that, since %.24s,\n", ctime(&prev.log));
01166       printf("  the real time clock (cmos clock) has run continuously,\n");
01167       printf("  it has not been reset with `/sbin/clock',\n");
01168       printf("  no operating system other than Linux has been running, and\n");
01169       printf("  ntpd has not been running? (y/n) [%c] ", default_answer);
01170       fgets(buf, BUFLEN, stdin);
01171       ch = buf[0];
01172       if (ch == '\n') ch = default_answer;
01173     } while (ch != 'n' && ch != 'y');
01174   if ((undisturbed_cmos = (ch == 'y')))
01175     {
01176       double drift_cmos_ppm, err_cmos_ppm;
01177       int digits;
01178             
01179       drift_cmos_ppm = 
01180         ((ftime_cmos - ftime_ref) - (prev.cmos - prev.ref))*
01181         1.e6/(ftime_ref - prev.ref);
01182       err_cmos_ppm = (prev.sigma_ref + sigma_ref)*1.e6/
01183         (ftime_ref - prev.ref);
01184       digits = -(int)floor(log(.5*err_cmos_ppm)/log(10.));
01185       if (digits < 0) digits = 0;
01186       printf("The estimated error in the cmos clock is %.*f +- %.*f ppm\n", 
01187              digits, drift_cmos_ppm, digits, err_cmos_ppm);
01188     }
01189 
01190   return undisturbed_cmos;
01191 }
01192 
01193 struct cmos_adj *get_cmos_adjustment()
01194 {
01195   FILE *adj;
01196   static struct cmos_adj ca;
01197   if ((adj = fopen (ADJPATH, "r")) == NULL)
01198     {
01199       perror (ADJPATH);
01200       exit (2);
01201     }
01202   if (fscanf (adj, "%lf %ld %lf", 
01203               &ca.ca_factor, 
01204               &ca.ca_adj_time, 
01205               &ca.ca_remainder) < 0)
01206     {
01207       perror (ADJPATH);
01208       exit (2);
01209     }
01210   fclose (adj);
01211 #ifdef DEBUG
01212   printf ("CMOS clock was last adjusted %s", ctime(&ca.ca_adj_time));
01213 #endif
01214   return &ca;
01215 }
01216 
01217 /* return the difference in seconds: cmos_time - system_time */
01218 static double
01219 compare_cmos_sys()
01220 {
01221   struct tm tm;
01222   time_t cmos_time;
01223   double system_sec;
01224   double dif;
01225   int i;
01226   extern char *optarg;
01227   struct timeval now;
01228 
01229   if (geteuid() != 0)
01230     {
01231       struct tm bdt;
01232       char before[256], after[256];
01233       int fd = open("/proc/rtc", O_RDONLY);
01234       if (fd == -1)
01235         {
01236           fprintf(stderr, "kernel lacks enhanced real time clock support, "
01237                   "so only root can read RTC\n");
01238           exit(1);
01239         }
01240       read(fd, before, sizeof(before));
01241       close(fd);
01242       do
01243         {
01244           fd = open("/proc/rtc", O_RDONLY);
01245           read(fd, after, sizeof(after));
01246           gettimeofday (&now, NULL);
01247           close(fd);
01248         } while (!strncmp(before, after, strlen(after)));
01249       strptime(after, "rtc_time : %H:%M:%S\nrtc_date : %Y-%m-%d", &bdt);
01250 
01251       if (universal)            /* also set tm_wday and tm_yday */
01252         cmos_time = mkgmtime(&bdt);
01253       else
01254         cmos_time = mktime(&bdt);
01255 #ifdef DEBUG
01256       printf("RTC says date & time are %.24s %s\n",
01257              asctime(&bdt), tzname[bdt.tm_isdst?1:0]);
01258 #endif
01259       system_sec = now.tv_sec + .000001 * now.tv_usec;
01260       dif = (double)cmos_time - system_sec;
01261       return dif;
01262     }
01263   else                          /* I am superuser */
01264     {
01265       cmos_init ();
01266 
01267       /* read RTC exactly on falling edge of update flag */
01268       /* Wait for rise.... (may take up to 1 second) */
01269 
01270       for (i = 0; i < 10000000; i++)
01271         if (CMOS_READ (10) & 0x80)
01272           break;
01273 
01274       /* Wait for fall.... (must try at least 2.228 ms) */
01275 
01276       for (i = 0; i < 1000000; i++)
01277         if (!(CMOS_READ (10) & 0x80))
01278           break;
01279 
01280       /* The "do" loop is "low-risk programming" */
01281       /* In theory it should never run more than once */
01282       do
01283         {
01284           tm.tm_sec = cmos_read_bcd (0);
01285           tm.tm_min = cmos_read_bcd (2);
01286           tm.tm_hour = cmos_read_bcd (4);
01287           tm.tm_wday = cmos_read_bcd (6);
01288           tm.tm_mday = cmos_read_bcd (7);
01289           tm.tm_mon = cmos_read_bcd (8);
01290           tm.tm_year = cmos_read_bcd (9);
01291         }
01292       while (tm.tm_sec != cmos_read_bcd (0));
01293 
01294       /* fetch system time immediately */
01295       gettimeofday (&now, NULL);
01296 
01297       tm.tm_mon--;              /* DOS uses 1 base */
01298       tm.tm_wday -= 3;          /* DOS uses 3 - 9 for week days */
01299       tm.tm_isdst = -1;         /* don't know whether it's daylight */
01300       if ((tm.tm_year += (epoch - 1900)) <= 69)
01301         tm.tm_year += 100;
01302 #ifdef DEBUG
01303       printf (" mday=%d  mon=%d  wday=%d  year=%d\n",
01304               tm.tm_mday, tm.tm_mon, tm.tm_wday, tm.tm_year);
01305       printf ("Cmos time  %d:%02d:%02d\n", tm.tm_hour, tm.tm_min, tm.tm_sec);
01306 #endif
01307     }
01308   /*
01309    * Mktime assumes we're giving it local time.  If the CMOS clock is in
01310    * GMT, we have to set up TZ so mktime knows it.  Tzset gets called
01311    * implicitly by the time code, but only the first time.  When
01312    * changing the environment variable, better call tzset explicitly.
01313    */
01314   if (universal)
01315     {
01316 #ifdef ZONESWITCH
01317       zone = (char *) getenv ("TZ");    /* save original time zone */
01318       (void) putenv ("TZ=");
01319       tzset ();
01320       cmos_time = mktime (&tm);
01321       /* now put back the original zone */
01322       if (zone)
01323         {
01324           if ((strlen (zone) + 4) > sizeof (zonebuf))
01325             {
01326               fprintf (stderr, "Size of TZ variable is too long\n");
01327               exit (2);
01328             }
01329           strcpy (zonebuf, "TZ=");
01330           strcat (zonebuf, zone);
01331           putenv (zonebuf);
01332         }
01333       else
01334         {                       /* wasn't one, so clear it */
01335           putenv ("TZ");
01336         }
01337       tzset ();
01338       printf ("%s", ctime (&cmos_time));
01339 #else /* !ZONESWITCH */
01340       cmos_time = mkgmtime(&tm);
01341 #endif
01342     }  
01343   else
01344     {
01345       cmos_time = mktime (&tm);
01346       /* printf ("%s", asctime (&tm)); */
01347     }
01348 
01349   system_sec = now.tv_sec + .000001 * now.tv_usec;
01350 #ifdef DEBUG
01351   printf ("Number of seconds since 1/1/1970 is %ld\n",
01352           (long) cmos_time);
01353 #endif
01354 
01355   dif = (double)cmos_time - system_sec;
01356 
01357   return dif;
01358 }
01359 
01360 
01361 static FILE *lfile;             /* pointer to log file, or NULL if it
01362                                    has not been opened yet */
01363 
01364 void sethackent(void)
01365 {
01366   endhackent();
01367   lfile = fopen(log_path, "r");
01368   if (!lfile && logging)
01369     {
01370       lfile = fopen(log_path, "a+"); /* create it if it doesn't exist */
01371       if (!lfile)
01372         {
01373           fprintf(stderr, "%s does not exist, and could not be created\n",
01374                   log_path);
01375           exit(1);
01376         }
01377       fseek(lfile, 0L, 0);      /* start at beginning */
01378     }
01379 }
01380 
01381 void endhackent(void)
01382 {
01383   if (lfile) fclose(lfile);
01384   lfile = NULL;
01385 }
01386 
01387 /* read next entry in clock comparison log, fill a struct hack from
01388    it, and return a pointer to it.  Ignore lines starting with `#'.
01389    Return NULL when there are no more lines to read.  */
01390 struct hack *gethackent(void)
01391 {
01392   char buf[256], sys_flag, cmos_flag, junk[26];
01393   static struct hack h;
01394 
01395   if (!lfile) sethackent