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