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