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 const *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 */ 492