1#! @PATH_PERL@ -w 2# @configure_input@ 3# 4# $Id$ 5# 6# DISCLAIMER 7# 8# Copyright (C) 1999,2000 Hans Lambermont and Origin B.V. 9# 10# Permission to use, copy, modify and distribute this software and its 11# documentation for any purpose and without fee is hereby granted, 12# provided that the above copyright notice appears in all copies and 13# that both the copyright notice and this permission notice appear in 14# supporting documentation. This software is supported as is and without 15# any express or implied warranties, including, without limitation, the 16# implied warranties of merchantability and fitness for a particular 17# purpose. The name Origin B.V. must not be used to endorse or promote 18# products derived from this software without prior written permission. 19# 20# Hans Lambermont <ntpsweep@lambermont.dyndns.org> 21 22package ntpsweep; 23use 5.006_000; 24use strict; 25use lib "@PERLLIBDIR@"; 26use NTP::Util qw(do_dns ntp_read_vars ntp_peers ntp_sntp_line); 27 28(my $program = $0) =~ s%.*/(.+?)(.pl)?$%$1%; 29my ($showpeers, $maxlevel, $strip); 30my (%known_host_info, %known_host_peers); 31 32exit run(@ARGV) unless caller; 33 34sub run { 35 my $opts; 36 if (!processOptions(\@_, $opts) || 37 (((@_ != 1) && !$opts->{host} && !@{$opts->{'host-list'}}))) { 38 usage(1); 39 }; 40 41 # no STDOUT buffering 42 $| = 1; 43 ($showpeers, $maxlevel, $strip) = 44 ($opts->{peers}, $opts->{maxlevel}, $opts->{strip}); 45 46 my $hostsfile = shift; 47 48 # Main program 49 50 my @hosts; 51 52 if ($opts->{host}) { 53 push @hosts, $opts->{host}; 54 } 55 else { 56 @hosts = read_hosts($hostsfile) if $hostsfile; 57 push @hosts, @{$opts->{'host-list'}}; 58 } 59 60 # Print header 61 print <<EOF; 62Host st offset(s) version system processor 63--------------------------------+--+---------+-----------+------------+--------- 64EOF 65 66 %known_host_info = (); 67 %known_host_peers = (); 68 scan_hosts(@hosts); 69 70 return 0; 71} 72 73sub scan_hosts { 74 my (@hosts) = @_; 75 76 my $host; 77 for $host (@hosts) { 78 scan_host($host, 0, $host => 1); 79 } 80} 81 82sub read_hosts { 83 my ($hostsfile) = @_; 84 my @hosts; 85 86 open my $hosts, $hostsfile 87 or die "$program: FATAL: unable to read $hostsfile: $!\n"; 88 89 while (<$hosts>) { 90 next if /^\s*(#|$)/; # comment/empty 91 chomp; 92 push @hosts, $_; 93 } 94 95 close $hosts; 96 return @hosts; 97} 98 99sub scan_host { 100 my ($host, $level, %trace) = @_; 101 my $stratum = 0; 102 my $offset = 0; 103 my $daemonversion = ""; 104 my $system = ""; 105 my $processor = ""; 106 my @peers; 107 my $known_host = 0; 108 109 if (exists $known_host_info{$host}) { 110 $known_host = 1; 111 } 112 else { 113 ($offset, $stratum) = ntp_sntp_line($host); 114 115 # got answers ? If so, go on. 116 if ($stratum) { 117 my $vars = ntp_read_vars(0, [qw(processor system daemon_version)], $host) || {}; 118 $daemonversion = $vars->{daemon_version}; 119 $system = $vars->{system}; 120 $processor = $vars->{processor}; 121 122 # Shorten daemon_version string. 123 $daemonversion =~ s/(;|Mon|Tue|Wed|Thu|Fri|Sat|Sun).*$//; 124 $daemonversion =~ s/version=//; 125 $daemonversion =~ s/(x|)ntpd //; 126 $daemonversion =~ s/(\(|\))//g; 127 $daemonversion =~ s/beta/b/; 128 $daemonversion =~ s/multicast/mc/; 129 130 # Shorten system string 131 $system =~ s/UNIX\///; 132 $system =~ s/RELEASE/r/; 133 $system =~ s/CURRENT/c/; 134 135 # Shorten processor string 136 $processor =~ s/unknown//; 137 } 138 139 # got answers ? If so, go on. 140 if ($daemonversion) { 141 if ($showpeers) { 142 my $peers_ref = ntp_peers($host); 143 my @peers_tmp = @$peers_ref; 144 for (@peers_tmp) { 145 $_->{remote} =~ s/^(?: |x|\.|-|\+|#|\*|o)([^ ]+)/$1/; 146 push @peers, $_->{remote}; 147 } 148 } 149 } 150 151 # Add scanned host to known_hosts array 152 #push @known_hosts, $host; 153 if ($stratum) { 154 $known_host_info{$host} = sprintf "%2d %9.3f %-11s %-12s %s", 155 $stratum, $offset, (substr $daemonversion, 0, 11), 156 (substr $system, 0, 12), (substr $processor, 0, 9); 157 } 158 else { 159 # Stratum level 0 is consider invalid 160 $known_host_info{$host} = " ?"; 161 } 162 $known_host_peers{$host} = [@peers]; 163 } 164 165 if ($stratum || $known_host) { # Valid or known host 166 my $printhost = ' ' x $level . (do_dns($host) || $host); 167 # Shorten host string 168 if ($strip) { 169 $printhost =~ s/$strip//; 170 } 171 # append number of peers in brackets if requested and valid 172 if ($showpeers && ($known_host_info{$host} ne " ?")) { 173 $printhost .= " (" . @{$known_host_peers{$host}} . ")"; 174 } 175 # Finally print complete host line 176 printf "%-32s %s\n", 177 (substr $printhost, 0, 32), $known_host_info{$host}; 178 if ($showpeers && ($maxlevel ? $level < $maxlevel : 1)) { 179 $trace{$host} = 1; 180 # Loop through peers 181 foreach my $peer (@{$known_host_peers{$host}}) { 182 if (exists $trace{$peer}) { 183 # we've detected a loop ! 184 $printhost = ' ' x ($level + 1) . "= " . $peer; 185 # Shorten host string 186 $printhost =~ s/$strip// if $strip; 187 printf "%-32s\n", substr $printhost, 0, 32; 188 } else { 189 if ((substr $peer, 0, 3) ne "127") { 190 scan_host($peer, $level + 1, %trace); 191 } 192 } 193 } 194 } 195 } 196 else { # We did not get answers from this host 197 my $printhost = ' ' x $level . (do_dns($host) || $host); 198 $printhost =~ s/$strip// if $strip; 199 printf "%-32s ?\n", substr $printhost, 0, 32; 200 } 201} 202 203@ntpsweep_opts@ 204 2051; 206__END__ 207