home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #1 / NN_1993_1.iso / spool / comp / sources / misc / 4248 / synclockd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-10  |  9.4 KB  |  407 lines

  1. /* (C) Silvano Maffeis, maffeis@ifi.unizh.ch             
  2.  * University of Zurich, Switzerland 1992            
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <sys/types.h>
  8. #include <sys/time.h>
  9. #include <sys/socket.h>
  10. #include <errno.h>
  11. #include <signal.h>
  12. #include <netinet/in.h>
  13. #include <netdb.h>
  14. #include <values.h>
  15. #include <malloc.h>
  16.  
  17. #define DRIFT      0.00001    /* Drift of the hardware clock compared to "real time" */
  18. #define MIN_RT       1500       /* Shortest roundtrip measured in an ethernet lan      */
  19. #define AVG_RT       2500       /* Average roundtrip measured in an ethernet lan       */
  20. #define SUSPICIOUS 100000     /* Log time deviations > SUSPICIOUS microsec           */
  21. #define VERSION    "V1.0" 
  22.  
  23.  
  24. extern getrtime();
  25.  
  26.  
  27. /* .......................globals.................................*/
  28. char **Argv;
  29. extern char *sys_errlist[];
  30. extern int errno;
  31. short synmon = 0;
  32. char *host;
  33. char *localHost;
  34. /* ...............................................................*/
  35.  
  36. /* log an event
  37.  */
  38. void LOG(str)
  39.      char *str;
  40. {
  41.   FILE *logF;
  42.   struct tm *tm; 
  43.   struct timeval tv;
  44.   char *date;
  45.   
  46.   if (gettimeofday(&tv, 0) < 0){
  47.     fprintf(stderr, "%s: gettimeofday: %s\n", Argv[0], sys_errlist[errno]);    
  48.     LOG("panic: gettimeofday failed!");
  49.     exit(1);       
  50.   };
  51.   
  52.   tm = localtime(&tv.tv_sec);
  53.   date = (char*)malloc(25);
  54.   strcpy(date, asctime(tm));
  55.   date[24] = '\0';   /* overwrite '\n' */
  56.   
  57.   if(!synmon){
  58.     logF = fopen(LOG_FILE, "a");
  59.     if(logF == NULL){ free(date); return;}
  60.     
  61.     fprintf(logF, "%s: %s: %s\n", date, localHost, str); 
  62.     fclose(logF);
  63.     free(date);
  64.   }
  65. }
  66.  
  67. /* 
  68.  * trap SIGINT:
  69.  */
  70. #if defined(SYSV)
  71. void term_fcn(sig)
  72. #else
  73.      term_fcn(sig)
  74. #endif SYSV
  75.      int sig;
  76. {
  77.   if(signal(sig, SIG_IGN) == SIG_ERR){
  78.     fprintf(stderr, "%s: term_fcn %s\n", Argv[0], sys_errlist[errno]);
  79.     exit(1);
  80.   }
  81.   
  82.   LOG("Got a kill signal!");
  83.   
  84.   exit(1);
  85. }
  86.  
  87. /*
  88.  * handle signals:
  89.  */
  90. int handle_sigs(){
  91.   if(signal(SIGHUP, SIG_IGN) == SIG_ERR)
  92.     return(0);
  93.   
  94.   if(signal(SIGINT, SIG_IGN) == SIG_ERR)
  95.     return(0);
  96.   
  97.   if(signal(SIGQUIT, SIG_IGN) == SIG_ERR)
  98.     return(0);
  99.   
  100.   if(signal(SIGTERM, term_fcn) == SIG_ERR)
  101.     return(0);
  102.   
  103.   return(1);
  104. }
  105.  
  106. /*
  107.  * read time of day:
  108.  */
  109. void  time_get(seconds, usecs)
  110.      long *seconds;
  111.      long *usecs;
  112. {
  113.   struct timeval tv;
  114.   
  115.   if (gettimeofday(&tv, 0) < 0){
  116.     fprintf(stderr, "%s: gettimeofday: %s\n", Argv[0], sys_errlist[errno]);
  117.     LOG("panic: gettimeofday failed!");
  118.     exit(1);
  119.   };
  120.   *seconds = tv.tv_sec;
  121.   *usecs   = tv.tv_usec;
  122. }
  123.  
  124. /*
  125.  * calculate roundtrip
  126.  * starttime must be <= endtime
  127.  */ 
  128. long calc_roundtrip(start_s, start_u, end_s, end_u)
  129.      long start_s;
  130.      long start_u;
  131.      long end_s;
  132.      long end_u;
  133. {
  134.   
  135.   end_s -= start_s;
  136.   if ((end_u -= start_u) < 0) {
  137.     end_u += 1000000;
  138.     --end_s;
  139.   }
  140.   return(end_u + end_s * 1000000);
  141. };
  142.  
  143. /*
  144.  * calculate delta
  145.  * starttime can also be > endtime
  146.  */ 
  147. long calc_delta(start_s, start_u, end_s, end_u)
  148.      long start_s;
  149.      long start_u;
  150.      long end_s;
  151.      long end_u;
  152. {
  153.   if(start_s < end_s || ( start_s == end_s && start_u < end_u ))
  154.     return calc_roundtrip(start_s, start_u, end_s, end_u);
  155.   
  156.   return 0-calc_roundtrip(end_s, end_u, start_s, start_u);
  157. };
  158.  
  159. /*
  160.  * set up connection to remote hosts
  161.  */
  162. void init_connection(addrp)
  163.      struct sockaddr_in *addrp;
  164. {
  165.   struct servent *servent;
  166.   long address;
  167.   struct hostent *ph;
  168.   
  169.   
  170.   if(isdigit(host[0])){
  171.     if((address=inet_addr(host))==-1){
  172.       fprintf(stderr, "%s: invalid inet number %s\n", Argv[0], host);
  173.       exit(1);
  174.     }
  175.     addrp->sin_addr.s_addr=address;
  176.     addrp->sin_family=AF_INET;
  177.   }
  178.   else{
  179.     addrp->sin_family = AF_INET;
  180.     if((ph = gethostbyname(host)) == 0){
  181.       fprintf(stderr, "%s: unknown host %s\n", Argv[0], host);
  182.       exit(1);
  183.     }
  184.     bcopy(ph->h_addr, &addrp->sin_addr, ph->h_length);
  185.   }
  186.   
  187. #ifndef PORT
  188.   servent = getservbyname("utime", "udp");
  189.   if(servent == NULL){
  190.     fprintf(stderr, "%s: getservbyname: %s\n", Argv[0], sys_errlist[errno]);
  191.     exit(1);
  192.   };
  193.   
  194.   addrp->sin_port = htons(servent->s_port);
  195. #else
  196.   addrp->sin_port = htons(PORT);
  197. #endif PORT
  198.   
  199. };
  200.  
  201. /* read parameters from config file
  202.  */
  203. void loadParams(file, sleept)
  204.      char *file;
  205.      int *sleept;
  206. {
  207.   FILE *conf;
  208.   static unsigned char is_loaded = 0;
  209.   
  210.   conf = fopen(file, "r");
  211.   if(conf == NULL){
  212.     if(is_loaded) return;
  213.     
  214.     fprintf(stderr, "%s: cannot open %s: %s\n", Argv[0], file,
  215.         sys_errlist[errno]);
  216.     exit(1);
  217.   };
  218.   is_loaded = 1;
  219.   
  220.   if(fscanf(conf, "%s %d\n", host, sleept) != 2){
  221.     fprintf(stderr, "%s: syntax error in %s\n", Argv[0], file);
  222.     exit(1);
  223.   };
  224.   fclose(conf); 
  225. }
  226.  
  227. short rt_is_lower(rt, now_sec, now_usec)
  228.      double rt;
  229.      double now_sec;
  230.      double now_usec;
  231. {
  232.   static double llast = -1.0;
  233.   static double shortest = MAXDOUBLE;
  234.   double elapsed, tmp;
  235.   
  236.   if(llast==-1.0) llast = now_sec * 1000000.0 + now_usec;
  237.   
  238.   elapsed = (now_sec * 1000000.0 + now_usec) - llast;
  239.   
  240.   if(synmon){
  241.     printf("now = %0.f, %0.f\n", now_sec, now_usec);
  242.     printf("last = %0.f\n", llast);
  243.     if(shortest != MAXDOUBLE)
  244.       printf("shortest roundtrip so far = %.0f usec\n", shortest);
  245.     printf("last adjustement          = %.0f usec ago\n", elapsed);
  246.   };
  247.   
  248.   tmp = now_sec*1000000.0 + now_usec;
  249.   
  250.   if(rt < shortest + 2*DRIFT * elapsed){
  251.     shortest = rt;
  252.     llast = tmp;
  253.     return 1;
  254.   }
  255.   
  256.   return 0;
  257. }
  258.  
  259. /******************************************************************************/
  260. main(argc, argv)
  261.      int argc;
  262.      char **argv;
  263. {
  264.   struct sockaddr_in addr;
  265.   struct timeval time, ltime, ntime;
  266.   int ret;
  267.   long start_sec, start_usec, end_sec, end_usec, rt, delta;
  268.   int sleept;
  269.   char buf[64], str[32];
  270.   
  271.   Argv = argv;
  272.  
  273.   if(argc != 3 && argc != 1){
  274.     fprintf(stderr, "usage: %s remotehost timeinterval\n", Argv[0]);
  275.     exit(1);
  276.   };
  277.   
  278.   if( argc == 1 ){
  279.     host = (char*)malloc(64);
  280.     loadParams(CONF, &sleept);
  281.   }
  282.   else{
  283.     if(sscanf(Argv[2], "%d", &sleept) != 1){
  284.       fprintf(stderr, "%s: parameter 2 must be an integer value\n", Argv[0]); 
  285.       exit(1);
  286.     };
  287.     host = Argv[1];
  288.   }
  289.   
  290.   localHost = (char*)malloc(64);
  291.   gethostname(localHost, 64);
  292.   
  293.   if(strcmp(argv[0], "synmon") == 0) synmon = 1; 
  294.   init_connection(&addr, host);
  295.   
  296.   sprintf(str, "synclockd %s started", VERSION);
  297.   LOG(str);
  298.   
  299.   if(synmon){
  300.     printf("remote host   = %s\n", host);
  301.     printf("time interval = %d\n", sleept);
  302.   }
  303.   else
  304.     handle_sigs();
  305.   
  306.   while(1){
  307.     if(synmon)
  308.       printf("\npolling %s:\n", host);
  309.     
  310.     time_get(&start_sec, &start_usec);
  311.     ret = getrtime(&addr, &time);
  312.     time_get(&end_sec, &end_usec);
  313.     
  314.     if(ret != 0){  /* rtimel timed stderr. Retry later */
  315.       if(synmon) 
  316.         fprintf(stderr, "%s: connection to %s timed out\n", Argv[0], host);
  317.       else
  318.     LOG("connection to time server timed out");
  319.       sleep(sleept);
  320.       continue;
  321.     }
  322.     
  323.     ret = gettimeofday(<ime, (struct timezone*)0);
  324.     if(ret != 0){
  325.       fprintf(stderr, "%s: gettimeofday: %s\n", Argv[0], sys_errlist[errno]);
  326.       LOG("panic: gettimeofday failed!");
  327.       exit(1);
  328.     }
  329.     
  330.     rt = calc_roundtrip(start_sec, start_usec, end_sec, end_usec);
  331.     
  332.     /* 
  333.      * since gettimeofday()'s _real_ exactness is around 1/60 second,
  334.      * most rountripdelays in a LAN will become 0 if we rely on
  335.      * gettimeofday().
  336.      * Too small values ( < MIN_RT ) are corrected to AVG_RT. Both values
  337.      * derive from measurements using an exact timer we undertook
  338.      * in a Ethernet LAN.
  339.      */
  340.     if(rt < MIN_RT) rt = AVG_RT;
  341.     
  342.     /*
  343.      * Next we look if the actual roundtrip is smaller than the corrected,
  344.      * shortest roundtrip so far:
  345.      * rt < rt_min + 2 * (1+DRIFT) * elapsed_time.
  346.      * With rt being the actual roundtrip, rt_min the shortest roundtrip
  347.      * encountered so far and the rest a parameter taking into
  348.      * account the maximal drift of two clocks.
  349.      */
  350.     if( rt_is_lower((double)rt, (double)start_sec, (double)start_usec, synmon) ){
  351.       if(synmon){ 
  352.         printf("roundtrip is lower       : %ld usec\n", rt);
  353.         printf("remote time: %ld sec, %ld usec\n", time.tv_sec, time.tv_usec);
  354.         printf("local time : %ld sec, %ld usec\n", ltime.tv_sec, ltime.tv_usec);
  355.       }
  356.       
  357.       /* compute time-delta between remote and local:
  358.        * roundtrip/2 + remote - local .
  359.        * If delta is positive, then speed up clock, else slow down.
  360.        */
  361.       delta =  rt/2 +  
  362.           calc_delta(ltime.tv_sec, ltime.tv_usec, time.tv_sec, time.tv_usec);
  363.       
  364.       if(delta != 0){
  365.     
  366.         ntime.tv_sec  = delta/1000000;
  367.         ntime.tv_usec = delta - ntime.tv_sec * 1000000;
  368.     
  369.     if(!synmon){
  370.           ret = adjtime(&ntime, 0);
  371.           if(ret != 0){
  372.             fprintf(stderr, "%s: adjtime: %s\n", Argv[0], sys_errlist[errno]);
  373.         LOG("panic: adjtime failed");
  374.             exit(1);
  375.           }
  376.         }
  377.     
  378.     /* Look if the difference between local and remote clock
  379.      * is "suspiciously" high. If so, log it.
  380.          */
  381.     rt = calc_delta(time.tv_sec, time.tv_usec, ltime.tv_sec,
  382.             ltime.tv_usec);
  383.     
  384.     if(abs(rt) > SUSPICIOUS){
  385.       sprintf(buf, "suspicious time difference: %ld", rt);
  386.       LOG(buf);
  387.         }
  388.       }
  389.       
  390.       if(synmon){
  391.         printf("advancing local clock by %ld sec, %ld usec\n", 
  392.            ntime.tv_sec, ntime.tv_usec);
  393.       }
  394.     }  
  395.     else 
  396.       if(synmon)
  397.     printf("roundtrip is *not* lower : %d usec\n", rt);
  398.     
  399.     sleep(sleept);
  400.     
  401.     /* allows to configure synclockd at runtime:
  402.      */
  403.     if(argc == 1)  
  404.       loadParams(CONF, host, &sleept);
  405.   }
  406. }
  407.