home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / x / xntp3.zip / adjtime / adjtimed.c < prev    next >
C/C++ Source or Header  |  1992-08-29  |  12KB  |  483 lines

  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[] = "@(#)$Header: adjtimed.c,v 1.24 92/08/19 12:51:50 src Exp $";
  17. #endif
  18.  
  19. /*
  20.  * Adjust time daemon.
  21.  * This deamon 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.  Perhaps a real
  26.  * adjtime(2) will be supported in the future.
  27.  */
  28.  
  29. #include <sys/param.h>
  30. #include <sys/types.h>
  31. #include <sys/ipc.h>
  32. #include <sys/msg.h>
  33. #include <time.h>
  34. #include <signal.h>
  35. #include <nlist.h>
  36. #include <fcntl.h>
  37. #include <stdio.h>
  38. #include <errno.h>
  39. #include <syslog.h>
  40. #include "adjtime.h"
  41.  
  42. double atof();
  43. extern int optind;
  44. extern char *optarg;
  45.  
  46. int InitClockRate();
  47. int AdjustClockRate();
  48. #ifdef notdef
  49. long GetClockRate();
  50. #endif
  51. int SetClockRate();
  52. void ResetClockRate();
  53. void Cleanup();
  54. void Exit();
  55.  
  56. #define MILLION        1000000L
  57.  
  58. #define tvtod(tv)    ((double)(long)tv.tv_sec + \
  59.             ((double)tv.tv_usec / (double)MILLION))
  60.  
  61. int verbose = 0;
  62. int sysdebug = 0;
  63. static int mqid;
  64. static double oldrate = 0.0;
  65. static double RATE = 0.15;
  66. static double PERIOD = 6.666667;
  67.  
  68.  
  69. main(argc, argv)
  70.      int argc;
  71.      char **argv;
  72. {
  73.   struct timeval remains;
  74.   struct sigvec vec;
  75.   MsgBuf msg;
  76.   char ch;
  77.   int nofork = 0;
  78.   int fd;
  79.  
  80.   openlog("adjtimed", LOG_PID, LOG_LOCAL6);
  81.  
  82.   while ((ch = getopt(argc, argv, "hkrvdfp:")) != EOF) {
  83.     switch (ch) {
  84.     case 'k':
  85.     case 'r':
  86.       if ((mqid = msgget(KEY, 0)) != -1) {
  87.     if (msgctl(mqid, IPC_RMID, (struct msqid_ds *)0) == -1) {
  88.       syslog(LOG_ERR, "remove old message queue: %m");
  89.       perror("adjtimed: remove old message queue");
  90.       exit(1);
  91.         }
  92.       }
  93.  
  94.       if (ch == 'k')
  95.     exit(0);
  96.  
  97.       break;
  98.  
  99.     case 'v':
  100.       ++verbose, nofork = 1;
  101.       break;
  102.  
  103.     case 'd':
  104.       ++sysdebug;
  105.       break;
  106.  
  107.     case 'f':
  108.       nofork = 1;
  109.       break;
  110.  
  111.     case 'p':
  112.       if ((RATE = atof(optarg)) <= 0.0 || RATE >= 100.0) {
  113.     fputs("adjtimed: percentage must be between 0.0 and 100.0\n", stderr);
  114.     exit(1);
  115.       }
  116.  
  117.       RATE /= 100.0;
  118.       PERIOD = 1.0 / RATE;
  119.       break;
  120.  
  121.     default:
  122.       puts("usage: adjtimed -hkrvdf -p rate");
  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.       puts("-p rate\tpercent rate of change");
  130.       syslog(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.       syslog(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.   syslog(LOG_INFO, "started (rate %.2f%%)", RATE * 100.0);
  168.   if (verbose) printf("adjtimed: started (rate %.2f%%)\n", RATE * 100.0);
  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.       syslog(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.     syslog(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.     syslog(LOG_ERR, "get message queue id: %m");
  198.     perror("adjtimed: get message queue id");
  199.     Exit(1);
  200.   }
  201.  
  202.   for (;;) {
  203.     if (msgrcv(mqid, &msg.msgp, MSGSIZE, CLIENT, 0) == -1) {
  204.       if (errno == EINTR) continue;
  205.       syslog(LOG_ERR, "read message: %m");
  206.       perror("adjtimed: read message");
  207.       Cleanup();
  208.     }
  209.  
  210.     switch (msg.msgb.code) {
  211.     case DELTA1:
  212.     case DELTA2:
  213.       AdjustClockRate(&msg.msgb.tv, &remains);
  214.  
  215.       if (msg.msgb.code == DELTA2) {
  216.     msg.msgb.tv = remains;
  217.     msg.msgb.mtype = SERVER;
  218.  
  219.     while (msgsnd(mqid, &msg.msgp, MSGSIZE, 0) == -1) {
  220.       if (errno == EINTR) continue;
  221.       syslog(LOG_ERR, "send message: %m");
  222.       perror("adjtimed: send message");
  223.       Cleanup();
  224.     }
  225.       }
  226.  
  227.       if (remains.tv_sec + remains.tv_usec != 0L) {
  228.     if (verbose) {
  229.       printf("adjtimed: previous correction remaining %.6fs\n",
  230.             tvtod(remains));
  231.     }
  232.     if (sysdebug) {
  233.       syslog(LOG_INFO, "previous correction remaining %.6fs",
  234.             tvtod(remains));
  235.     }
  236.       }
  237.       break;
  238.  
  239.     default:
  240.       fprintf(stderr, "adjtimed: unknown message code %d\n", msg.msgb.code);
  241.       syslog(LOG_ERR, "unknown message code %d", msg.msgb.code);
  242.     } /* switch */
  243.   } /* loop */
  244. } /* main */
  245.  
  246. /*
  247.  * Default clock rate (old_tick).
  248.  */
  249. #define DEFAULT_RATE    (MILLION / HZ)
  250. #define UNKNOWN_RATE    0L
  251. #define SLEW_RATE    (MILLION / DEFAULT_RATE)
  252. #define MIN_DELTA    SLEW_RATE
  253. /*
  254. #define RATE        0.005
  255. #define PERIOD        (1.0 / RATE)
  256. */
  257. static long default_rate = DEFAULT_RATE;
  258. static long slew_rate = SLEW_RATE;
  259.  
  260. AdjustClockRate(delta, olddelta)
  261.      register struct timeval *delta, *olddelta;
  262. {
  263.   register long rate, dt;
  264.   struct itimerval period, remains;
  265.   static long leftover = 0;
  266. /*
  267.  * rate of change
  268.  */
  269.   dt = (delta->tv_sec * MILLION) + delta->tv_usec + leftover;
  270.  
  271.   if (dt < MIN_DELTA && dt > -MIN_DELTA) {
  272.     leftover += delta->tv_usec;
  273.  
  274.     if (olddelta) {
  275.       getitimer(ITIMER_REAL, &remains);
  276.       dt = ((remains.it_value.tv_sec * MILLION) + remains.it_value.tv_usec) *
  277.         oldrate;
  278.       olddelta->tv_sec = dt / MILLION;
  279.       olddelta->tv_usec = dt - (olddelta->tv_sec * MILLION); 
  280.     }
  281.  
  282.     if (verbose > 2) printf("adjtimed: delta is too small: %dus\n", dt);
  283.     if (sysdebug > 2) syslog(LOG_INFO, "delta is too small: %dus", dt);
  284.     return (1);
  285.   }
  286.  
  287.   leftover = dt % MIN_DELTA;
  288.   dt -= leftover;
  289.  
  290.   if (verbose)
  291.     printf("adjtimed: new correction %.6fs\n", (double)dt / (double)MILLION);
  292.   if (sysdebug)
  293.     syslog(LOG_INFO, "new correction %.6fs", (double)dt / (double)MILLION);
  294.   if (verbose > 2) printf("adjtimed: leftover %dus\n", leftover);
  295.   if (sysdebug > 2) syslog(LOG_INFO, "leftover %dus", leftover);
  296.   rate = dt * RATE;
  297.  
  298.   if (rate < slew_rate && rate > -slew_rate) {
  299.     rate = (rate < 0L ? -slew_rate : slew_rate);
  300.     dt = abs(dt * (MILLION / slew_rate));
  301.     period.it_value.tv_sec = dt / MILLION;
  302.   } else {
  303.     period.it_value.tv_sec = (long)PERIOD;
  304.   }
  305. /*
  306.  * The adjustment will always be a multiple of the minimum adjustment.
  307.  * So the period will always be a whole second value.
  308.  */
  309.   period.it_value.tv_usec = 0;
  310.  
  311.   if (verbose > 1)
  312.     printf("adjtimed: will be complete in %ds\n", period.it_value.tv_sec);
  313.   if (sysdebug > 1)
  314.     syslog(LOG_INFO, "will be complete in %ds", period.it_value.tv_sec);
  315. /*
  316.  * adjust the clock rate
  317.  */
  318.   if (SetClockRate((rate / slew_rate) + default_rate) == -1) {
  319.     syslog(LOG_ERR, "set clock rate: %m");
  320.     perror("adjtimed: set clock rate");
  321.   }
  322. /*
  323.  * start the timer
  324.  * (do this after changing the rate because the period has been rounded down)
  325.  */
  326.   period.it_interval.tv_sec = period.it_interval.tv_usec = 0L;
  327.   setitimer(ITIMER_REAL, &period, &remains);
  328. /*
  329.  * return old delta
  330.  */
  331.   if (olddelta) {
  332.     dt = ((remains.it_value.tv_sec * MILLION) + remains.it_value.tv_usec) *
  333.         oldrate;
  334.     olddelta->tv_sec = dt / MILLION;
  335.     olddelta->tv_usec = dt - (olddelta->tv_sec * MILLION); 
  336.   }
  337.  
  338.   oldrate = (double)rate / (double)MILLION;
  339. } /* AdjustClockRate */
  340.  
  341. static struct nlist nl[] = {
  342. #ifdef hp9000s800
  343. #ifdef PRE7_0
  344.   { "tick" },
  345. #else
  346.   { "old_tick" },
  347. #endif
  348. #else
  349.   { "_old_tick" },
  350. #endif
  351.   { "" }
  352. };
  353.  
  354. static int kmem;
  355.  
  356. /*
  357.  * The return value is the clock rate in old_tick units or -1 if error.
  358.  */
  359. long
  360. GetClockRate()
  361. {
  362.   long rate, mask;
  363.  
  364.   if (lseek(kmem, (long)nl[0].n_value, 0) == -1L)
  365.     return (-1L);
  366.  
  367.   mask = sigblock(sigmask(SIGALRM));
  368.  
  369.   if (read(kmem, (caddr_t)&rate, sizeof(rate)) != sizeof(rate))
  370.     rate = UNKNOWN_RATE;
  371.  
  372.   sigsetmask(mask);
  373.   return (rate);
  374. } /* GetClockRate */
  375.  
  376. /*
  377.  * The argument is the new rate in old_tick units.
  378.  */
  379. SetClockRate(rate)
  380.      long rate;
  381. {
  382.   long mask;
  383.  
  384.   if (lseek(kmem, (long)nl[0].n_value, 0) == -1L)
  385.     return (-1);
  386.  
  387.   mask = sigblock(sigmask(SIGALRM));
  388.  
  389.   if (write(kmem, (caddr_t)&rate, sizeof(rate)) != sizeof(rate)) {
  390.     sigsetmask(mask);
  391.     return (-1);
  392.   }
  393.  
  394.   sigsetmask(mask);
  395.  
  396.   if (rate != default_rate) {
  397.     if (verbose > 3) {
  398.       printf("adjtimed: clock rate (%lu) %ldus/s\n", rate,
  399.         (rate - default_rate) * slew_rate);
  400.     }
  401.     if (sysdebug > 3) {
  402.       syslog(LOG_INFO, "clock rate (%lu) %ldus/s", rate,
  403.         (rate - default_rate) * slew_rate);
  404.     }
  405.   }
  406.  
  407.   return (0);
  408. } /* SetClockRate */
  409.  
  410. InitClockRate()
  411. {
  412.   if ((kmem = open("/dev/kmem", O_RDWR)) == -1) {
  413.     syslog(LOG_ERR, "open(/dev/kmem): %m");
  414.     perror("adjtimed: open(/dev/kmem)");
  415.     return (-1);
  416.   }
  417.  
  418.   nlist("/hp-ux", nl);
  419.  
  420.   if (nl[0].n_type == 0) {
  421.     fputs("adjtimed: /hp-ux has no symbol table\n", stderr);
  422.     syslog(LOG_ERR, "/hp-ux has no symbol table");
  423.     return (-1);
  424.   }
  425. /*
  426.  * Set the default to the system's original value
  427.  */
  428.   default_rate = GetClockRate();
  429.   if (default_rate == UNKNOWN_RATE) default_rate = DEFAULT_RATE;
  430.   slew_rate = (MILLION / default_rate);
  431.  
  432.   return (0);
  433. } /* InitClockRate */
  434.  
  435. /*
  436.  * Reset the clock rate to the default value.
  437.  */
  438. void
  439. ResetClockRate()
  440. {
  441.   struct itimerval it;
  442.  
  443.   it.it_value.tv_sec = it.it_value.tv_usec = 0L;
  444.   setitimer(ITIMER_REAL, &it, (struct itimerval *)0);
  445.  
  446.   if (verbose > 2) puts("adjtimed: resetting the clock");
  447.   if (sysdebug > 2) syslog(LOG_INFO, "resetting the clock");
  448.  
  449.   if (GetClockRate() != default_rate) {
  450.     if (SetClockRate(default_rate) == -1) {
  451.       syslog(LOG_ERR, "set clock rate: %m");
  452.       perror("adjtimed: set clock rate");
  453.     }
  454.   }
  455.  
  456.   oldrate = 0.0;
  457. } /* ResetClockRate */
  458.  
  459. void
  460. Cleanup()
  461. {
  462.   ResetClockRate();
  463.  
  464.   if (msgctl(mqid, IPC_RMID, (struct msqid_ds *)0) == -1) {
  465.     if (errno != EINVAL) {
  466.       syslog(LOG_ERR, "remove message queue: %m");
  467.       perror("adjtimed: remove message queue");
  468.     }
  469.   }
  470.  
  471.   Exit(2);
  472. } /* Cleanup */
  473.  
  474. void
  475. Exit(status)
  476.      int status;
  477. {
  478.   syslog(LOG_ERR, "terminated");
  479.   closelog();
  480.   if (kmem != -1) close(kmem);
  481.   exit(status);
  482. } /* Exit */
  483.