/* $NetBSD: tickadj.c,v 1.5 2020/05/25 20:47:37 christos Exp $ */ /* * tickadj - read, and possibly modify, the kernel `tick' and * `tickadj' variables, as well as `dosynctodr'. Note that * this operates on the running kernel only. I'd like to be * able to read and write the binary as well, but haven't * mastered this yet. * * HMS: The #includes here are different from those in xntpd/ntp_unixclock.c * These seem "worse". */ #ifdef HAVE_CONFIG_H # include <config.h> #endif #include "ntp_types.h" #include "l_stdlib.h" #include <stdio.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif /* HAVE_UNISTD_H */ #ifdef HAVE_SYS_TIMEX_H # include <sys/timex.h> #endif #ifdef HAVE_ADJTIMEX /* Linux */ struct timex txc; #if 0 int main( int argc, char *argv[] ) { int c, i; int quiet = 0; int errflg = 0; char *progname; extern int ntp_optind; extern char *ntp_optarg; progname = argv[0]; if (argc==2 && argv[1][0] != '-') { /* old Linux format, for compatability */ if ((i = atoi(argv[1])) > 0) { txc.time_tick = i; txc.modes = ADJ_TIMETICK; } else { fprintf(stderr, "Silly value for tick: %s\n", argv[1]); errflg++; } } else { while ((c = ntp_getopt(argc, argv, "a:qt:")) != EOF) { switch (c) { case 'a': if ((i=atoi(ntp_optarg)) > 0) { txc.tickadj = i; txc.modes |= ADJ_TICKADJ; } else { fprintf(stderr, "%s: unlikely value for tickadj: %s\n", progname, ntp_optarg); errflg++; } break; case 'q': quiet = 1; break; case 't': if ((i=atoi(ntp_optarg)) > 0) { txc.time_tick = i; txc.modes |= ADJ_TIMETICK; } else { (void) fprintf(stderr, "%s: unlikely value for tick: %s\n", progname, ntp_optarg); errflg++; } break; default: fprintf(stderr, "Usage: %s [tick_value]\n-or- %s [ -q ] [ -t tick ] [ -a tickadj ]\n", progname, progname); errflg++; break; } } } if (!errflg) { if (adjtimex(&txc) < 0) perror("adjtimex"); else if (!quiet) printf("tick = %ld\ntick_adj = %d\n", txc.time_tick, txc.tickadj); } exit(errflg ? 1 : 0); } #else int main( int argc, char *argv[] ) { if (argc > 2) { fprintf(stderr, "Usage: %s [tick_value]\n", argv[0]); exit(-1); } else if (argc == 2) { #ifdef ADJ_TIMETICK if ( (txc.time_tick = atoi(argv[1])) < 1 ) #else if ( (txc.tick = atoi(argv[1])) < 1 ) #endif { fprintf(stderr, "Silly value for tick: %s\n", argv[1]); exit(-1); } #ifdef ADJ_TIMETICK txc.modes = ADJ_TIMETICK; #else #ifdef MOD_OFFSET txc.modes = ADJ_TICK; #else txc.mode = ADJ_TICK; #endif #endif } else { #ifdef ADJ_TIMETICK txc.modes = 0; #else #ifdef MOD_OFFSET txc.modes = 0; #else txc.mode = 0; #endif #endif } if (adjtimex(&txc) < 0) { perror("adjtimex"); } else { #ifdef ADJ_TIMETICK printf("tick = %ld\ntick_adj = %ld\n", txc.time_tick, txc.tickadj); #else printf("tick = %ld\n", txc.tick); #endif } exit(0); } #endif #else /* not Linux... kmem tweaking: */ #ifdef HAVE_SYS_FILE_H # include <sys/file.h> #endif #include <sys/stat.h> #ifdef HAVE_SYS_PARAM_H # include <sys/param.h> #endif #ifdef NLIST_STRUCT # include <nlist.h> #else /* not NLIST_STRUCT */ /* was defined(SYS_AUX3) || defined(SYS_AUX2) */ # include <sys/resource.h> # include <sys/file.h> # include <a.out.h> # ifdef HAVE_SYS_VAR_H # include <sys/var.h> # endif #endif #include "ntp_stdlib.h" #include "ntp_io.h" #ifdef hz /* Was: RS6000 */ # undef hz #endif /* hz */ #ifdef HAVE_KVM_OPEN # include <kvm.h> #endif #ifdef SYS_VXWORKS /* vxWorks needs mode flag -casey*/ #define open(name, flags) open(name, flags, 0777) #endif #ifndef L_SET /* Was: defined(SYS_PTX) || defined(SYS_IX86OSF1) */ # define L_SET SEEK_SET #endif #ifndef HZ # define HZ DEFAULT_HZ #endif #define KMEM "/dev/kmem" #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) char *progname; int dokmem = 1; int writetickadj = 0; int writeopttickadj = 0; int unsetdosync = 0; int writetick = 0; int quiet = 0; int setnoprintf = 0; const char *kmem = KMEM; const char *file = NULL; int fd = -1; static void getoffsets (off_t *, off_t *, off_t *, off_t *); static int openfile (const char *, int); static void writevar (int, off_t, int); static void readvar (int, off_t, int *); /* * main - parse arguments and handle options */ int main( int argc, char *argv[] ) { int c; int errflg = 0; off_t tickadj_offset; off_t tick_offset; off_t dosync_offset; off_t noprintf_offset; int tickadj, ktickadj; /* HMS: Why isn't this u_long? */ int tick, ktick; /* HMS: Why isn't this u_long? */ int dosynctodr; int noprintf; int hz; int hz_int, hz_hundredths; int recommend_tickadj; long tmp; init_lib(); progname = argv[0]; while ((c = ntp_getopt(argc, argv, "a:Adkpqst:")) != EOF) { switch (c) { case 'a': writetickadj = atoi(ntp_optarg); if (writetickadj <= 0) { (void) fprintf(stderr, "%s: unlikely value for tickadj: %s\n", progname, ntp_optarg); errflg++; } #if defined SCO5_CLOCK if (writetickadj % HZ) { writetickadj = (writetickadj / HZ) * HZ; (void) fprintf(stderr, "tickadj truncated to: %d\n", writetickadj); } #endif /* SCO5_CLOCK */ break; case 'A': writeopttickadj = 1; break; case 'd': ++debug; break; case 'k': dokmem = 1; break; case 'p': setnoprintf = 1; break; case 'q': quiet = 1; break; case 's': unsetdosync = 1; break; case 't': writetick = atoi(ntp_optarg); if (writetick <= 0) { (void) fprintf(stderr, "%s: unlikely value for tick: %s\n", progname, ntp_optarg); errflg++; } break; default: errflg++; break; } } if (errflg || ntp_optind != argc) { (void) fprintf(stderr, "usage: %s [-Adkpqs] [-a newadj] [-t newtick]\n", progname); exit(2); } getoffsets(&tick_offset, &tickadj_offset, &dosync_offset, &noprintf_offset); if (debug) { (void) printf("tick offset = %lu\n", (unsigned long)tick_offset); (void) printf("tickadj offset = %lu\n", (unsigned long)tickadj_offset); (void) printf("dosynctodr offset = %lu\n", (unsigned long)dosync_offset); (void) printf("noprintf offset = %lu\n", (unsigned long)noprintf_offset); } if (writetick && (tick_offset == 0)) { (void) fprintf(stderr, "No tick kernel variable\n"); errflg++; } if (writeopttickadj && (tickadj_offset == 0)) { (void) fprintf(stderr, "No tickadj kernel variable\n"); errflg++; } if (unsetdosync && (dosync_offset == 0)) { (void) fprintf(stderr, "No dosynctodr kernel variable\n"); errflg++; } if (setnoprintf && (noprintf_offset == 0)) { (void) fprintf(stderr, "No noprintf kernel variable\n"); errflg++; } if (tick_offset != 0) { readvar(fd, tick_offset, &tick); #if defined(TICK_NANO) && defined(K_TICK_NAME) if (!quiet) (void) printf("KERNEL %s = %d nsec\n", K_TICK_NAME, tick); #endif /* TICK_NANO && K_TICK_NAME */ #ifdef TICK_NANO tick /= 1000; #endif } else { tick = 0; } if (tickadj_offset != 0) { readvar(fd, tickadj_offset, &tickadj); #ifdef SCO5_CLOCK /* scale from nsec/sec to usec/tick */ tickadj /= (1000L * HZ); #endif /*SCO5_CLOCK */ #if defined(TICKADJ_NANO) && defined(K_TICKADJ_NAME) if (!quiet) (void) printf("KERNEL %s = %d nsec\n", K_TICKADJ_NAME, tickadj); #endif /* TICKADJ_NANO && K_TICKADJ_NAME */ #ifdef TICKADJ_NANO tickadj += 999; tickadj /= 1000; #endif } else { tickadj = 0; } if (dosync_offset != 0) { readvar(fd, dosync_offset, &dosynctodr); } if (noprintf_offset != 0) { readvar(fd, noprintf_offset, &noprintf); } (void) close(fd); if (unsetdosync && dosync_offset == 0) { (void) fprintf(stderr, "%s: can't find %s in namelist\n", progname, #ifdef K_DOSYNCTODR_NAME K_DOSYNCTODR_NAME #else /* not K_DOSYNCTODR_NAME */ "dosynctodr" #endif /* not K_DOSYNCTODR_NAME */ ); exit(1); } hz = HZ; #if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK) hz = (int) sysconf (_SC_CLK_TCK); #endif /* not HAVE_SYSCONF && _SC_CLK_TCK */ #ifdef OVERRIDE_HZ hz = DEFAULT_HZ; #endif ktick = tick; #ifdef PRESET_TICK tick = PRESET_TICK; #endif /* PRESET_TICK */ #ifdef TICKADJ_NANO tickadj /= 1000; if (tickadj == 0) tickadj = 1; #endif ktickadj = tickadj; #ifdef PRESET_TICKADJ tickadj = (PRESET_TICKADJ) ? PRESET_TICKADJ : 1; #endif /* PRESET_TICKADJ */ if (!quiet) { if (tick_offset != 0) { (void) printf("KERNEL tick = %d usec (from %s kernel variable)\n", ktick, #ifdef K_TICK_NAME K_TICK_NAME #else "<this can't happen>" #endif ); } #ifdef PRESET_TICK (void) printf("PRESET tick = %d usec\n", tick); #endif /* PRESET_TICK */ if (tickadj_offset != 0) { (void) printf("KERNEL tickadj = %d usec (from %s kernel variable)\n", ktickadj, #ifdef K_TICKADJ_NAME K_TICKADJ_NAME #else "<this can't happen>" #endif ); } #ifdef PRESET_TICKADJ (void) printf("PRESET tickadj = %d usec\n", tickadj); #endif /* PRESET_TICKADJ */ if (dosync_offset != 0) { (void) printf("dosynctodr is %s\n", dosynctodr ? "on" : "off"); } if (noprintf_offset != 0) { (void) printf("kernel level printf's: %s\n", noprintf ? "off" : "on"); } } if (tick <= 0) { (void) fprintf(stderr, "%s: the value of tick is silly!\n", progname); exit(1); } hz_int = (int)(1000000L / (long)tick); hz_hundredths = (int)((100000000L / (long)tick) - ((long)hz_int * 100L)); if (!quiet) { (void) printf("KERNEL hz = %d\n", hz); (void) printf("calculated hz = %d.%02d Hz\n", hz_int, hz_hundredths); } #if defined SCO5_CLOCK recommend_tickadj = 100; #else /* SCO5_CLOCK */ tmp = (long) tick * 500L; recommend_tickadj = (int)(tmp / 1000000L); if (tmp % 1000000L > 0) { recommend_tickadj++; } #ifdef MIN_REC_TICKADJ if (recommend_tickadj < MIN_REC_TICKADJ) { recommend_tickadj = MIN_REC_TICKADJ; } #endif /* MIN_REC_TICKADJ */ #endif /* SCO5_CLOCK */ if ((!quiet) && (tickadj_offset != 0)) { (void) printf("recommended value of tickadj = %d us\n", recommend_tickadj); } if ( writetickadj == 0 && !writeopttickadj && !unsetdosync && writetick == 0 && !setnoprintf) { exit(errflg ? 1 : 0); } if (writetickadj == 0 && writeopttickadj) { writetickadj = recommend_tickadj; } fd = openfile(file, O_WRONLY); if (setnoprintf && (noprintf_offset != 0)) { if (!quiet) { (void) fprintf(stderr, "setting noprintf: "); (void) fflush(stderr); } writevar(fd, noprintf_offset, 1); if (!quiet) { (void) fprintf(stderr, "done!\n"); } } if ((writetick > 0) && (tick_offset != 0)) { if (!quiet) { (void) fprintf(stderr, "writing tick, value %d: ", writetick); (void) fflush(stderr); } writevar(fd, tick_offset, writetick); if (!quiet) { (void) fprintf(stderr, "done!\n"); } } if ((writetickadj > 0) && (tickadj_offset != 0)) { if (!quiet) { (void) fprintf(stderr, "writing tickadj, value %d: ", writetickadj); (void) fflush(stderr); } #ifdef SCO5_CLOCK /* scale from usec/tick to nsec/sec */ writetickadj *= (1000L * HZ); #endif /* SCO5_CLOCK */ writevar(fd, tickadj_offset, writetickadj); if (!quiet) { (void) fprintf(stderr, "done!\n"); } } if (unsetdosync && (dosync_offset != 0)) { if (!quiet) { (void) fprintf(stderr, "zeroing dosynctodr: "); (void) fflush(stderr); } writevar(fd, dosync_offset, 0); if (!quiet) { (void) fprintf(stderr, "done!\n"); } } (void) close(fd); return(errflg ? 1 : 0); } /* * getoffsets - read the magic offsets from the specified file */ static void getoffsets( off_t *tick_off, off_t *tickadj_off, off_t *dosync_off, off_t *noprintf_off ) { #ifndef NOKMEM # ifndef HAVE_KVM_OPEN const char **kname; # endif #endif #ifndef NOKMEM # ifdef NLIST_NAME_UNION # define NL_B {{ # define NL_E }} # else # define NL_B { # define NL_E } # endif #endif #define K_FILLER_NAME "DavidLetterman" #ifdef NLIST_EXTRA_INDIRECTION int i; #endif #ifndef NOKMEM static struct nlist nl[] = { NL_B #ifdef K_TICKADJ_NAME #define N_TICKADJ 0 K_TICKADJ_NAME #else K_FILLER_NAME #endif NL_E, NL_B #ifdef K_TICK_NAME #define N_TICK 1 K_TICK_NAME #else K_FILLER_NAME #endif NL_E, NL_B #ifdef K_DOSYNCTODR_NAME #define N_DOSYNC 2 K_DOSYNCTODR_NAME #else K_FILLER_NAME #endif NL_E, NL_B #ifdef K_NOPRINTF_NAME #define N_NOPRINTF 3 K_NOPRINTF_NAME #else K_FILLER_NAME #endif NL_E, NL_B "" NL_E, }; #ifndef HAVE_KVM_OPEN static const char *kernels[] = { #ifdef HAVE_GETBOOTFILE NULL, /* *** SEE BELOW! *** */ #endif "/kernel/unix", "/kernel", "/vmunix", "/unix", "/mach", "/hp-ux", "/386bsd", "/netbsd", "/stand/vmunix", "/bsd", NULL }; #endif /* not HAVE_KVM_OPEN */ #ifdef HAVE_KVM_OPEN /* * Solaris > 2.5 doesn't have a kernel file. Use the kvm_* interface * to read the kernel name list. -- stolcke 3/4/96 */ kvm_t *kvm_handle = kvm_open(NULL, NULL, NULL, O_RDONLY, progname); if (kvm_handle == NULL) { (void) fprintf(stderr, "%s: kvm_open failed\n", progname); exit(1); } if (kvm_nlist(kvm_handle, nl) == -1) { (void) fprintf(stderr, "%s: kvm_nlist failed\n", progname); exit(1); } kvm_close(kvm_handle); #else /* not HAVE_KVM_OPEN */ #ifdef HAVE_GETBOOTFILE /* *** SEE HERE! *** */ if (kernels[0] == NULL) { char * cp = (char *)getbootfile(); if (cp) { kernels[0] = cp; } else { kernels[0] = "/Placeholder"; } } #endif /* HAVE_GETBOOTFILE */ for (kname = kernels; *kname != NULL; kname++) { struct stat stbuf; if (stat(*kname, &stbuf) == -1) { continue; } if (nlist(*kname, nl) >= 0) { break; } else { (void) fprintf(stderr, "%s: nlist didn't find needed symbols from <%s>: %s\n", progname, *kname, strerror(errno)); } } if (*kname == NULL) { (void) fprintf(stderr, "%s: Couldn't find the kernel\n", progname); exit(1); } #endif /* HAVE_KVM_OPEN */ if (dokmem) { file = kmem; fd = openfile(file, O_RDONLY); #ifdef NLIST_EXTRA_INDIRECTION /* * Go one more round of indirection. */ for (i = 0; i < (sizeof(nl) / sizeof(struct nlist)); i++) { if ((nl[i].n_value) && (nl[i].n_sclass == 0x6b)) { readvar(fd, nl[i].n_value, &nl[i].n_value); } } #endif /* NLIST_EXTRA_INDIRECTION */ } #endif /* not NOKMEM */ *tickadj_off = 0; *tick_off = 0; *dosync_off = 0; *noprintf_off = 0; #if defined(N_TICKADJ) *tickadj_off = nl[N_TICKADJ].n_value; #endif #if defined(N_TICK) *tick_off = nl[N_TICK].n_value; #endif #if defined(N_DOSYNC) *dosync_off = nl[N_DOSYNC].n_value; #endif #if defined(N_NOPRINTF) *noprintf_off = nl[N_NOPRINTF].n_value; #endif return; } #undef N_TICKADJ #undef N_TICK #undef N_DOSYNC #undef N_NOPRINTF /* * openfile - open the file, check for errors */ static int openfile( const char *name, int mode ) { int ifd; ifd = open(name, mode); if (ifd < 0) { (void) fprintf(stderr, "%s: open %s: ", progname, name); perror(""); exit(1); } return ifd; } /* * writevar - write a variable into the file */ static void writevar( int ofd, off_t off, int var ) { if (lseek(ofd, off, L_SET) == -1) { (void) fprintf(stderr, "%s: lseek fails: ", progname); perror(""); exit(1); } if (write(ofd, (char *)&var, sizeof(int)) != sizeof(int)) { (void) fprintf(stderr, "%s: write fails: ", progname); perror(""); exit(1); } return; } /* * readvar - read a variable from the file */ static void readvar( int ifd, off_t off, int *var ) { int i; if (lseek(ifd, off, L_SET) == -1) { (void) fprintf(stderr, "%s: lseek fails: ", progname); perror(""); exit(1); } i = read(ifd, (char *)var, sizeof(int)); if (i < 0) { (void) fprintf(stderr, "%s: read fails: ", progname); perror(""); exit(1); } if (i != sizeof(int)) { (void) fprintf(stderr, "%s: read expected %d, got %d\n", progname, (int)sizeof(int), i); exit(1); } return; } #endif /* not Linux */