Home | History | Annotate | Line # | Download | only in NTP
      1      1.1  christos package NTP::Util;
      2      1.1  christos use strict;
      3      1.1  christos use warnings;
      4      1.1  christos use Exporter 'import';
      5      1.1  christos use Carp;
      6  1.1.1.2  christos use version 0.77;
      7      1.1  christos 
      8      1.1  christos our @EXPORT_OK = qw(ntp_read_vars do_dns ntp_peers ntp_sntp_line);
      9      1.1  christos 
     10      1.1  christos my $ntpq_path = 'ntpq';
     11      1.1  christos my $sntp_path = 'sntp';
     12      1.1  christos 
     13      1.1  christos our $IP_AGNOSTIC;
     14      1.1  christos 
     15      1.1  christos BEGIN {
     16      1.1  christos     require Socket;
     17  1.1.1.2  christos     if (version->parse($Socket::VERSION) >= version->parse(1.94)) {
     18      1.1  christos         Socket->import(qw(getaddrinfo getnameinfo SOCK_RAW AF_INET));
     19      1.1  christos         $IP_AGNOSTIC = 1;
     20      1.1  christos     }
     21      1.1  christos     else {
     22      1.1  christos         Socket->import(qw(inet_aton SOCK_RAW AF_INET));
     23      1.1  christos     }
     24      1.1  christos }
     25      1.1  christos 
     26      1.1  christos my %obsolete_vars = (
     27      1.1  christos     phase          => 'offset',
     28      1.1  christos     rootdispersion => 'rootdisp',
     29      1.1  christos );
     30      1.1  christos 
     31      1.1  christos sub ntp_read_vars {
     32      1.1  christos     my ($peer, $vars, $host) = @_;
     33      1.1  christos     my $do_all   = !@$vars;
     34      1.1  christos     my %out_vars = map {; $_ => undef } @$vars;
     35      1.1  christos 
     36      1.1  christos     $out_vars{status_line} = {} if $do_all;
     37      1.1  christos 
     38      1.1  christos     my $cmd = "$ntpq_path -n -c 'rv $peer ".(join ',', @$vars)."'";
     39      1.1  christos     $cmd .= " $host" if defined $host;
     40      1.1  christos     $cmd .= " |";
     41      1.1  christos 
     42      1.1  christos     open my $fh, $cmd or croak "Could not start ntpq: $!";
     43      1.1  christos 
     44      1.1  christos     while (<$fh>) {
     45      1.1  christos         return undef if /Connection refused/;
     46      1.1  christos 
     47      1.1  christos         if (/^asso?c?id=0 status=(\S{4}) (\S+), (\S+),/gi) {
     48      1.1  christos             $out_vars{status_line}{status} = $1;
     49      1.1  christos             $out_vars{status_line}{leap}   = $2;
     50      1.1  christos             $out_vars{status_line}{sync}   = $3;
     51      1.1  christos         }
     52      1.1  christos 
     53      1.1  christos         while (/(\w+)=([^,]+),?\s/g) {
     54      1.1  christos             my ($var, $val) = ($1, $2);
     55      1.1  christos             $val =~ s/^"([^"]+)"$/$1/;
     56      1.1  christos             $var = $obsolete_vars{$var} if exists $obsolete_vars{$var};
     57      1.1  christos             if ($do_all) {
     58      1.1  christos                 $out_vars{$var} = $val
     59      1.1  christos             }
     60      1.1  christos             else {
     61      1.1  christos                 $out_vars{$var} = $val if exists $out_vars{$var};
     62      1.1  christos             }
     63      1.1  christos         }
     64      1.1  christos     }
     65      1.1  christos 
     66      1.1  christos     close $fh or croak "running ntpq failed: $! (exit status $?)";
     67      1.1  christos     return \%out_vars;
     68      1.1  christos }
     69      1.1  christos 
     70      1.1  christos sub do_dns {
     71      1.1  christos     my ($host) = @_;
     72      1.1  christos 
     73      1.1  christos     if ($IP_AGNOSTIC) {
     74      1.1  christos         my ($err, $res);
     75      1.1  christos 
     76      1.1  christos         ($err, $res) = getaddrinfo($host, '', {socktype => SOCK_RAW});
     77      1.1  christos         die "getaddrinfo failed: $err\n" if $err;
     78      1.1  christos 
     79      1.1  christos         ($err, $res) = getnameinfo($res->{addr}, 0);
     80      1.1  christos         die "getnameinfo failed: $err\n" if $err;
     81      1.1  christos 
     82      1.1  christos         return $res;
     83      1.1  christos     }
     84      1.1  christos     # Too old perl, do only ipv4
     85      1.1  christos     elsif ($host =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) {
     86      1.1  christos         return gethostbyaddr inet_aton($host), AF_INET;
     87      1.1  christos     }
     88      1.1  christos     else {
     89      1.1  christos         return;
     90      1.1  christos     }
     91      1.1  christos }
     92      1.1  christos 
     93      1.1  christos sub ntp_peers {
     94      1.1  christos     my ($host) = @_;
     95      1.1  christos 
     96  1.1.1.3  christos     $host ||= '';
     97  1.1.1.3  christos     my $cmd = "$ntpq_path -npw $host |";
     98      1.1  christos 
     99      1.1  christos     open my $fh, $cmd or croak "Could not start ntpq: $!";
    100      1.1  christos 
    101      1.1  christos     <$fh> for 1 .. 2;
    102      1.1  christos 
    103  1.1.1.3  christos     my @columns = qw(tally host refid st t when poll reach delay offset jitter);
    104      1.1  christos     my @peers;
    105      1.1  christos     while (<$fh>) {
    106  1.1.1.3  christos         if (/^([ x+#*o-])((?:[\w.*:-]+\s+){10}|([\w.*:-]+\s+))$/) {
    107      1.1  christos             my $col = 0;
    108  1.1.1.3  christos 	    my @line = ($1, split /\s+/, $2);
    109  1.1.1.3  christos 	    if( @line == 2 ) {
    110  1.1.1.3  christos 		defined ($_ = <$fh>) or last;
    111  1.1.1.3  christos 		s/^\s+//;
    112  1.1.1.3  christos 		push @line, split /\s+/;
    113  1.1.1.3  christos 	    }
    114  1.1.1.3  christos 	    my $r = { map {; $columns[ $col++ ] => $_ } @line };
    115  1.1.1.3  christos 	    $r->{remote} = $r->{tally} . $r->{host};
    116  1.1.1.3  christos             push @peers, $r;
    117      1.1  christos         }
    118      1.1  christos         else {
    119      1.1  christos             #TODO return error (but not needed anywhere now)
    120      1.1  christos             warn "ERROR: $_";
    121      1.1  christos         }
    122      1.1  christos     }
    123      1.1  christos 
    124      1.1  christos     close $fh or croak "running ntpq failed: $! (exit status $?)";
    125      1.1  christos     return \@peers;
    126      1.1  christos }
    127      1.1  christos 
    128      1.1  christos # TODO: we don't need this but it would be nice to have all the line parsed
    129      1.1  christos sub ntp_sntp_line {
    130      1.1  christos     my ($host) = @_;
    131      1.1  christos 
    132      1.1  christos     my $cmd = "$sntp_path $host |";
    133      1.1  christos     open my $fh, $cmd or croak "Could not start sntp: $!";
    134      1.1  christos 
    135      1.1  christos     my ($offset, $stratum);
    136      1.1  christos     while (<$fh>) {
    137      1.1  christos         next if !/^\d{4}-\d\d-\d\d/;
    138      1.1  christos         chomp;
    139      1.1  christos         my @output = split / /;
    140      1.1  christos 
    141      1.1  christos         $offset = $output[3];
    142  1.1.1.5  christos 	if (0) {
    143  1.1.1.5  christos 	} elsif ($output[7] =~ /s(\d{1,2})/) {
    144  1.1.1.5  christos 		$stratum = $1;
    145  1.1.1.5  christos 		# warn "Found stratum at #7\n";
    146  1.1.1.5  christos 	} elsif ($output[8] =~ /s(\d{1,2})/) {
    147  1.1.1.5  christos 		$stratum = $1;
    148  1.1.1.5  christos 		# warn "Found stratum at #8\n";
    149  1.1.1.5  christos 	}
    150      1.1  christos     }
    151      1.1  christos     close $fh or croak "running sntp failed: $! (exit status $?)";
    152      1.1  christos     return ($offset, $stratum);
    153      1.1  christos }
    154  1.1.1.3  christos 
    155  1.1.1.3  christos 1;
    156