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