Annotation of embedaddon/ntp/scripts/ntpsweep.in, revision 1.1.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>