chiark / gitweb /
When turning on debug, turn on verbose too.
[secnet.git] / polypath-interface-monitor-linux
1 #!/usr/bin/perl -w
2
3 # This file is part of secnet.
4 # See README for full list of copyright holders.
5 #
6 # secnet is free software; you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version d of the License, or
9 # (at your option) any later version.
10
11 # secnet is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # version 3 along with secnet; if not, see
18 # https://www.gnu.org/licenses/gpl.html.
19
20 use strict;
21 use IO::Handle;
22
23 my $us = $0;
24 $us =~ s{.*/}{};
25
26 open DEBUG, ">/dev/null" or die $!;
27
28 if (@ARGV && $ARGV[0] eq '-D') {
29     shift @ARGV;
30     open DEBUG, ">&STDERR" or die $!;
31 }
32
33 die "$us: no arguments permitted\n" if @ARGV;
34
35 our ($monh,$monchild);
36
37 our %reported;
38 #  no entry: not reported, does not exist
39 #  /ry+/: reported, entry exists
40 # during processing only:
41 #  /r/: reported, may not still exist
42 #  /y+/: not reported, entry exists
43
44 sub killmonitor () {
45     return unless $monchild;
46     kill 9, $monchild
47         or warn "$us: cannot kill monitor child [$monchild]: $!\n";
48     $monchild=undef;
49     close $monh;
50 }
51
52 END { killmonitor(); }
53
54 my $restart;
55
56 for (;;) {
57     my $o;
58     eval {
59         if (!$monh) {
60             killmonitor();
61             $monh = new IO::File;
62             $monchild = open $monh, "-|", qw(ip -o monitor addr)
63                 or die "spawn monitor: $!\n";
64             sleep(1) if $restart++;
65         } else {
66             my $discard;
67             my $got = sysread $monh, $discard, 4096;
68             die "read monitor: $!\n" unless defined $got;
69             die "monitor failed\n" unless $got;
70         }
71         $_='r' foreach values %reported;
72         print DEBUG "#########################################\n";
73         foreach my $ip (qw(4 6)) {
74             print DEBUG "###### $ip:\n";
75             my $addrh = new IO::File;
76             open $addrh, "-|", qw(ip -o), "-$ip", qw(addr show)
77                 or die "spawn addr $ip show: $!\n";
78             my $afstr = $ip==4 ? 'inet' : $ip==6 ? 'inet6' : die;
79             while (<$addrh>) {
80                 print DEBUG "#$_";
81                 if (m{^\d+\:\s*(\S+)\s+$afstr\s+([0-9a-z.:]+)(?:/\d+)?\s}) {
82                     my $rhs=$'; #';
83                     my $outline = "$ip $1 $2";
84                     # "ip -o addr show" has a ridiculous output format.  In
85                     # particular, it mixes output keywords which introduce
86                     # values with ones which don't, and there seems to be
87                     # no way to tell without knowing all the possible
88                     # keywords.  We hope that before the \ there is nothing
89                     # which contains arbitrary text (specifically, which
90                     # might be `tentative' other than to specify IPv6
91                     # tentativeness).  We have to do this for IPv6 only
92                     # because in the IPv4 output, the interface name
93                     # appears here!
94                     next if $ip==6 && $rhs=~m{[^\\]* tentative\s};
95                     $reported{$outline} .= "y";
96                 } else {
97                     chomp;
98                     warn "unexpected output from addr $ip show: $_\n";
99                 }
100             }
101             my $r = close $addrh;
102             die "addr $ip show failed $!\n" unless $r;
103             $o = '';
104         }
105         foreach my $k (keys %reported) {
106             local $_ = $reported{$k};
107             if (m/^r$/) {
108                 $o .= "-$k\n";
109                 delete $reported{$k};
110             } elsif (m/^y/) {
111                 $o .= "+$k\n";
112             }
113         }
114     };
115     if ($@) {
116         print STDERR "$us: $@";
117         sleep 5;
118         next;
119     }
120     print $o or die $!;
121     STDOUT->flush or die $!;
122 }