]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ntp/scripts/ntpsweep.in
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / ntp / scripts / ntpsweep.in
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
21 require 5.0;            # But actually tested on 5.004 ;)
22 use Getopt::Long;       # GetOptions()
23 use strict;
24
25 my $version = 1.3;
26 (my $program = $0) =~ s%.*/(.+?)(.pl)?$%$1%;
27
28 # Hardcoded paths/program names
29 my $ntpdate = "ntpdate";
30 my $ntpq = "ntpq";
31
32 # no STDOUT buffering
33 $| = 1;
34
35 my ($help, $single_host, $showpeers, $maxlevel, $strip, $askversion);
36 my $res = GetOptions("help!"      => \$help,
37                      "host=s"     => \$single_host,
38                      "peers!"     => \$showpeers,
39                      "maxlevel=s" => \$maxlevel,
40                      "strip=s"    => \$strip,
41                      "version!"   => \$askversion);
42
43 if ($askversion) {
44     print("$version\n");
45     exit 0;
46 }
47
48 if ($help || ((@ARGV != 1) && !$single_host)) {
49     warn <<EOF;
50 This is $program, version $version
51 Copyright (C) 1999,2000 Hans Lambermont and Origin B.V.  Disclaimer inside.
52
53 Usage:
54   $program [--help|--peers|--strip <string>|--maxlevel <level>|--version] \\
55     <file>|[--host <hostname>]
56
57 Description:
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
62 Options:
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
80 Examples:
81     $program myhosts.txt --strip .foo.com
82     $program --host some.host --peers --maxlevel 4
83 EOF
84     exit 1;
85 }
86
87 my $hostsfile = shift;
88 my (@hosts, @known_hosts);
89 my (%known_host_info, %known_host_peers);
90
91 sub 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
105 sub 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
119 sub 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
128 sub 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
274 sub 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
286 if ($single_host) {
287     push(@hosts, $single_host);
288 } else {
289     &read_hosts($hostsfile);
290 }
291
292 # Print header
293 print <<EOF;
294 Host                             st offset(s) version     system       processor
295 --------------------------------+--+---------+-----------+------------+---------
296 EOF
297
298 &scan_hosts();
299
300 exit 0;