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