Annotation of embedaddon/ntp/scripts/ntpsweep.in, revision 1.1
1.1 ! misho 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;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>