PageRenderTime 17ms CodeModel.GetById 13ms app.highlight 1ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/ntp/scripts/ntpsweep.in

https://bitbucket.org/freebsd/freebsd-head/
Autoconf | 300 lines | 227 code | 30 blank | 43 comment | 45 complexity | 120a942f50b5e246d652cd2b86b27ea1 MD5 | raw file
  1#! @PATH_PERL@ -w
  2#
  3# $Id$
  4#
  5# DISCLAIMER
  6# 
  7# Copyright (C) 1999,2000 Hans Lambermont and Origin B.V.
  8# 
  9# Permission to use, copy, modify and distribute this software and its
 10# documentation for any purpose and without fee is hereby granted,
 11# provided that the above copyright notice appears in all copies and
 12# that both the copyright notice and this permission notice appear in
 13# supporting documentation. This software is supported as is and without
 14# any express or implied warranties, including, without limitation, the
 15# implied warranties of merchantability and fitness for a particular
 16# purpose. The name Origin B.V. must not be used to endorse or promote
 17# products derived from this software without prior written permission.
 18#
 19# Hans Lambermont <ntpsweep@lambermont.dyndns.org>
 20
 21require 5.0;		# But actually tested on 5.004 ;)
 22use Getopt::Long;       # GetOptions()
 23use strict;
 24
 25my $version = 1.3;
 26(my $program = $0) =~ s%.*/(.+?)(.pl)?$%$1%;
 27
 28# Hardcoded paths/program names
 29my $ntpdate = "ntpdate";
 30my $ntpq = "ntpq";
 31
 32# no STDOUT buffering
 33$| = 1;
 34
 35my ($help, $single_host, $showpeers, $maxlevel, $strip, $askversion);
 36my $res = GetOptions("help!"      => \$help,
 37		     "host=s"     => \$single_host,
 38		     "peers!"     => \$showpeers,
 39		     "maxlevel=s" => \$maxlevel,
 40		     "strip=s"    => \$strip,
 41		     "version!"   => \$askversion);
 42
 43if ($askversion) {
 44    print("$version\n");
 45    exit 0;
 46}
 47
 48if ($help || ((@ARGV != 1) && !$single_host)) {
 49    warn <<EOF;
 50This is $program, version $version
 51Copyright (C) 1999,2000 Hans Lambermont and Origin B.V.  Disclaimer inside.
 52
 53Usage:
 54  $program [--help|--peers|--strip <string>|--maxlevel <level>|--version] \\
 55    <file>|[--host <hostname>]
 56
 57Description:
 58  $program prints per host given in <file> the NTP stratum level, the
 59  clock offset in seconds, the daemon version, the operating system and
 60  the processor. Optionally recursing through all peers.
 61
 62Options:
 63--help
 64    Print this short help text and exit.
 65--version
 66    Print version ($version) and exit.
 67<file>
 68    Specify hosts file. File format is one hostname or ip number per line.
 69    Lines beginning with # are considered as comment.
 70--host <hostname>
 71    Speficy a single host, bypassing the need for a hosts file.
 72--peers
 73    Recursively list all peers a host synchronizes to.
 74    An '= ' before a peer means a loop. Recursion stops here.
 75--maxlevel <level>
 76    Traverse peers up to this level (4 is a reasonable number).
 77--strip <string>
 78    Strip <string> from hostnames.
 79
 80Examples:
 81    $program myhosts.txt --strip .foo.com
 82    $program --host some.host --peers --maxlevel 4
 83EOF
 84    exit 1;
 85}
 86
 87my $hostsfile = shift;
 88my (@hosts, @known_hosts);
 89my (%known_host_info, %known_host_peers);
 90
 91sub read_hosts()
 92{
 93    local *HOSTS;
 94    open (HOSTS, $hostsfile) ||
 95	die "$program: FATAL: unable to read $hostsfile: $!\n";
 96    while (<HOSTS>) {
 97	next if /^\s*(#|$)/; # comment/empty
 98	chomp;
 99	push(@hosts, $_);
100    }
101    close(HOSTS);
102}
103
104# translate IP to hostname if possible
105sub ip2name {
106    my($ip) = @_;
107    my($addr, $name, $aliases, $addrtype, $length, @addrs);
108    $addr = pack('C4', split(/\./, $ip));
109    ($name, $aliases, $addrtype, $length, @addrs) = gethostbyaddr($addr, 2);
110    if ($name) {
111        # return lower case name
112	return("\L$name");
113    } else {
114	return($ip);
115    }
116}
117
118# item_in_list($item, @list): returns 1 if $item is in @list, 0 if not
119sub item_in_list {
120    my($item, @list) = @_;
121    my($i);
122    foreach $i (@list) {
123	return 1 if ($item eq $i);
124    }
125    return 0;
126}
127
128sub scan_host($;$;$) {
129    my($host, $level, @trace) = @_;
130    my $stratum = 0;
131    my $offset = 0;
132    my $daemonversion = "";
133    my $system = "";
134    my $processor = "";
135    my @peers;
136    my $known_host = 0;
137
138    if (&item_in_list($host, @known_hosts)) {
139	$known_host = 1;
140    } else {
141	# ntpdate part
142	open(NTPDATE, "$ntpdate -bd $host 2>/dev/null |") ||
143    	die "Cannot open ntpdate pipe: $!\n";
144	while (<NTPDATE>) {
145	    /^stratum\s+(\d+).*$/ && do {
146		$stratum = $1;
147	    };
148	    /^offset\s+([0-9.-]+)$/ && do {
149		$offset = $1;
150	    };
151	}
152	close(NTPDATE);
153    
154	# got answers ? If so, go on.
155	if ($stratum) {
156	    # ntpq part
157	    my $ntpqparams = "-c 'rv 0 processor,system,daemon_version'";
158	    open(NTPQ, "$ntpq $ntpqparams $host 2>/dev/null |") ||
159		die "Cannot open ntpq pipe: $!\n";
160	    while (<NTPQ>) {
161		/daemon_version="(.*)"/ && do {
162		    $daemonversion = $1;
163		};
164		/system="([^"]*)"/ && do {
165		    $system = $1;
166		};
167		/processor="([^"]*)"/ && do {
168		    $processor = $1;
169		};
170	    }
171	    close(NTPQ);
172	    
173	    # Shorten daemon_version string.
174	    $daemonversion =~ s/(;|Mon|Tue|Wed|Thu|Fri|Sat|Sun).*$//;
175	    $daemonversion =~ s/version=//;
176	    $daemonversion =~ s/(x|)ntpd //;
177	    $daemonversion =~ s/(\(|\))//g;
178	    $daemonversion =~ s/beta/b/;
179	    $daemonversion =~ s/multicast/mc/;
180	
181	    # Shorten system string
182	    $system =~ s/UNIX\///;
183	    $system =~ s/RELEASE/r/;
184	    $system =~ s/CURRENT/c/;
185
186	    # Shorten processor string
187	    $processor =~ s/unknown//;
188	}
189    
190	# got answers ? If so, go on.
191	if ($daemonversion) {
192	    # ntpq again, find out the peers this time
193	    if ($showpeers) {
194		my $ntpqparams = "-pn";
195		open(NTPQ, "$ntpq $ntpqparams $host 2>/dev/null |") ||
196		    die "Cannot open ntpq pipe: $!\n";
197		while (<NTPQ>) {
198		    /^No association ID's returned$/ && do {
199			last;
200		    };
201		    /^     remote/ && do {
202			next;
203		    };
204		    /^==/ && do {
205			next;
206		    };
207		    /^( |x|\.|-|\+|#|\*|o)([^ ]+)/ && do {
208			push(@peers, ip2name($2));
209			next;
210		    };
211		    print "ERROR: $_";
212		}
213		close(NTPQ);
214	    }
215	}
216    
217	# Add scanned host to known_hosts array
218	push(@known_hosts, $host);
219	if ($stratum) {
220	    $known_host_info{$host} = sprintf("%2d %9.3f %-11s %-12s %s",
221		$stratum, $offset, substr($daemonversion,0,11),
222		substr($system,0,12), substr($processor,0,9));
223	} else {
224	    # Stratum level 0 is consider invalid
225	    $known_host_info{$host} = sprintf(" ?");
226	}
227	$known_host_peers{$host} = [@peers];
228    }
229
230    if ($stratum || $known_host) { # Valid or known host
231	my $printhost = ' ' x $level . $host;
232	# Shorten host string
233	if ($strip) {
234	    $printhost =~ s/$strip//;
235	}
236	# append number of peers in brackets if requested and valid
237	if ($showpeers && ($known_host_info{$host} ne " ?")) {
238	    $printhost .= " (" . @{$known_host_peers{$host}} . ")";
239	}
240	# Finally print complete host line
241	printf("%-32s %s\n",
242	    substr($printhost,0,32), $known_host_info{$host});
243	if ($showpeers && (eval($maxlevel ? $level < $maxlevel : 1))) {
244	    my $peer;
245	    push(@trace, $host);
246	    # Loop through peers
247	    foreach $peer (@{$known_host_peers{$host}}) {
248		if (&item_in_list($peer, @trace)) {
249		    # we've detected a loop !
250		    $printhost = ' ' x ($level + 1) . "= " . $peer;
251		    # Shorten host string
252		    if ($strip) {
253			$printhost =~ s/$strip//;
254		    }
255		    printf("%-32s %s\n",
256			substr($printhost,0,32));
257		} else {
258		    if (substr($peer,0,3) ne "127") {
259			&scan_host($peer, $level + 1, @trace);
260		    }
261		}
262	    }
263	}
264    } else { # We did not get answers from this host
265	my $printhost = ' ' x $level . $host;
266	# Shorten host string
267	if ($strip) {
268	    $printhost =~ s/$strip//;
269	}
270	printf("%-32s  ?\n", substr($printhost,0,32));
271    }
272}
273
274sub scan_hosts()
275{
276    my $host;
277    for $host (@hosts) {
278	my @trace;
279	push(@trace, $host);
280	scan_host($host, 0, @trace);
281    }
282}
283
284# Main program
285
286if ($single_host) {
287    push(@hosts, $single_host);
288} else {
289    &read_hosts($hostsfile);
290}
291
292# Print header
293print <<EOF;
294Host                             st offset(s) version     system       processor
295--------------------------------+--+---------+-----------+------------+---------
296EOF
297
298&scan_hosts();
299
300exit 0;