/* $NetBSD: ntp_unixclock.c,v 1.4 1998/03/06 18:17:22 christos Exp $ */ /* * ntp_unixclock.c - routines for reading and adjusting a 4BSD-style * system clock. Emacs has hide-ifdef-mode ... */ #ifdef HAVE_CONFIG_H # include #endif #include #include #ifdef HAVE_UNISTD_H # include #endif /* HAVE_UNISTD_H */ #ifdef NLIST_STRUCT # include # ifdef NLIST_NAME_UNION # define N_NAME n_un.n_name # else /* not NLIST_NAME_UNION */ # define N_NAME n_name # endif /* not NLIST_NAME_UNION */ #endif /* NLIST_STRUCT */ #include #include #ifdef HAVE_SYS_PARAM_H # include #endif #ifdef HAVE_UTMP_H # include #endif #ifdef HAVE_SYS_SYSCTL_H #include #endif #if defined(HAVE_GETBOOTFILE) # include #endif #include "ntpd.h" #include "ntp_io.h" #include "ntp_unixtime.h" #include "ntp_stdlib.h" #if defined(HAVE_LIBKVM) # ifdef HAVE_SYS_PROC_H # include # endif # include # include # ifndef _POSIX2_LINE_MAX # define _POSIX2_LINE_MAX 2048 # endif #endif /* HAVE_LIBKVM */ #ifdef HAVE_SYS_TIMEX_H /* Linux - also HAVE___ADJTIMEX */ # include #endif #ifdef hz /* Was: RS6000 */ # undef hz #endif /* hz */ extern int debug; /* * These routines (init_systime, get_systime, step_systime, adj_systime) * implement an interface between the (more or less) system independent * bits of NTP and the peculiarities of dealing with the Unix system * clock. These routines will run with good precision fairly independently * of your kernel's value of tickadj. I couldn't tell the difference * between tickadj==40 and tickadj==5 on a microvax, though I prefer * to set tickadj == 500/hz when in doubt. At your option you * may compile this so that your system's clock is always slewed to the * correct time even for large corrections. Of course, all of this takes * a lot of code which wouldn't be needed with a reasonable tickadj and * a willingness to let the clock be stepped occasionally. Oh well. */ extern long adj_precision; /* adj precision in usec (tickadj) */ extern long tvu_maxslew; /* maximum adjust doable in one sec (usec) */ #ifdef SYS_WINNT extern long units_per_tick; /* imported from lib/systime.c module */ #endif /* SYS_WINNT */ extern u_long tsf_maxslew; /* same as above, as long format */ extern l_fp sys_clock_offset; /* correction for current system time */ #if defined(GDT_SURVEYING) extern l_fp gdt_rsadj; /* running sum of adjustments to time */ #endif /* * Import sys_clock (it is updated in get_systime) */ extern long sys_clock; /* * Export default_tick and default_tickadj (for ntp_config) */ u_long default_tick = 0; u_long default_tickadj = 0; static void clock_parms P((u_long *, u_long *)); /* * init_systime - initialize the system clock support code, return * clock precision. * * Note that this code obtains to kernel variables related to the local * clock, tickadj and tick. The code knows how the Berkeley adjtime * call works, and assumes these two variables are obtainable and are * used in the same manner. Tick is supposed to be the number of * microseconds which are added to the system clock at clock interrupt * time when the time isn't being slewed. Tickadj is supposed to be * the number of microseconds which are added or subtracted from tick when * the time is being slewed. * * If either of these two variables is missing, or is there but is used * for a purpose different than that described, you are SOL and may have * to do some custom kludging. * * This really shouldn't be in here. */ void init_systime() { u_long tickadj; u_long tick; u_long hz; /* * Obtain the values */ #if !defined(VMS) clock_parms(&tickadj, &tick); #else { extern int get_tickfreq(int *FREQ, int *SYSTICK); int freq,systick,sts; sts = get_tickfreq(&freq,&systick); if(!(sts & 1)) lib$stop(sts); tick = 1000000/freq; /* (usec) */ } tickadj = tick/10; /* VMS_{ADJTIME,TICTIME}.C will do up to 10% */ #endif /* VMS */ #ifdef DEBUG if (debug) printf("kernel vars: tickadj = %ld, tick = %ld\n", tickadj, tick); #endif /* * If tickadj or hz wasn't found, we're doomed. If hz is * unreasonably small, forget it. */ if (tickadj == 0 || tick == 0) { msyslog(LOG_ERR, "tickadj or tick unknown, exiting"); exit(3); } if (tick > 65535) { msyslog(LOG_ERR, "tick value of %lu is unreasonably large", tick); exit(3); } /* * Estimate hz from tick */ #ifndef SYS_VXWORKS hz = 1000000L / tick; #else hz =sysClkRateGet(); #endif #ifdef SYS_WINNT if ((1000000L % tick) > tick/2) hz += 1; #endif /* SYS_WINNT */ /* * Set adj_precision and the maximum slew based on this. Note * that maxslew is set slightly shorter than it needs to be as * insurance that all slews requested will complete in one second. */ #ifdef ADJTIME_IS_ACCURATE adj_precision = 1; #else # if defined(SCO3_TICKADJ) || defined(SCO5_TICKADJ) adj_precision = tickadj * hz; # else # ifndef SYS_WINNT adj_precision = tickadj; # else /* minimum adjustment is one 100 nanosecond unit at each clock tick */ adj_precision = (long)(hz * 0.1); # endif /* SYS_WINNT */ # endif /* not SCO[35]_TICKADJ */ #endif /* ADJTIME_IS_ACCURATE */ #ifdef NEED_HPUX_ADJTIME /* * when using adjtimed daemon, need to allow more time * because daemon may not run right away */ tvu_maxslew = tickadj * (hz - 3); #else # if defined(SLEWALWAYS) && !defined(ADJTIME_IS_ACCURATE) /* * give us more time if we are always slewing... just in case */ tvu_maxslew = tickadj * (hz - 3); # else # if !defined(SYS_WINNT) && !defined(SCO3_TICKADJ) && !defined(SCO5_TICKADJ) tvu_maxslew = tickadj * (hz - 1); # else tvu_maxslew = tickadj * hz; # endif /* SYS_WINTNT */ # endif /* SLEWALWAYS */ #endif /* NEED_HPUX_ADJTIME */ if (tvu_maxslew > 999990) { /* * Don't let the maximum slew exceed 1 second in 4. This * simplifies calculations a lot since we can then deal * with less-than-one-second fractions. */ tvu_maxslew = (999990/adj_precision) * adj_precision; } TVUTOTSF(tvu_maxslew, tsf_maxslew); NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */ msyslog(LOG_NOTICE, "tickadj = %d, tick = %d, tvu_maxslew = %d, est. hz = %d", tickadj, tick, tvu_maxslew, hz); #ifdef DEBUG if (debug) printf( "adj_precision = %ld, tvu_maxslew = %ld, tsf_maxslew = 0.%08lx\n", adj_precision, tvu_maxslew, tsf_maxslew); #endif /* * Set the current offset to 0 */ L_CLR(&sys_clock_offset); #if defined(GDT_SURVEYING) /* * Set the current increment */ L_CLR(&gdt_rsadj); #endif /* GDT_SURVEYING */ } #if !defined(VMS) # ifndef HZ # define HZ DEFAULT_HZ # endif # ifdef NLIST_STRUCT # ifdef NLIST_NAME_UNION # define NL_B {{ # define NL_E }} # else # define NL_B { # define NL_E } # endif # endif #define K_FILLER_NAME "DavidLetterman" /* * clock_parms - return the local clock tickadj and tick parameters * */ static void clock_parms(ptickadj, ptick) u_long *ptickadj; u_long *ptick; { u_long tick; int got_tick = 0; int got_tickadj = 0; int hz = 0; int got_hz = 0; # ifdef SYS_WINNT DWORD add, every; BOOL noslew; # else /* not SYS_WINNT */ # if defined(HAVE_SYSCTL) && defined(CTL_KERN) && defined(KERN_CLOCKRATE) int mib[2]; size_t ci_len; struct clockinfo c; int rc; # endif /* HAVE_SYSCTL && CTL_KERN && KERN_CLOCKRATE */ # ifndef NOKMEM static struct nlist nl[] = { NL_B # ifdef K_TICKADJ_NAME # define N_TICKADJ 0 K_TICKADJ_NAME # else K_FILLER_NAME # endif /* K_TICKADJ_NAME */ NL_E, NL_B # ifdef K_TICK_NAME # define N_TICK 1 K_TICK_NAME # else K_FILLER_NAME # endif /* K_TICK_NAME */ NL_E, NL_B "" NL_E, }; # ifdef HAVE_K_OPEN # else /* not HAVE_K_OPEN */ # ifdef HAVE_KVM_OPEN register kvm_t *kd; # else /* not HAVE_KVM_OPEN */ register int i; int kmem; struct stat stbuf; off_t where; # ifdef HAVE_BOOTFILE const char *kernelname; # else /* not HAVE_BOOTFILE */ static char *kernelnames[] = { "/kernel/unix", "/kernel", "/vmunix", "/unix", "/mach", "/hp-ux", "/386bsd", "/netbsd", "/stand/vmunix", "/bsd", # ifdef KERNELFILE KERNELFILE, # endif NULL }; # endif /* not HAVE_BOOTFILE */ # endif /* not HAVE_KVM_OPEN */ # endif /* not HAVE_K_OPEN */ # endif /* not NOKMEM */ # endif /* not SYS_WINNT */ # ifdef SYS_WINNT if (!GetSystemTimeAdjustment(&add, &every, &noslew)) { *ptick = 0; *ptickadj = 0; return; } units_per_tick = add; # else /* not SYS_WINNT */ # if defined(HAVE_SYSCTL) && defined(CTL_KERN) && defined(KERN_CLOCKRATE) mib[0] = CTL_KERN; mib[1] = KERN_CLOCKRATE; ci_len = sizeof(c); rc = sysctl(mib, 2, &c, &ci_len, NULL, 0); if (rc == -1) { NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */ syslog(LOG_NOTICE, "sysctl returned %d: %m", rc); } *ptick = c.tick; ++got_tick; # ifdef HAVE_TICKADJ_IN_STRUCT_CLOCKINFO *ptickadj = c.tickadj; ++got_tickadj; # endif /* HAVE_TICKADJ_IN_STRUCT_CLOCKINFO */ # ifdef HAVE_HZ_IN_STRUCT_CLOCKINFO hz = c.hz; ++got_hz; # endif /* HAVE_HZ_IN_STRUCT_CLOCKINFO */ # endif /* HAVE_SYSCTL && CTL_KERN && KERN_CLOCKRATE */ if (!got_hz) { hz = HZ; # if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK) hz = (int) sysconf (_SC_CLK_TCK); # endif /* HAVE_SYSCONF && _SC_CLK_TCK */ # ifdef OVERRIDE_HZ hz = DEFAULT_HZ; # endif ++got_hz; } { # ifndef NOKMEM # ifdef HAVE_K_OPEN /* { */ if (K_open((char *)0, O_RDONLY, "/vmunix")!=0) { msyslog(LOG_ERR, "K_open failed"); exit(3); } kusenlist(1); if (knlist(nl) != 0) { msyslog(LOG_ERR, "knlist failed"); exit(3); } # else /* } not HAVE_K_OPEN { */ # ifdef HAVE_KVM_OPEN /* { */ if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL) { msyslog(LOG_ERR, "kvm_open failed"); exit(3); } if (kvm_nlist(kd, nl) == -1) { msyslog(LOG_ERR, "kvm_nlist failed"); exit(3); } # else /* } not HAVE_KVM_OPEN { */ # ifdef HAVE_GETBOOTFILE kernelname = getbootfile(); if (!kernelname) { msyslog(LOG_ERR, "getbootfile() didn't find kernel name!"); exit(3); } else if (stat(kernelname, &stbuf) == -1) { msyslog(LOG_ERR, "stat(%s) failed: %m", kernelname); exit(3); } else if (nlist(kernelname, nl) < 0) { msyslog(LOG_ERR, "nlist(%s) failed: %m", kernelname); exit(3); } # else /* not HAVE_GETBOOTFILE */ for (i = 0; kernelnames[i] != NULL; i++) { if (stat(kernelnames[i], &stbuf) == -1) continue; if (nlist(kernelnames[i], nl) >= 0) break; } if (kernelnames[i] == NULL) { msyslog(LOG_ERR, "Clock init couldn't find kernel object file"); } # endif /* not HAVE_GETBOOTFILE */ kmem = open("/dev/kmem", O_RDONLY); if (kmem < 0) { msyslog(LOG_ERR, "Can't open /dev/kmem for reading: %m"); #ifdef DEBUG if (debug) perror("/dev/kmem"); #endif } # endif /* } not HAVE_KVM_OPEN */ # endif /* } not HAVE_K_OPEN */ # endif /* not NOKMEM */ } # endif /* not SYS_WINNT */ /* Skippy says we need to know TICK before handling TICKADJ */ if (got_tick != 1) { # if defined(HAVE_SYS_TIMEX_H) && defined(HAVE___ADJTIMEX) struct timex txc; # endif /* HAVE_SYS_TIMEX_H && HAVE___ADJTIMEX */ # if !defined(NOKMEM) && defined(N_TICK) # ifdef HAVE_K_OPEN if (K_read(ptick, sizeof(*ptick), nl[N_TICK].n_value) != sizeof(*ptick)) { msyslog(LOG_ERR, "K_read tick failed"); } else { if (*ptick) ++got_tick; } # else /* not HAVE_K_OPEN */ # ifdef HAVE_KVM_OPEN if (kvm_read(kd, nl[N_TICK].n_value, (char *)ptick, sizeof(*ptick)) != sizeof(*ptick)) { msyslog(LOG_ERR, "kvm_read tick failed"); } else { if (*ptick) ++got_tick; } # else /* not HAVE_KVM_OPEN */ if ((where = nl[N_TICK].n_value) == 0) { msyslog(LOG_ERR, "Unknown kernel var <%s>", nl[N_TICK].N_NAME); } else { if (lseek(kmem, where, SEEK_SET) == -1) { msyslog(LOG_ERR, "lseek for %s fails: %m", nl[N_TICK].N_NAME); } else { if (read(kmem, ptick, sizeof(*ptick)) != sizeof(*ptick)) { msyslog(LOG_ERR, "read for %s fails: %m", nl[N_TICK].N_NAME); } else { # ifdef NLIST_EXTRA_INDIRECTION /* * Aix requires one more round of indirection * if storage class a pointer. */ if ( nl[N_TICK].n_sclass == 0x6b) { if (lseek(kmem, *ptick, SEEK_SET) == -1) { msyslog(LOG_ERR, "lseek2 for %s fails: %m", nl[N_TICK].N_NAME); } else { if (read(kmem, ptick, sizeof(*ptick)) != sizeof(*ptick)) { msyslog(LOG_ERR, "read2 for %s fails: %m", nl[N_TICK].N_NAME); } else { if (*ptick) ++got_tick; } } } # else /* not NLIST_EXTRA_INDIRECTION */ if (*ptick) ++got_tick; # endif /* not NLIST_EXTRA_INDIRECTION */ } } } # endif /* not HAVE_KVM_OPEN */ # endif /* not HAVE_K_OPEN */ # ifdef TICK_NANO if (got_tick) { *ptick /= 1000; } # endif /* TICK_NANO */ # endif /* not NOKMEM && N_TICK */ if (!got_tick && default_tick) { *ptick = default_tick; if (*ptick) ++got_tick; } # ifdef PRESET_TICK if (!got_tick) { # if defined(HAVE_SYS_TIMEX_H) && defined(HAVE___ADJTIMEX) # ifdef MOD_OFFSET txc.modes = 0; # else txc.mode = 0; # endif __adjtimex(&txc); # endif /* HAVE_SYS_TIMEX_H && HAVE___ADJTIMEX */ *ptick = (u_long) PRESET_TICK; if (*ptick) ++got_tick; } # endif /* PRESET_TICK */ if (got_tick != 1) { msyslog(LOG_ERR, "Problem getting tick: %d != 1", got_tick); exit(3); } tick = *ptick; /* This might be used by PRESET_TICKADJ */ } /* Skippy says we need to know TICK before handling TICKADJ */ if (got_tickadj != 1) { # if !defined(NOKMEM) && defined(N_TICKADJ) if (nl[N_TICKADJ].n_value == 0) { msyslog(LOG_ERR, "Unknown kernel variable <%s>", nl[N_TICKADJ].N_NAME); } else { # ifdef HAVE_K_OPEN if (K_read(ptickadj, sizeof(*ptickadj), nl[N_TICKADJ].n_value) != sizeof(*ptickadj)) { msyslog(LOG_ERR, "K_read tickadj failed"); } else { if (*ptickadj) ++got_tickadj; } # else /* not HAVE_K_OPEN */ # ifdef HAVE_KVM_OPEN if (kvm_read(kd, nl[N_TICKADJ].n_value, (char *)ptickadj, sizeof(*ptickadj)) != sizeof(*ptickadj)) { msyslog(LOG_ERR, "kvm_read tickadj failed"); } else { if (*ptickadj) ++got_tickadj; } # else /* not HAVE_KVM_OPEN */ if ((where = nl[N_TICKADJ].n_value) == 0) { msyslog(LOG_ERR, "Unknown kernel var <%s>", nl[N_TICKADJ].N_NAME); } else { if (lseek(kmem, where, SEEK_SET) == -1) { msyslog(LOG_ERR, "lseek for %s fails: %m", nl[N_TICKADJ].N_NAME); } else { if (read(kmem, ptickadj, sizeof(*ptickadj)) != sizeof(*ptickadj)) { msyslog(LOG_ERR, "read for %s fails: %m", nl[N_TICKADJ].N_NAME); } else { # ifdef NLIST_EXTRA_INDIRECTION /* * Aix requires one more round of indirection * if storage class a pointer. */ if ( nl[N_TICKADJ].n_sclass == 0x6b) { if (lseek(kmem, *ptickadj, SEEK_SET) == -1) { msyslog(LOG_ERR, "lseek2 for %s fails: %m", nl[N_TICKADJ].N_NAME); } else { if (read(kmem, ptickadj, sizeof(*ptickadj)) != sizeof(*ptickadj)) { msyslog(LOG_ERR, "read2 for %s fails: %m", nl[N_TICKADJ].N_NAME); } else { if (*ptickadj) ++got_tickadj; } } } # else /* not NLIST_EXTRA_INDIRECTION */ if (*ptickadj) ++got_tickadj; # endif /* not NLIST_EXTRA_INDIRECTION */ } } } # endif /* not HAVE_KVM_OPEN */ # endif /* not HAVE_K_OPEN */ } # ifdef TICKADJ_NANO if (got_tickadj) { *ptickadj /= 1000; if (*ptickadj == 0) { *ptickadj = 1; } } # endif /* TICKADJ_NANO */ # ifdef SCO5_TICKADJ if (got_tickadj) { *ptickadj /= (1000 * hz); } # else /* not SCO5_TICKADJ */ # ifdef SCO3_TICKADJ if (got_tickadj) { *ptickadj *= (10000 / hz); } # endif /* SCO3_TICKADJ */ # endif/* not SCO5_TICKADJ */ # endif /* not NOKMEM && N_TICKADJ */ if (!got_tickadj && default_tickadj) { *ptickadj = default_tickadj; if (*ptickadj) ++got_tickadj; } # ifdef PRESET_TICKADJ if (!got_tickadj) { *ptickadj = (u_long) ((PRESET_TICKADJ) ? PRESET_TICKADJ : 1); if (*ptickadj) ++got_tickadj; } # endif /* PRESET_TICKADJ */ if (got_tickadj != 1) { msyslog(LOG_ERR, "Problem getting tickadj: %d != 1", got_tickadj); exit(3); } } # ifndef NOKMEM # ifdef HAVE_K_OPEN (void) K_close(); # else /* not HAVE_K_OPEN */ # ifdef HAVE_KVM_OPEN if (kvm_close(kd) < 0) { msyslog(LOG_ERR, "kvm_close failed"); exit(3); } # else /* not HAVE_KVM_OPEN */ close(kmem); # endif /* not HAVE_KVM_OPEN */ # endif /* not HAVE_K_OPEN */ # endif /* not NOKMEM */ # ifdef DEBUG if (debug) printf("tick = %ld, tickadj = %ld, hz = %d\n", *ptick, *ptickadj, hz); # endif } #endif /* not VMS */