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