154359Sroberto/*************************************************************************/ 254359Sroberto/* (c) Copyright Tai Jin, 1988. All Rights Reserved. */ 354359Sroberto/* Hewlett-Packard Laboratories. */ 454359Sroberto/* */ 554359Sroberto/* Permission is hereby granted for unlimited modification, use, and */ 654359Sroberto/* distribution. This software is made available with no warranty of */ 754359Sroberto/* any kind, express or implied. This copyright notice must remain */ 854359Sroberto/* intact in all versions of this software. */ 954359Sroberto/* */ 1054359Sroberto/* The author would appreciate it if any bug fixes and enhancements were */ 1154359Sroberto/* to be sent back to him for incorporation into future versions of this */ 1254359Sroberto/* software. Please send changes to tai@iag.hp.com or ken@sdd.hp.com. */ 1354359Sroberto/*************************************************************************/ 1454359Sroberto 1554359Sroberto#ifndef lint 1654359Srobertostatic char RCSid[] = "adjtimed.c,v 3.1 1993/07/06 01:04:45 jbj Exp"; 1754359Sroberto#endif 1854359Sroberto 1954359Sroberto/* 2054359Sroberto * Adjust time daemon. 2154359Sroberto * This daemon adjusts the rate of the system clock a la BSD's adjtime(). 2254359Sroberto * The adjtime() routine uses SYSV messages to communicate with this daemon. 2354359Sroberto * 2454359Sroberto * Caveat: This emulation uses an undocumented kernel variable. As such, it 2554359Sroberto * cannot be guaranteed to work in future HP-UX releases. Fortunately, 2654359Sroberto * it will no longer be needed in HPUX 10.01 and later. 2754359Sroberto */ 2854359Sroberto 2954359Sroberto#include <sys/param.h> 3054359Sroberto#include <sys/types.h> 3154359Sroberto#include <sys/ipc.h> 3254359Sroberto#include <sys/msg.h> 3354359Sroberto#include <sys/lock.h> 3454359Sroberto#include <time.h> 3554359Sroberto#include <signal.h> 3654359Sroberto#include <nlist.h> 3754359Sroberto#include <fcntl.h> 3854359Sroberto#include <stdio.h> 3954359Sroberto#include <unistd.h> 4054359Sroberto 4154359Sroberto#include "ntp_syslog.h" 4254359Sroberto#include "ntp_stdlib.h" 4354359Sroberto 4454359Sroberto#include "adjtime.h" 4554359Sroberto 4654359Srobertodouble atof (const char *); 4754359Sroberto 4854359Srobertoint InitClockRate (void); 4954359Srobertoint AdjustClockRate (register struct timeval *delta, register struct timeval *olddelta); 5054359Srobertolong GetClockRate (void); 5154359Srobertoint SetClockRate (long); 5254359Srobertovoid ResetClockRate (void); 5354359Srobertovoid Cleanup (void); 5454359Srobertovoid Exit (int); 5554359Sroberto 5654359Sroberto#define MILLION 1000000L 5754359Sroberto 5854359Sroberto/* emacs cc-mode goes nuts if we split the next line... */ 5954359Sroberto#define tvtod(tv) ((double)tv.tv_sec + ((double)tv.tv_usec / (double)MILLION)) 6054359Sroberto 61289997Sglebiuschar const *progname = NULL; 6254359Srobertoint verbose = 0; 6354359Srobertoint sysdebug = 0; 6454359Srobertostatic int mqid; 6554359Srobertostatic double oldrate = 0.0; 6654359Sroberto 6754359Srobertoint 6854359Srobertomain( 6954359Sroberto int argc, 7054359Sroberto char *argv[] 7154359Sroberto ) 7254359Sroberto{ 7354359Sroberto struct timeval remains; 7454359Sroberto struct sigvec vec; 7554359Sroberto MsgBuf msg; 7654359Sroberto char ch; 7754359Sroberto int nofork = 0; 7854359Sroberto int fd; 7954359Sroberto 8054359Sroberto progname = argv[0]; 8154359Sroberto 8254359Sroberto#ifdef LOG_LOCAL6 8354359Sroberto openlog("adjtimed", LOG_PID, LOG_LOCAL6); 8454359Sroberto#else 8554359Sroberto openlog("adjtimed", LOG_PID); 8654359Sroberto#endif 8754359Sroberto 8854359Sroberto while ((ch = ntp_getopt(argc, argv, "hkrvdfp:")) != EOF) { 8954359Sroberto switch (ch) { 9054359Sroberto case 'k': 9154359Sroberto case 'r': 9254359Sroberto if ((mqid = msgget(KEY, 0)) != -1) { 9354359Sroberto if (msgctl(mqid, IPC_RMID, (struct msqid_ds *)0) == -1) { 9454359Sroberto msyslog(LOG_ERR, "remove old message queue: %m"); 9554359Sroberto perror("adjtimed: remove old message queue"); 9654359Sroberto exit(1); 9754359Sroberto } 9854359Sroberto } 9954359Sroberto 10054359Sroberto if (ch == 'k') 10154359Sroberto exit(0); 10254359Sroberto 10354359Sroberto break; 10454359Sroberto 10554359Sroberto case 'v': 10654359Sroberto ++verbose, nofork = 1; 10754359Sroberto break; 10854359Sroberto 10954359Sroberto case 'd': 11054359Sroberto ++sysdebug; 11154359Sroberto break; 11254359Sroberto 11354359Sroberto case 'f': 11454359Sroberto nofork = 1; 11554359Sroberto break; 11654359Sroberto 11754359Sroberto case 'p': 11854359Sroberto fputs("adjtimed: -p option ignored\n", stderr); 11954359Sroberto break; 12054359Sroberto 12154359Sroberto default: 12254359Sroberto puts("usage: adjtimed -hkrvdf"); 12354359Sroberto puts("-h\thelp"); 12454359Sroberto puts("-k\tkill existing adjtimed, if any"); 12554359Sroberto puts("-r\trestart (kills existing adjtimed, if any)"); 12654359Sroberto puts("-v\tdebug output (repeat for more output)"); 12754359Sroberto puts("-d\tsyslog output (repeat for more output)"); 12854359Sroberto puts("-f\tno fork"); 12954359Sroberto msyslog(LOG_ERR, "usage error"); 13054359Sroberto exit(1); 13154359Sroberto } /* switch */ 13254359Sroberto } /* while */ 13354359Sroberto 13454359Sroberto if (!nofork) { 13554359Sroberto switch (fork()) { 13654359Sroberto case 0: 13754359Sroberto close(fileno(stdin)); 13854359Sroberto close(fileno(stdout)); 13954359Sroberto close(fileno(stderr)); 14054359Sroberto 14154359Sroberto#ifdef TIOCNOTTY 14254359Sroberto if ((fd = open("/dev/tty")) != -1) { 14354359Sroberto ioctl(fd, TIOCNOTTY, 0); 14454359Sroberto close(fd); 14554359Sroberto } 14654359Sroberto#else 14754359Sroberto setpgrp(); 14854359Sroberto#endif 14954359Sroberto break; 15054359Sroberto 15154359Sroberto case -1: 15254359Sroberto msyslog(LOG_ERR, "fork: %m"); 15354359Sroberto perror("adjtimed: fork"); 15454359Sroberto exit(1); 15554359Sroberto 15654359Sroberto default: 15754359Sroberto exit(0); 15854359Sroberto } /* switch */ 15954359Sroberto } /* if */ 16054359Sroberto 16154359Sroberto if (nofork) { 16254359Sroberto setvbuf(stdout, NULL, _IONBF, BUFSIZ); 16354359Sroberto setvbuf(stderr, NULL, _IONBF, BUFSIZ); 16454359Sroberto } 16554359Sroberto 16654359Sroberto msyslog(LOG_INFO, "started"); 16754359Sroberto if (verbose) printf("adjtimed: started\n"); 16854359Sroberto 16954359Sroberto if (InitClockRate() == -1) 17054359Sroberto Exit(2); 17154359Sroberto 17254359Sroberto (void)signal(SIGHUP, SIG_IGN); 17354359Sroberto (void)signal(SIGINT, SIG_IGN); 17454359Sroberto (void)signal(SIGQUIT, SIG_IGN); 17554359Sroberto (void)signal(SIGTERM, Cleanup); 17654359Sroberto 17754359Sroberto vec.sv_handler = ResetClockRate; 17854359Sroberto vec.sv_flags = 0; 17954359Sroberto vec.sv_mask = ~0; 18054359Sroberto sigvector(SIGALRM, &vec, (struct sigvec *)0); 18154359Sroberto 18254359Sroberto if (msgget(KEY, IPC_CREAT|IPC_EXCL) == -1) { 18354359Sroberto if (errno == EEXIST) { 18454359Sroberto msyslog(LOG_ERR, "message queue already exists, use -r to remove it"); 18554359Sroberto fputs("adjtimed: message queue already exists, use -r to remove it\n", 18654359Sroberto stderr); 18754359Sroberto Exit(1); 18854359Sroberto } 18954359Sroberto 19054359Sroberto msyslog(LOG_ERR, "create message queue: %m"); 19154359Sroberto perror("adjtimed: create message queue"); 19254359Sroberto Exit(1); 19354359Sroberto } 19454359Sroberto 19554359Sroberto if ((mqid = msgget(KEY, 0)) == -1) { 19654359Sroberto msyslog(LOG_ERR, "get message queue id: %m"); 19754359Sroberto perror("adjtimed: get message queue id"); 19854359Sroberto Exit(1); 19954359Sroberto } 20054359Sroberto 20154359Sroberto /* Lock process in memory to improve response time */ 20254359Sroberto if (plock(PROCLOCK)) { 20354359Sroberto msyslog(LOG_ERR, "plock: %m"); 20454359Sroberto perror("adjtimed: plock"); 20554359Sroberto Cleanup(); 20654359Sroberto } 20754359Sroberto 20854359Sroberto /* Also raise process priority. 20954359Sroberto * If we do not get run when we want, this leads to bad timekeeping 21054359Sroberto * and "Previous time adjustment didn't complete" gripes from xntpd. 21154359Sroberto */ 21254359Sroberto if (nice(-10) == -1) { 21354359Sroberto msyslog(LOG_ERR, "nice: %m"); 21454359Sroberto perror("adjtimed: nice"); 21554359Sroberto Cleanup(); 21654359Sroberto } 21754359Sroberto 21854359Sroberto for (;;) { 21954359Sroberto if (msgrcv(mqid, &msg.msgp, MSGSIZE, CLIENT, 0) == -1) { 22054359Sroberto if (errno == EINTR) continue; 22154359Sroberto msyslog(LOG_ERR, "read message: %m"); 22254359Sroberto perror("adjtimed: read message"); 22354359Sroberto Cleanup(); 22454359Sroberto } 22554359Sroberto 22654359Sroberto switch (msg.msgb.code) { 22754359Sroberto case DELTA1: 22854359Sroberto case DELTA2: 22954359Sroberto AdjustClockRate(&msg.msgb.tv, &remains); 23054359Sroberto 23154359Sroberto if (msg.msgb.code == DELTA2) { 23254359Sroberto msg.msgb.tv = remains; 23354359Sroberto msg.msgb.mtype = SERVER; 23454359Sroberto 23554359Sroberto while (msgsnd(mqid, &msg.msgp, MSGSIZE, 0) == -1) { 23654359Sroberto if (errno == EINTR) continue; 23754359Sroberto msyslog(LOG_ERR, "send message: %m"); 23854359Sroberto perror("adjtimed: send message"); 23954359Sroberto Cleanup(); 24054359Sroberto } 24154359Sroberto } 24254359Sroberto 24354359Sroberto if (remains.tv_sec + remains.tv_usec != 0L) { 24454359Sroberto if (verbose) { 24554359Sroberto printf("adjtimed: previous correction remaining %.6fs\n", 24654359Sroberto tvtod(remains)); 24754359Sroberto } 24854359Sroberto if (sysdebug) { 24954359Sroberto msyslog(LOG_INFO, "previous correction remaining %.6fs", 25054359Sroberto tvtod(remains)); 25154359Sroberto } 25254359Sroberto } 25354359Sroberto break; 25454359Sroberto 25554359Sroberto default: 25654359Sroberto fprintf(stderr, "adjtimed: unknown message code %d\n", msg.msgb.code); 25754359Sroberto msyslog(LOG_ERR, "unknown message code %d", msg.msgb.code); 25854359Sroberto } /* switch */ 25954359Sroberto } /* loop */ 26054359Sroberto} /* main */ 26154359Sroberto 26254359Sroberto/* 26354359Sroberto * Default clock rate (old_tick). 26454359Sroberto */ 26554359Sroberto#define DEFAULT_RATE (MILLION / HZ) 26654359Sroberto#define UNKNOWN_RATE 0L 26754359Sroberto#define TICK_ADJ 5 /* standard adjustment rate, microsec/tick */ 26854359Sroberto 26954359Srobertostatic long default_rate = DEFAULT_RATE; 27054359Srobertostatic long tick_rate = HZ; /* ticks per sec */ 27154359Srobertostatic long slew_rate = TICK_ADJ * HZ; /* in microsec/sec */ 27254359Sroberto 27354359Srobertoint 27454359SrobertoAdjustClockRate( 27554359Sroberto register struct timeval *delta, 27654359Sroberto register struct timeval *olddelta 27754359Sroberto ) 27854359Sroberto{ 27954359Sroberto register long rate, dt, leftover; 28054359Sroberto struct itimerval period, remains; 28154359Sroberto 28254359Sroberto dt = (delta->tv_sec * MILLION) + delta->tv_usec; 28354359Sroberto 28454359Sroberto if (verbose) 28554359Sroberto printf("adjtimed: new correction %.6fs\n", (double)dt / (double)MILLION); 28654359Sroberto if (sysdebug) 28754359Sroberto msyslog(LOG_INFO, "new correction %.6fs", (double)dt / (double)MILLION); 28854359Sroberto if (verbose > 2) printf("adjtimed: leftover %ldus\n", leftover); 28954359Sroberto if (sysdebug > 2) msyslog(LOG_INFO, "leftover %ldus", leftover); 29054359Sroberto rate = dt; 29154359Sroberto 29254359Sroberto /* 29354359Sroberto * Apply a slew rate of slew_rate over a period of dt/slew_rate seconds. 29454359Sroberto */ 29554359Sroberto if (dt > 0) { 29654359Sroberto rate = slew_rate; 29754359Sroberto } else { 29854359Sroberto rate = -slew_rate; 29954359Sroberto dt = -dt; 30054359Sroberto } 30154359Sroberto period.it_value.tv_sec = dt / slew_rate; 30254359Sroberto period.it_value.tv_usec = (dt % slew_rate) * (MILLION / slew_rate); 30354359Sroberto /* 30454359Sroberto * Note: we assume the kernel will convert the specified period into ticks 30554359Sroberto * using the modified clock rate rather than an assumed nominal clock rate, 30654359Sroberto * and therefore will generate the timer interrupt after the specified 30754359Sroberto * number of true seconds, not skewed seconds. 30854359Sroberto */ 30954359Sroberto 31054359Sroberto if (verbose > 1) 31154359Sroberto printf("adjtimed: will be complete in %lds %ldus\n", 31254359Sroberto period.it_value.tv_sec, period.it_value.tv_usec); 31354359Sroberto if (sysdebug > 1) 31454359Sroberto msyslog(LOG_INFO, "will be complete in %lds %ldus", 31554359Sroberto period.it_value.tv_sec, period.it_value.tv_usec); 31654359Sroberto /* 31754359Sroberto * adjust the clock rate 31854359Sroberto */ 31954359Sroberto if (dt) { 32054359Sroberto if (SetClockRate((rate / tick_rate) + default_rate) == -1) { 32154359Sroberto msyslog(LOG_ERR, "set clock rate: %m"); 32254359Sroberto perror("adjtimed: set clock rate"); 32354359Sroberto } 32454359Sroberto } 32554359Sroberto /* 32654359Sroberto * start the timer 32754359Sroberto * (do this after changing the rate because the period has been rounded down) 32854359Sroberto */ 32954359Sroberto period.it_interval.tv_sec = period.it_interval.tv_usec = 0L; 33054359Sroberto setitimer(ITIMER_REAL, &period, &remains); 33154359Sroberto /* 33254359Sroberto * return old delta 33354359Sroberto */ 33454359Sroberto if (olddelta) { 33554359Sroberto dt = ((remains.it_value.tv_sec * MILLION) + remains.it_value.tv_usec) * 33654359Sroberto oldrate; 33754359Sroberto olddelta->tv_sec = dt / MILLION; 33854359Sroberto olddelta->tv_usec = dt - (olddelta->tv_sec * MILLION); 33954359Sroberto } 34054359Sroberto 34154359Sroberto oldrate = (double)rate / (double)MILLION; 34254359Sroberto return(0); 34354359Sroberto} /* AdjustClockRate */ 34454359Sroberto 34554359Srobertostatic struct nlist nl[] = { 34654359Sroberto#ifdef __hp9000s800 34754359Sroberto#ifdef PRE7_0 34854359Sroberto { "tick" }, 34954359Sroberto#else 35054359Sroberto { "old_tick" }, 35154359Sroberto#endif 35254359Sroberto#else 35354359Sroberto { "_old_tick" }, 35454359Sroberto#endif 35554359Sroberto { "" } 35654359Sroberto}; 35754359Sroberto 35854359Srobertostatic int kmem; 35954359Sroberto 36054359Sroberto/* 36154359Sroberto * The return value is the clock rate in old_tick units or -1 if error. 36254359Sroberto */ 36354359Srobertolong 36454359SrobertoGetClockRate(void) 36554359Sroberto{ 36654359Sroberto long rate, mask; 36754359Sroberto 36854359Sroberto if (lseek(kmem, (off_t)nl[0].n_value, 0) == -1L) 36954359Sroberto return (-1L); 37054359Sroberto 37154359Sroberto mask = sigblock(sigmask(SIGALRM)); 37254359Sroberto 37354359Sroberto if (read(kmem, (caddr_t)&rate, sizeof(rate)) != sizeof(rate)) 37454359Sroberto rate = UNKNOWN_RATE; 37554359Sroberto 37654359Sroberto sigsetmask(mask); 37754359Sroberto return (rate); 37854359Sroberto} /* GetClockRate */ 37954359Sroberto 38054359Sroberto/* 38154359Sroberto * The argument is the new rate in old_tick units. 38254359Sroberto */ 38354359Srobertoint 38454359SrobertoSetClockRate( 38554359Sroberto long rate 38654359Sroberto ) 38754359Sroberto{ 38854359Sroberto long mask; 38954359Sroberto 39054359Sroberto if (lseek(kmem, (off_t)nl[0].n_value, 0) == -1L) 39154359Sroberto return (-1); 39254359Sroberto 39354359Sroberto mask = sigblock(sigmask(SIGALRM)); 39454359Sroberto 39554359Sroberto if (write(kmem, (caddr_t)&rate, sizeof(rate)) != sizeof(rate)) { 39654359Sroberto sigsetmask(mask); 39754359Sroberto return (-1); 39854359Sroberto } 39954359Sroberto 40054359Sroberto sigsetmask(mask); 40154359Sroberto 40254359Sroberto if (rate != default_rate) { 40354359Sroberto if (verbose > 3) { 40454359Sroberto printf("adjtimed: clock rate (%lu) %ldus/s\n", rate, 40554359Sroberto (rate - default_rate) * tick_rate); 40654359Sroberto } 40754359Sroberto if (sysdebug > 3) { 40854359Sroberto msyslog(LOG_INFO, "clock rate (%lu) %ldus/s", rate, 40954359Sroberto (rate - default_rate) * tick_rate); 41054359Sroberto } 41154359Sroberto } 41254359Sroberto 41354359Sroberto return (0); 41454359Sroberto} /* SetClockRate */ 41554359Sroberto 41654359Srobertoint 41754359SrobertoInitClockRate(void) 41854359Sroberto{ 41954359Sroberto if ((kmem = open("/dev/kmem", O_RDWR)) == -1) { 42054359Sroberto msyslog(LOG_ERR, "open(/dev/kmem): %m"); 42154359Sroberto perror("adjtimed: open(/dev/kmem)"); 42254359Sroberto return (-1); 42354359Sroberto } 42454359Sroberto 42554359Sroberto nlist("/hp-ux", nl); 42654359Sroberto 42754359Sroberto if (nl[0].n_type == 0) { 42854359Sroberto fputs("adjtimed: /hp-ux has no symbol table\n", stderr); 42954359Sroberto msyslog(LOG_ERR, "/hp-ux has no symbol table"); 43054359Sroberto return (-1); 43154359Sroberto } 43254359Sroberto /* 43354359Sroberto * Set the default to the system's original value 43454359Sroberto */ 43554359Sroberto default_rate = GetClockRate(); 43654359Sroberto if (default_rate == UNKNOWN_RATE) default_rate = DEFAULT_RATE; 43754359Sroberto tick_rate = (MILLION / default_rate); 43854359Sroberto slew_rate = TICK_ADJ * tick_rate; 43954359Sroberto fprintf(stderr,"default_rate=%ld, tick_rate=%ld, slew_rate=%ld\n",default_rate,tick_rate,slew_rate); 44054359Sroberto 44154359Sroberto return (0); 44254359Sroberto} /* InitClockRate */ 44354359Sroberto 44454359Sroberto/* 44554359Sroberto * Reset the clock rate to the default value. 44654359Sroberto */ 44754359Srobertovoid 44854359SrobertoResetClockRate(void) 44954359Sroberto{ 45054359Sroberto struct itimerval it; 45154359Sroberto 45254359Sroberto it.it_value.tv_sec = it.it_value.tv_usec = 0L; 45354359Sroberto setitimer(ITIMER_REAL, &it, (struct itimerval *)0); 45454359Sroberto 45554359Sroberto if (verbose > 2) puts("adjtimed: resetting the clock"); 45654359Sroberto if (sysdebug > 2) msyslog(LOG_INFO, "resetting the clock"); 45754359Sroberto 45854359Sroberto if (GetClockRate() != default_rate) { 45954359Sroberto if (SetClockRate(default_rate) == -1) { 46054359Sroberto msyslog(LOG_ERR, "set clock rate: %m"); 46154359Sroberto perror("adjtimed: set clock rate"); 46254359Sroberto } 46354359Sroberto } 46454359Sroberto 46554359Sroberto oldrate = 0.0; 46654359Sroberto} /* ResetClockRate */ 46754359Sroberto 46854359Srobertovoid 46954359SrobertoCleanup(void) 47054359Sroberto{ 47154359Sroberto ResetClockRate(); 47254359Sroberto 47354359Sroberto if (msgctl(mqid, IPC_RMID, (struct msqid_ds *)0) == -1) { 47454359Sroberto if (errno != EINVAL) { 47554359Sroberto msyslog(LOG_ERR, "remove message queue: %m"); 47654359Sroberto perror("adjtimed: remove message queue"); 47754359Sroberto } 47854359Sroberto } 47954359Sroberto 48054359Sroberto Exit(2); 48154359Sroberto} /* Cleanup */ 48254359Sroberto 48354359Srobertovoid 48454359SrobertoExit(status) 48554359Sroberto int status; 48654359Sroberto{ 48754359Sroberto msyslog(LOG_ERR, "terminated"); 48854359Sroberto closelog(); 48954359Sroberto if (kmem != -1) close(kmem); 49054359Sroberto exit(status); 49154359Sroberto} /* Exit */ 492