1*abb0f93cSkardel#!/usr/bin/perl -w 2*abb0f93cSkardel# --*-perl-*- 3*abb0f93cSkardel;# 4*abb0f93cSkardel;# ntploopstat,v 3.1 1993/07/06 01:09:11 jbj Exp 5*abb0f93cSkardel;# 6*abb0f93cSkardel;# Poll NTP server using NTP mode 7 loopinfo request. 7*abb0f93cSkardel;# Log info and timestamp to file for processing by ntploopwatch. 8*abb0f93cSkardel;# 9*abb0f93cSkardel;# 10*abb0f93cSkardel;# Copyright (c) 1992 11*abb0f93cSkardel;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg 12*abb0f93cSkardel;# 13*abb0f93cSkardel;################################################################# 14*abb0f93cSkardel;# 15*abb0f93cSkardel;# The format written to the logfile is the same as used by xntpd 16*abb0f93cSkardel;# for the loopstats file. 17*abb0f93cSkardel;# This script however allows to gather loop filter statistics from 18*abb0f93cSkardel;# remote servers where you do not have access to the loopstats logfile. 19*abb0f93cSkardel;# 20*abb0f93cSkardel;# Please note: Communication delays affect the accuracy of the 21*abb0f93cSkardel;# timestamps recorded. Effects from these delays will probably 22*abb0f93cSkardel;# not show up, as timestamps are recorded to the second only. 23*abb0f93cSkardel;# (Should have implemented &gettimeofday()..) 24*abb0f93cSkardel;# 25*abb0f93cSkardel 26*abb0f93cSkardel$0 =~ s!^.*/([^/]+)$!$1!; # beautify script name 27*abb0f93cSkardel 28*abb0f93cSkardel$ntpserver = 'localhost'; # default host to poll 29*abb0f93cSkardel$delay = 60; # default sampling rate 30*abb0f93cSkardel ;# keep it shorter than minpoll (=64) 31*abb0f93cSkardel ;# to get all values 32*abb0f93cSkardel 33*abb0f93cSkardelrequire "ctime.pl"; 34*abb0f93cSkardel;# handle bug in early ctime distributions 35*abb0f93cSkardel$ENV{'TZ'} = 'MET' unless defined($ENV{'TZ'}) || $] > 4.010; 36*abb0f93cSkardel 37*abb0f93cSkardelif (defined(@ctime'MoY)) 38*abb0f93cSkardel{ 39*abb0f93cSkardel *MonthName = *ctime'MoY; 40*abb0f93cSkardel} 41*abb0f93cSkardelelse 42*abb0f93cSkardel{ 43*abb0f93cSkardel @MonthName = ('Jan','Feb','Mar','Apr','May','Jun', 44*abb0f93cSkardel 'Jul','Aug','Sep','Oct','Nov','Dec'); 45*abb0f93cSkardel} 46*abb0f93cSkardel 47*abb0f93cSkardel;# this routine can be redefined to point to syslog if necessary 48*abb0f93cSkardelsub msg 49*abb0f93cSkardel{ 50*abb0f93cSkardel return unless $verbose; 51*abb0f93cSkardel 52*abb0f93cSkardel print STDERR "$0: "; 53*abb0f93cSkardel printf STDERR @_; 54*abb0f93cSkardel} 55*abb0f93cSkardel 56*abb0f93cSkardel;############################################################# 57*abb0f93cSkardel;# 58*abb0f93cSkardel;# process command line 59*abb0f93cSkardel$usage = <<"E-O-S"; 60*abb0f93cSkardel 61*abb0f93cSkardelusage: 62*abb0f93cSkardel $0 [-d<delay>] [-t<timeout>] [-l <logfile>] [-v] [ntpserver] 63*abb0f93cSkardelE-O-S 64*abb0f93cSkardel 65*abb0f93cSkardelwhile($_ = shift) 66*abb0f93cSkardel{ 67*abb0f93cSkardel /^-v(\d*)$/ && ($verbose=($1 eq '') ? 1 : $1,1) && next; 68*abb0f93cSkardel /^-d(\d*)$/ && 69*abb0f93cSkardel do { 70*abb0f93cSkardel ($1 ne '') && ($delay = $1,1) && next; 71*abb0f93cSkardel @ARGV || die("$0: delay value missing after -d\n$usage"); 72*abb0f93cSkardel $delay = shift; 73*abb0f93cSkardel ($delay >= 0) || die("$0: bad delay value \"$delay\"\n$usage"); 74*abb0f93cSkardel next; 75*abb0f93cSkardel }; 76*abb0f93cSkardel /^-l$/ && 77*abb0f93cSkardel do { 78*abb0f93cSkardel @ARGV || die("$0: logfile missing after -l\n$usage"); 79*abb0f93cSkardel $logfile = shift; 80*abb0f93cSkardel next; 81*abb0f93cSkardel }; 82*abb0f93cSkardel /^-t(\d*(\.\d*)?)$/ && 83*abb0f93cSkardel do { 84*abb0f93cSkardel ($1 ne '') && ($timeout = $1,1) && next; 85*abb0f93cSkardel @ARGV || die("$0: timeout value missing after -t\n$usage\n"); 86*abb0f93cSkardel $timeout = shift; 87*abb0f93cSkardel ($timeout > 0) || 88*abb0f93cSkardel die("$0: bad timeout value \"$timeout\"\n$usage"); 89*abb0f93cSkardel next; 90*abb0f93cSkardel }; 91*abb0f93cSkardel 92*abb0f93cSkardel /^-/ && die("$0: unknown option \"$_\"\n$usage"); 93*abb0f93cSkardel 94*abb0f93cSkardel ;# any other argument is server to poll 95*abb0f93cSkardel $ntpserver = $_; 96*abb0f93cSkardel last; 97*abb0f93cSkardel} 98*abb0f93cSkardel 99*abb0f93cSkardelif (@ARGV) 100*abb0f93cSkardel{ 101*abb0f93cSkardel warn("unexpected arguments: ".join(" ",@ARGV).".\n"); 102*abb0f93cSkardel die("$0: too many servers specified\n$usage"); 103*abb0f93cSkardel} 104*abb0f93cSkardel 105*abb0f93cSkardel;# logfile defaults to include server name 106*abb0f93cSkardel;# The name of the current month is appended and 107*abb0f93cSkardel;# the file is opened and closed for each sample. 108*abb0f93cSkardel;# 109*abb0f93cSkardel$logfile = "loopstats:$ntpserver." unless defined($logfile); 110*abb0f93cSkardel$timeout = 12.0 unless defined($timeout); # wait $timeout seconds for reply 111*abb0f93cSkardel 112*abb0f93cSkardel$MAX_FAIL = 60; # give up after $MAX_FAIL failed polls 113*abb0f93cSkardel 114*abb0f93cSkardel 115*abb0f93cSkardel$MJD_1970 = 40587; 116*abb0f93cSkardel 117*abb0f93cSkardelif (eval 'require "syscall.ph";') 118*abb0f93cSkardel{ 119*abb0f93cSkardel if (defined(&SYS_gettimeofday)) 120*abb0f93cSkardel { 121*abb0f93cSkardel ;# assume standard 122*abb0f93cSkardel ;# gettimeofday(struct timeval *tp,struct timezone *tzp) 123*abb0f93cSkardel ;# syntax for gettimeofday syscall 124*abb0f93cSkardel ;# tzp = NULL -> undef 125*abb0f93cSkardel ;# tp = (long,long) 126*abb0f93cSkardel eval 'sub time { local($tz) = pack("LL",0,0); 127*abb0f93cSkardel (&msg("gettimeofday failed: $!\n"), 128*abb0f93cSkardel return (time)) 129*abb0f93cSkardel unless syscall(&SYS_gettimeofday,$tz,undef) == 0; 130*abb0f93cSkardel local($s,$us) = unpack("LL",$tz); 131*abb0f93cSkardel return $s + $us/1000000; }'; 132*abb0f93cSkardel local($t1,$t2,$t3); 133*abb0f93cSkardel $t1 = time; 134*abb0f93cSkardel eval '$t2 = &time;'; 135*abb0f93cSkardel $t3 = time; 136*abb0f93cSkardel die("$0: gettimeofday failed: $@.\n") if defined($@) && $@; 137*abb0f93cSkardel die("$0: gettimeofday inconsistency time=$t1,gettimeofday=$t2,time=$t2\n") 138*abb0f93cSkardel if (int($t1) != int($t2) && int($t3) != int($t2)); 139*abb0f93cSkardel &msg("Using gettimeofday for timestamps\n"); 140*abb0f93cSkardel } 141*abb0f93cSkardel else 142*abb0f93cSkardel { 143*abb0f93cSkardel warn("No gettimeofday syscall found - using time builtin for timestamps\n"); 144*abb0f93cSkardel eval 'sub time { return time; }'; 145*abb0f93cSkardel } 146*abb0f93cSkardel} 147*abb0f93cSkardelelse 148*abb0f93cSkardel{ 149*abb0f93cSkardel warn("No syscall.ph file found - using time builtin for timestamps\n"); 150*abb0f93cSkardel eval 'sub time { return time; }'; 151*abb0f93cSkardel} 152*abb0f93cSkardel 153*abb0f93cSkardel 154*abb0f93cSkardel;#------------------+ 155*abb0f93cSkardel;# from ntp_request.h 156*abb0f93cSkardel;#------------------+ 157*abb0f93cSkardel 158*abb0f93cSkardel;# NTP mode 7 packet format: 159*abb0f93cSkardel;# Byte 1: ResponseBit MoreBit Version(3bit) Mode(3bit)==7 160*abb0f93cSkardel;# Byte 2: AuthBit Sequence # - 0 - 127 see MoreBit 161*abb0f93cSkardel;# Byte 3: Implementation # 162*abb0f93cSkardel;# Byte 4: Request Code 163*abb0f93cSkardel;# 164*abb0f93cSkardel;# Short 1: Err(3bit) NumItems(12bit) 165*abb0f93cSkardel;# Short 2: MBZ(3bit)=0 DataItemSize(12bit) 166*abb0f93cSkardel;# 0 - 500 byte Data 167*abb0f93cSkardel;# if AuthBit is set: 168*abb0f93cSkardel;# Long: KeyId 169*abb0f93cSkardel;# 2xLong: AuthCode 170*abb0f93cSkardel 171*abb0f93cSkardel;# 172*abb0f93cSkardel$IMPL_XNTPD = 2; 173*abb0f93cSkardel$REQ_LOOP_INFO = 8; 174*abb0f93cSkardel 175*abb0f93cSkardel 176*abb0f93cSkardel;# request packet for REQ_LOOP_INFO: 177*abb0f93cSkardel;# B1: RB=0 MB=0 V=2 M=7 178*abb0f93cSkardel;# B2: S# = 0 179*abb0f93cSkardel;# B3: I# = IMPL_XNTPD 180*abb0f93cSkardel;# B4: RC = REQ_LOOP_INFO 181*abb0f93cSkardel;# S1: E=0 NI=0 182*abb0f93cSkardel;# S2: MBZ=0 DIS=0 183*abb0f93cSkardel;# data: 32 byte 0 padding 184*abb0f93cSkardel;# 8byte timestamp if encryption, 0 padding otherwise 185*abb0f93cSkardel$loopinfo_reqpkt = 186*abb0f93cSkardel pack("CCCC nn x32 x8", 0x17, 0, $IMPL_XNTPD, $REQ_LOOP_INFO, 0, 0); 187*abb0f93cSkardel 188*abb0f93cSkardel;# ignore any auth data in packets 189*abb0f93cSkardel$loopinfo_response_size = 190*abb0f93cSkardel 1+1+1+1+2+2 # header size like request pkt 191*abb0f93cSkardel + 8 # l_fp last_offset 192*abb0f93cSkardel + 8 # l_fp drift_comp 193*abb0f93cSkardel + 4 # u_long compliance 194*abb0f93cSkardel + 4 # u_long watchdog_timer 195*abb0f93cSkardel ; 196*abb0f93cSkardel$loopinfo_response_fmt = "C4n2N2N2NN"; 197*abb0f93cSkardel$loopinfo_response_fmt_v2 = "C4n2N2N2N2N"; 198*abb0f93cSkardel 199*abb0f93cSkardel;# 200*abb0f93cSkardel;# prepare connection to server 201*abb0f93cSkardel;# 202*abb0f93cSkardel 203*abb0f93cSkardel;# workaround for broken socket.ph on dynix_ptx 204*abb0f93cSkardeleval 'sub INTEL {1;}' unless defined(&INTEL); 205*abb0f93cSkardeleval 'sub ATT {1;}' unless defined(&ATT); 206*abb0f93cSkardel 207*abb0f93cSkardelrequire "sys/socket.ph"; 208*abb0f93cSkardel 209*abb0f93cSkardelrequire 'netinet/in.ph'; 210*abb0f93cSkardel 211*abb0f93cSkardel;# if you do not have netinet/in.ph enable the following lines 212*abb0f93cSkardel;#eval 'sub INADDR_ANY { 0x00000000; }' unless defined(&INADDR_ANY); 213*abb0f93cSkardel;#eval 'sub IPPRORO_UDP { 17; }' unless defined(&IPPROTO_UDP); 214*abb0f93cSkardel 215*abb0f93cSkardelif ($ntpserver =~ /^((0x?)?\w+)\.((0x?)?\w+)\.((0x?)?\w+)\.((0x?)?\w+)$/) 216*abb0f93cSkardel{ 217*abb0f93cSkardel local($a,$b,$c,$d) = ($1,$3,$5,$7); 218*abb0f93cSkardel $a = oct($a) if defined($2); 219*abb0f93cSkardel $b = oct($b) if defined($4); 220*abb0f93cSkardel $c = oct($c) if defined($6); 221*abb0f93cSkardel $d = oct($d) if defined($8); 222*abb0f93cSkardel $server_addr = pack("C4", $a,$b,$c,$d); 223*abb0f93cSkardel 224*abb0f93cSkardel $server_mainname 225*abb0f93cSkardel = (gethostbyaddr($server_addr,&AF_INET))[$[] || $ntpserver; 226*abb0f93cSkardel} 227*abb0f93cSkardelelse 228*abb0f93cSkardel{ 229*abb0f93cSkardel ($server_mainname,$server_addr) 230*abb0f93cSkardel = (gethostbyname($ntpserver))[$[,$[+4]; 231*abb0f93cSkardel 232*abb0f93cSkardel die("$0: host \"$ntpserver\" is unknown\n") 233*abb0f93cSkardel unless defined($server_addr); 234*abb0f93cSkardel} 235*abb0f93cSkardel&msg ("Address of server \"$ntpserver\" is \"%d.%d.%d.%d\"\n", 236*abb0f93cSkardel unpack("C4",$server_addr)); 237*abb0f93cSkardel 238*abb0f93cSkardel$proto_udp = (getprotobyname('udp'))[$[+2] || &IPPROTO_UDP; 239*abb0f93cSkardel 240*abb0f93cSkardel$ntp_port = 241*abb0f93cSkardel (getservbyname('ntp','udp'))[$[+2] || 242*abb0f93cSkardel (warn "Could not get port number for service \"ntp/udp\" using 123\n"), 243*abb0f93cSkardel ($ntp_port=123); 244*abb0f93cSkardel 245*abb0f93cSkardel;# 246*abb0f93cSkardel0 && &SOCK_DGRAM; # satisfy perl -w ... 247*abb0f93cSkardelsocket(S, &AF_INET, &SOCK_DGRAM, $proto_udp) || 248*abb0f93cSkardel die("Cannot open socket: $!\n"); 249*abb0f93cSkardel 250*abb0f93cSkardelbind(S, pack("S n N x8", &AF_INET, 0, &INADDR_ANY)) || 251*abb0f93cSkardel die("Cannot bind: $!\n"); 252*abb0f93cSkardel 253*abb0f93cSkardel($my_port, $my_addr) = (unpack("S n a4 x8",getsockname(S)))[$[+1,$[+2]; 254*abb0f93cSkardel 255*abb0f93cSkardel&msg("Listening at address %d.%d.%d.%d port %d\n", 256*abb0f93cSkardel unpack("C4",$my_addr), $my_port); 257*abb0f93cSkardel 258*abb0f93cSkardel$server_inaddr = pack("Sna4x8", &AF_INET, $ntp_port, $server_addr); 259*abb0f93cSkardel 260*abb0f93cSkardel;############################################################ 261*abb0f93cSkardel;# 262*abb0f93cSkardel;# the main loop: 263*abb0f93cSkardel;# send request 264*abb0f93cSkardel;# get reply 265*abb0f93cSkardel;# wait til next sample time 266*abb0f93cSkardel 267*abb0f93cSkardelundef($lasttime); 268*abb0f93cSkardel$lostpacket = 0; 269*abb0f93cSkardel 270*abb0f93cSkardelwhile(1) 271*abb0f93cSkardel{ 272*abb0f93cSkardel $stime = &time; 273*abb0f93cSkardel 274*abb0f93cSkardel &msg("Sending request $stime...\n"); 275*abb0f93cSkardel 276*abb0f93cSkardel $ret = send(S,$loopinfo_reqpkt,0,$server_inaddr); 277*abb0f93cSkardel 278*abb0f93cSkardel if (! defined($ret) || $ret < length($loopinfo_reqpkt)) 279*abb0f93cSkardel { 280*abb0f93cSkardel warn("$0: send failed ret=($ret): $!\n"); 281*abb0f93cSkardel $fail++; 282*abb0f93cSkardel next; 283*abb0f93cSkardel } 284*abb0f93cSkardel 285*abb0f93cSkardel &msg("Waiting for reply...\n"); 286*abb0f93cSkardel 287*abb0f93cSkardel $mask = ""; vec($mask,fileno(S),1) = 1; 288*abb0f93cSkardel $ret = select($mask,undef,undef,$timeout); 289*abb0f93cSkardel 290*abb0f93cSkardel if (! defined($ret)) 291*abb0f93cSkardel { 292*abb0f93cSkardel warn("$0: select failed: $!\n"); 293*abb0f93cSkardel $fail++; 294*abb0f93cSkardel next; 295*abb0f93cSkardel } 296*abb0f93cSkardel elsif ($ret == 0) 297*abb0f93cSkardel { 298*abb0f93cSkardel warn("$0: request to $ntpserver timed out ($timeout seconds)\n"); 299*abb0f93cSkardel ;# do not count this event as failure 300*abb0f93cSkardel ;# it usually this happens due to dropped udp packets on noisy and 301*abb0f93cSkardel ;# havily loaded lines, so just try again; 302*abb0f93cSkardel $lostpacket = 1; 303*abb0f93cSkardel next; 304*abb0f93cSkardel } 305*abb0f93cSkardel 306*abb0f93cSkardel &msg("Receiving reply...\n"); 307*abb0f93cSkardel 308*abb0f93cSkardel $len = 520; # max size of a mode 7 packet 309*abb0f93cSkardel $reply = ""; # just make it defined for -w 310*abb0f93cSkardel $ret = recv(S,$reply,$len,0); 311*abb0f93cSkardel 312*abb0f93cSkardel if (!defined($ret)) 313*abb0f93cSkardel { 314*abb0f93cSkardel warn("$0: recv failed: $!\n"); 315*abb0f93cSkardel $fail++; 316*abb0f93cSkardel next; 317*abb0f93cSkardel } 318*abb0f93cSkardel 319*abb0f93cSkardel $etime = &time; 320*abb0f93cSkardel &msg("Received at\t$etime\n"); 321*abb0f93cSkardel 322*abb0f93cSkardel ;#$time = ($stime + $etime) / 2; # symmetric delay assumed 323*abb0f93cSkardel $time = $etime; # the above assumption breaks for X25 324*abb0f93cSkardel ;# so taking etime makes timestamps be a 325*abb0f93cSkardel ;# little late, but keeps them increasing 326*abb0f93cSkardel ;# monotonously 327*abb0f93cSkardel 328*abb0f93cSkardel &msg(sprintf("Reply from %d.%d.%d.%d took %f seconds\n", 329*abb0f93cSkardel (unpack("SnC4",$ret))[$[+2 .. $[+5], ($etime - $stime))); 330*abb0f93cSkardel 331*abb0f93cSkardel if ($len < $loopinfo_response_size) 332*abb0f93cSkardel { 333*abb0f93cSkardel warn("$0: short packet ($len bytes) received ($loopinfo_response_size bytes expected\n"); 334*abb0f93cSkardel $fail++; 335*abb0f93cSkardel next; 336*abb0f93cSkardel } 337*abb0f93cSkardel 338*abb0f93cSkardel ($b1,$b2,$b3,$b4,$s1,$s2, 339*abb0f93cSkardel $offset_i,$offset_f,$drift_i,$drift_f,$compl,$watchdog) 340*abb0f93cSkardel = unpack($loopinfo_response_fmt,$reply); 341*abb0f93cSkardel 342*abb0f93cSkardel ;# check reply 343*abb0f93cSkardel if (($s1 >> 12) != 0) # error ! 344*abb0f93cSkardel { 345*abb0f93cSkardel die("$0: got error reply ".($s1>>12)."\n"); 346*abb0f93cSkardel } 347*abb0f93cSkardel if (($b1 != 0x97 && $b1 != 0x9f) || # Reply NotMore V=2 M=7 348*abb0f93cSkardel ($b2 != 0 && $b2 != 0x80) || # S=0 Auth no/yes 349*abb0f93cSkardel $b3 != $IMPL_XNTPD || # ! IMPL_XNTPD 350*abb0f93cSkardel $b4 != $REQ_LOOP_INFO || # Ehh.. not loopinfo reply ? 351*abb0f93cSkardel $s1 != 1 || # ???? 352*abb0f93cSkardel ($s2 != 24 && $s2 != 28) # 353*abb0f93cSkardel ) 354*abb0f93cSkardel { 355*abb0f93cSkardel warn("$0: Bad/unexpected reply from server:\n"); 356*abb0f93cSkardel warn(" \"".unpack("H*",$reply)."\"\n"); 357*abb0f93cSkardel warn(" ".sprintf("b1=%x b2=%x b3=%x b4=%x s1=%d s2=%d\n", 358*abb0f93cSkardel $b1,$b2,$b3,$b4,$s1,$s2)); 359*abb0f93cSkardel $fail++; 360*abb0f93cSkardel next; 361*abb0f93cSkardel } 362*abb0f93cSkardel elsif ($s2 == 28) 363*abb0f93cSkardel { 364*abb0f93cSkardel ;# seems to be a version 2 xntpd 365*abb0f93cSkardel ($b1,$b2,$b3,$b4,$s1,$s2, 366*abb0f93cSkardel $offset_i,$offset_f,$drift_i,$drift_f,$compl_i,$compl_f,$watchdog) 367*abb0f93cSkardel = unpack($loopinfo_response_fmt_v2,$reply); 368*abb0f93cSkardel $compl = &lfptoa($compl_i, $compl_f); 369*abb0f93cSkardel } 370*abb0f93cSkardel 371*abb0f93cSkardel $time -= $watchdog; 372*abb0f93cSkardel 373*abb0f93cSkardel $offset = &lfptoa($offset_i, $offset_f); 374*abb0f93cSkardel $drift = &lfptoa($drift_i, $drift_f); 375*abb0f93cSkardel 376*abb0f93cSkardel &log($time,$offset,$drift,$compl) && ($fail = 0);; 377*abb0f93cSkardel} 378*abb0f93cSkardelcontinue 379*abb0f93cSkardel{ 380*abb0f93cSkardel die("$0: Too many failures - terminating\n") if $fail > $MAX_FAIL; 381*abb0f93cSkardel &msg("Sleeping " . ($lostpacket ? ($delay / 2) : $delay) . " seconds...\n"); 382*abb0f93cSkardel 383*abb0f93cSkardel sleep($lostpacket ? ($delay / 2) : $delay); 384*abb0f93cSkardel $lostpacket = 0; 385*abb0f93cSkardel} 386*abb0f93cSkardel 387*abb0f93cSkardelsub log 388*abb0f93cSkardel{ 389*abb0f93cSkardel local($time,$offs,$freq,$cmpl) = @_; 390*abb0f93cSkardel local($y,$m,$d); 391*abb0f93cSkardel local($fname,$suff) = ($logfile); 392*abb0f93cSkardel 393*abb0f93cSkardel 394*abb0f93cSkardel ;# silently drop sample if distance to last sample is too low 395*abb0f93cSkardel if (defined($lasttime) && ($lasttime + 2) >= $time) 396*abb0f93cSkardel { 397*abb0f93cSkardel &msg("Dropped packet - old sample\n"); 398*abb0f93cSkardel return 1; 399*abb0f93cSkardel } 400*abb0f93cSkardel 401*abb0f93cSkardel ;# $suff determines which samples end up in the same file 402*abb0f93cSkardel ;# could have used $year (;-) or WeekOfYear, DayOfYear,.... 403*abb0f93cSkardel ;# Change it to your suit... 404*abb0f93cSkardel 405*abb0f93cSkardel ($d,$m,$y) = (localtime($time))[$[+3 .. $[+5]; 406*abb0f93cSkardel $suff = sprintf("%04d%02d%02d",$y+1900,$m+1,$d); 407*abb0f93cSkardel $fname .= $suff; 408*abb0f93cSkardel if (!open(LOG,">>$fname")) 409*abb0f93cSkardel { 410*abb0f93cSkardel warn("$0: open($fname) failed: $!\n"); 411*abb0f93cSkardel $fail++; 412*abb0f93cSkardel return 0; 413*abb0f93cSkardel } 414*abb0f93cSkardel else 415*abb0f93cSkardel { 416*abb0f93cSkardel ;# file format 417*abb0f93cSkardel ;# MJD seconds offset drift compliance 418*abb0f93cSkardel printf LOG ("%d %.3lf %.8lf %.7lf %d\n", 419*abb0f93cSkardel int($time/86400)+$MJD_1970, 420*abb0f93cSkardel $time - int($time/86400) * 86400, 421*abb0f93cSkardel $offs,$freq,$cmpl); 422*abb0f93cSkardel close(LOG); 423*abb0f93cSkardel $lasttime = $time; 424*abb0f93cSkardel } 425*abb0f93cSkardel return 1; 426*abb0f93cSkardel} 427*abb0f93cSkardel 428*abb0f93cSkardel;# see ntp_fp.h to understand this 429*abb0f93cSkardelsub lfptoa 430*abb0f93cSkardel{ 431*abb0f93cSkardel local($i,$f) = @_; 432*abb0f93cSkardel local($sign) = 1; 433*abb0f93cSkardel 434*abb0f93cSkardel 435*abb0f93cSkardel if ($i & 0x80000000) 436*abb0f93cSkardel { 437*abb0f93cSkardel if ($f == 0) 438*abb0f93cSkardel { 439*abb0f93cSkardel $i = -$i; 440*abb0f93cSkardel } 441*abb0f93cSkardel else 442*abb0f93cSkardel { 443*abb0f93cSkardel $f = -$f; 444*abb0f93cSkardel $i = ~$i; 445*abb0f93cSkardel $i += 1; # 2s complement 446*abb0f93cSkardel } 447*abb0f93cSkardel $sign = -1; 448*abb0f93cSkardel ;#print "NEG: $i $f\n"; 449*abb0f93cSkardel } 450*abb0f93cSkardel else 451*abb0f93cSkardel { 452*abb0f93cSkardel ;#print "POS: $i $f\n"; 453*abb0f93cSkardel } 454*abb0f93cSkardel ;# unlike xntpd I have perl do the dirty work. 455*abb0f93cSkardel ;# Using floats here may affect precision, but 456*abb0f93cSkardel ;# currently these bits aren't significant anyway 457*abb0f93cSkardel return $sign * ($i + $f/2**32); 458*abb0f93cSkardel} 459