source: trunk/third/xntp/adjtimed/adjtimed.c @ 10832

Revision 10832, 11.5 KB checked in by brlewis, 27 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r10831, which included commits to RCS files with non-trunk default branches.
Line 
1/*************************************************************************/
2/* (c) Copyright Tai Jin, 1988.  All Rights Reserved.                    */
3/*     Hewlett-Packard Laboratories.                                     */
4/*                                                                       */
5/* Permission is hereby granted for unlimited modification, use, and     */
6/* distribution.  This software is made available with no warranty of    */
7/* any kind, express or implied.  This copyright notice must remain      */
8/* intact in all versions of this software.                              */
9/*                                                                       */
10/* The author would appreciate it if any bug fixes and enhancements were */
11/* to be sent back to him for incorporation into future versions of this */
12/* software.  Please send changes to tai@iag.hp.com or ken@sdd.hp.com.   */
13/*************************************************************************/
14
15#ifndef lint
16static char RCSid[] = "adjtimed.c,v 3.1 1993/07/06 01:04:45 jbj Exp";
17#endif
18
19/*
20 * Adjust time daemon.
21 * This daemon adjusts the rate of the system clock a la BSD's adjtime().
22 * The adjtime() routine uses SYSV messages to communicate with this daemon.
23 *
24 * Caveat: This emulation uses an undocumented kernel variable.  As such, it
25 * cannot be guaranteed to work in future HP-UX releases.  Fortunately,
26 * it will no longer be needed in HPUX 10.01 and later.
27 */
28
29#include <sys/param.h>
30#include <sys/types.h>
31#include <sys/ipc.h>
32#include <sys/msg.h>
33#include <sys/lock.h>
34#include <time.h>
35#include <signal.h>
36#include <nlist.h>
37#include <fcntl.h>
38#include <stdio.h>
39#include <errno.h>
40#include <unistd.h>
41#include "ntp_syslog.h"
42#include "adjtime.h"
43
44double atof();
45extern int ntp_optind;
46extern char *ntp_optarg;
47
48int InitClockRate();
49int AdjustClockRate();
50#ifdef notdef
51long GetClockRate();
52#endif
53int SetClockRate();
54void ResetClockRate();
55void Cleanup();
56void Exit();
57
58#define MILLION         1000000L
59
60#define tvtod(tv)       ((double)(long)tv.tv_sec + \
61                        ((double)tv.tv_usec / (double)MILLION))
62
63char *progname = NULL;
64int verbose = 0;
65int sysdebug = 0;
66static int mqid;
67static double oldrate = 0.0;
68
69int
70main(argc, argv)
71     int argc;
72     char **argv;
73{
74  struct timeval remains;
75  struct sigvec vec;
76  MsgBuf msg;
77  char ch;
78  int nofork = 0;
79  int fd;
80
81  progname = argv[0];
82
83#ifdef LOG_LOCAL6
84  openlog("adjtimed", LOG_PID, LOG_LOCAL6);
85#else
86  openlog("adjtimed", LOG_PID);
87#endif
88
89  while ((ch = ntp_getopt(argc, argv, "hkrvdfp:")) != EOF) {
90    switch (ch) {
91    case 'k':
92    case 'r':
93      if ((mqid = msgget(KEY, 0)) != -1) {
94        if (msgctl(mqid, IPC_RMID, (struct msqid_ds *)0) == -1) {
95          msyslog(LOG_ERR, "remove old message queue: %m");
96          perror("adjtimed: remove old message queue");
97          exit(1);
98        }
99      }
100
101      if (ch == 'k')
102        exit(0);
103
104      break;
105
106    case 'v':
107      ++verbose, nofork = 1;
108      break;
109
110    case 'd':
111      ++sysdebug;
112      break;
113
114    case 'f':
115      nofork = 1;
116      break;
117
118    case 'p':
119      fputs("adjtimed: -p option ignored\n", stderr);
120      break;
121
122    default:
123      puts("usage: adjtimed -hkrvdf");
124      puts("-h\thelp");
125      puts("-k\tkill existing adjtimed, if any");
126      puts("-r\trestart (kills existing adjtimed, if any)");
127      puts("-v\tdebug output (repeat for more output)");
128      puts("-d\tsyslog output (repeat for more output)");
129      puts("-f\tno fork");
130      msyslog(LOG_ERR, "usage error");
131      exit(1);
132    } /* switch */
133  } /* while */
134
135  if (!nofork) {
136    switch (fork()) {
137    case 0:
138      close(fileno(stdin));
139      close(fileno(stdout));
140      close(fileno(stderr));
141
142#ifdef TIOCNOTTY
143      if ((fd = open("/dev/tty")) != -1) {
144        ioctl(fd, TIOCNOTTY, 0);
145        close(fd);
146      }
147#else
148      setpgrp();
149#endif
150      break;
151
152    case -1:
153      msyslog(LOG_ERR, "fork: %m");
154      perror("adjtimed: fork");
155      exit(1);
156
157    default:
158      exit(0);
159    } /* switch */
160  } /* if */
161
162  if (nofork) {
163    setvbuf(stdout, NULL, _IONBF, BUFSIZ);
164    setvbuf(stderr, NULL, _IONBF, BUFSIZ);
165  }
166
167  msyslog(LOG_INFO, "started");
168  if (verbose) printf("adjtimed: started\n");
169
170  if (InitClockRate() == -1)
171    Exit(2);
172
173  (void)signal(SIGHUP, SIG_IGN);
174  (void)signal(SIGINT, SIG_IGN);
175  (void)signal(SIGQUIT, SIG_IGN);
176  (void)signal(SIGTERM, Cleanup);
177
178  vec.sv_handler = ResetClockRate;
179  vec.sv_flags = 0;
180  vec.sv_mask = ~0;
181  sigvector(SIGALRM, &vec, (struct sigvec *)0);
182
183  if (msgget(KEY, IPC_CREAT|IPC_EXCL) == -1) {
184    if (errno == EEXIST) {
185      msyslog(LOG_ERR, "message queue already exists, use -r to remove it");
186      fputs("adjtimed: message queue already exists, use -r to remove it\n",
187                stderr);
188      Exit(1);
189    }
190
191    msyslog(LOG_ERR, "create message queue: %m");
192    perror("adjtimed: create message queue");
193    Exit(1);
194  }
195
196  if ((mqid = msgget(KEY, 0)) == -1) {
197    msyslog(LOG_ERR, "get message queue id: %m");
198    perror("adjtimed: get message queue id");
199    Exit(1);
200  }
201 
202  /* Lock process in memory to improve response time */
203  if (plock(PROCLOCK)) {
204      msyslog(LOG_ERR, "plock: %m");
205      perror("adjtimed: plock");
206      Cleanup();
207  }
208
209  /* Also raise process priority.
210   * If we do not get run when we want, this leads to bad timekeeping
211   * and "Previous time adjustment didn't complete" gripes from xntpd.
212   */
213  if (nice(-10) == -1) {
214      msyslog(LOG_ERR, "nice: %m");
215      perror("adjtimed: nice");
216      Cleanup();
217  }
218
219  for (;;) {
220    if (msgrcv(mqid, &msg.msgp, MSGSIZE, CLIENT, 0) == -1) {
221      if (errno == EINTR) continue;
222      msyslog(LOG_ERR, "read message: %m");
223      perror("adjtimed: read message");
224      Cleanup();
225    }
226
227    switch (msg.msgb.code) {
228    case DELTA1:
229    case DELTA2:
230      AdjustClockRate(&msg.msgb.tv, &remains);
231
232      if (msg.msgb.code == DELTA2) {
233        msg.msgb.tv = remains;
234        msg.msgb.mtype = SERVER;
235
236        while (msgsnd(mqid, &msg.msgp, MSGSIZE, 0) == -1) {
237          if (errno == EINTR) continue;
238          msyslog(LOG_ERR, "send message: %m");
239          perror("adjtimed: send message");
240          Cleanup();
241        }
242      }
243
244      if (remains.tv_sec + remains.tv_usec != 0L) {
245        if (verbose) {
246          printf("adjtimed: previous correction remaining %.6fs\n",
247                        tvtod(remains));
248        }
249        if (sysdebug) {
250          msyslog(LOG_INFO, "previous correction remaining %.6fs",
251                        tvtod(remains));
252        }
253      }
254      break;
255
256    default:
257      fprintf(stderr, "adjtimed: unknown message code %d\n", msg.msgb.code);
258      msyslog(LOG_ERR, "unknown message code %d", msg.msgb.code);
259    } /* switch */
260  } /* loop */
261} /* main */
262
263/*
264 * Default clock rate (old_tick).
265 */
266#define DEFAULT_RATE    (MILLION / HZ)
267#define UNKNOWN_RATE    0L
268#define TICK_ADJ        5       /* standard adjustment rate, microsec/tick */
269
270static long default_rate = DEFAULT_RATE;
271static long tick_rate = HZ;     /* ticks per sec */
272static long slew_rate = TICK_ADJ * HZ; /* in microsec/sec */
273
274int
275AdjustClockRate(delta, olddelta)
276     register struct timeval *delta, *olddelta;
277{
278  register long rate, dt, leftover;
279  struct itimerval period, remains;
280 
281  dt = (delta->tv_sec * MILLION) + delta->tv_usec;
282
283  if (verbose)
284    printf("adjtimed: new correction %.6fs\n", (double)dt / (double)MILLION);
285  if (sysdebug)
286    msyslog(LOG_INFO, "new correction %.6fs", (double)dt / (double)MILLION);
287  if (verbose > 2) printf("adjtimed: leftover %ldus\n", leftover);
288  if (sysdebug > 2) msyslog(LOG_INFO, "leftover %ldus", leftover);
289  rate = dt;
290
291/*
292 * Apply a slew rate of slew_rate over a period of dt/slew_rate seconds.
293 */
294  if (dt > 0) {
295    rate = slew_rate;
296  } else {
297    rate = -slew_rate;
298    dt = -dt;
299  }
300  period.it_value.tv_sec = dt / slew_rate;
301  period.it_value.tv_usec = (dt % slew_rate) * (MILLION / slew_rate);
302/*
303 * Note: we assume the kernel will convert the specified period into ticks
304 * using the modified clock rate rather than an assumed nominal clock rate,
305 * and therefore will generate the timer interrupt after the specified
306 * number of true seconds, not skewed seconds.
307 */
308
309  if (verbose > 1)
310    printf("adjtimed: will be complete in %lds %ldus\n",
311           period.it_value.tv_sec, period.it_value.tv_usec);
312  if (sysdebug > 1)
313    msyslog(LOG_INFO, "will be complete in %lds %ldus",
314           period.it_value.tv_sec, period.it_value.tv_usec);
315/*
316 * adjust the clock rate
317 */
318  if (dt) {
319    if (SetClockRate((rate / tick_rate) + default_rate) == -1) {
320      msyslog(LOG_ERR, "set clock rate: %m");
321      perror("adjtimed: set clock rate");
322    }
323  }
324/*
325 * start the timer
326 * (do this after changing the rate because the period has been rounded down)
327 */
328  period.it_interval.tv_sec = period.it_interval.tv_usec = 0L;
329  setitimer(ITIMER_REAL, &period, &remains);
330/*
331 * return old delta
332 */
333  if (olddelta) {
334    dt = ((remains.it_value.tv_sec * MILLION) + remains.it_value.tv_usec) *
335                oldrate;
336    olddelta->tv_sec = dt / MILLION;
337    olddelta->tv_usec = dt - (olddelta->tv_sec * MILLION);
338  }
339
340  oldrate = (double)rate / (double)MILLION;
341  return(0);
342} /* AdjustClockRate */
343
344static struct nlist nl[] = {
345#ifdef hp9000s800
346#ifdef PRE7_0
347  { "tick" },
348#else
349  { "old_tick" },
350#endif
351#else
352  { "_old_tick" },
353#endif
354  { "" }
355};
356
357static int kmem;
358
359/*
360 * The return value is the clock rate in old_tick units or -1 if error.
361 */
362long
363GetClockRate()
364{
365  long rate, mask;
366
367  if (lseek(kmem, (long)nl[0].n_value, 0) == -1L)
368    return (-1L);
369
370  mask = sigblock(sigmask(SIGALRM));
371
372  if (read(kmem, (caddr_t)&rate, sizeof(rate)) != sizeof(rate))
373    rate = UNKNOWN_RATE;
374
375  sigsetmask(mask);
376  return (rate);
377} /* GetClockRate */
378
379/*
380 * The argument is the new rate in old_tick units.
381 */
382int
383SetClockRate(rate)
384     long rate;
385{
386  long mask;
387
388  if (lseek(kmem, (long)nl[0].n_value, 0) == -1L)
389    return (-1);
390
391  mask = sigblock(sigmask(SIGALRM));
392
393  if (write(kmem, (caddr_t)&rate, sizeof(rate)) != sizeof(rate)) {
394    sigsetmask(mask);
395    return (-1);
396  }
397
398  sigsetmask(mask);
399
400  if (rate != default_rate) {
401    if (verbose > 3) {
402      printf("adjtimed: clock rate (%lu) %ldus/s\n", rate,
403                (rate - default_rate) * tick_rate);
404    }
405    if (sysdebug > 3) {
406      msyslog(LOG_INFO, "clock rate (%lu) %ldus/s", rate,
407                (rate - default_rate) * tick_rate);
408    }
409  }
410
411  return (0);
412} /* SetClockRate */
413
414int
415InitClockRate()
416{
417  if ((kmem = open("/dev/kmem", O_RDWR)) == -1) {
418    msyslog(LOG_ERR, "open(/dev/kmem): %m");
419    perror("adjtimed: open(/dev/kmem)");
420    return (-1);
421  }
422
423  nlist("/hp-ux", nl);
424
425  if (nl[0].n_type == 0) {
426    fputs("adjtimed: /hp-ux has no symbol table\n", stderr);
427    msyslog(LOG_ERR, "/hp-ux has no symbol table");
428    return (-1);
429  }
430/*
431 * Set the default to the system's original value
432 */
433  default_rate = GetClockRate();
434  if (default_rate == UNKNOWN_RATE) default_rate = DEFAULT_RATE;
435  tick_rate = (MILLION / default_rate);
436  slew_rate = TICK_ADJ * tick_rate;
437
438  return (0);
439} /* InitClockRate */
440
441/*
442 * Reset the clock rate to the default value.
443 */
444void
445ResetClockRate()
446{
447  struct itimerval it;
448
449  it.it_value.tv_sec = it.it_value.tv_usec = 0L;
450  setitimer(ITIMER_REAL, &it, (struct itimerval *)0);
451
452  if (verbose > 2) puts("adjtimed: resetting the clock");
453  if (sysdebug > 2) msyslog(LOG_INFO, "resetting the clock");
454
455  if (GetClockRate() != default_rate) {
456    if (SetClockRate(default_rate) == -1) {
457      msyslog(LOG_ERR, "set clock rate: %m");
458      perror("adjtimed: set clock rate");
459    }
460  }
461
462  oldrate = 0.0;
463} /* ResetClockRate */
464
465void
466Cleanup()
467{
468  ResetClockRate();
469
470  if (msgctl(mqid, IPC_RMID, (struct msqid_ds *)0) == -1) {
471    if (errno != EINVAL) {
472      msyslog(LOG_ERR, "remove message queue: %m");
473      perror("adjtimed: remove message queue");
474    }
475  }
476
477  Exit(2);
478} /* Cleanup */
479
480void
481Exit(status)
482     int status;
483{
484  msyslog(LOG_ERR, "terminated");
485  closelog();
486  if (kmem != -1) close(kmem);
487  exit(status);
488} /* Exit */
Note: See TracBrowser for help on using the repository browser.