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