udp: Print `&' in address descriptions only if multiple sockets
[secnet] / polypath-interface-monitor-linux
1 #!/usr/bin/perl -w
2 use strict;
3 use IO::Handle;
4
5 my $us = $0;
6 $us =~ s{.*/}{};
7
8 die "$us: no arguments permitted\n" if @ARGV;
9
10 our ($monh,$monchild);
11
12 our %reported;
13 # no entry: not reported, does not exist
14 # /ry+/: reported, entry exists
15 # during processing only:
16 # /r/: reported, may not still exist
17 # /y+/: not reported, entry exists
18
19 sub killmonitor () {
20 return unless $monchild;
21 kill 9, $monchild
22 or warn "$us: cannot kill monitor child [$monchild]: $!\n";
23 $monchild=undef;
24 close $monh;
25 }
26
27 END { killmonitor(); }
28
29 my $restart;
30
31 for (;;) {
32 my $o;
33 eval {
34 if (!$monh) {
35 killmonitor();
36 $monh = new IO::File;
37 $monchild = open $monh, "-|", qw(ip -o monitor addr)
38 or die "spawn monitor: $!\n";
39 sleep(1) if $restart++;
40 } else {
41 my $discard;
42 my $got = sysread $monh, $discard, 4096;
43 die "read monitor: $!\n" unless defined $got;
44 die "monitor failed\n" unless $got;
45 }
46 $_='r' foreach values %reported;
47 foreach my $ip (qw(4 6)) {
48 my $addrh = new IO::File;
49 open $addrh, "-|", qw(ip -o), "-$ip", qw(addr show)
50 or die "spawn addr $ip show: $!\n";
51 my $afstr = $ip==4 ? 'inet' : $ip==6 ? 'inet6' : die;
52 while (<$addrh>) {
53 if (m{^\d+\:\s*(\S+)\s+$afstr\s+([0-9a-z.:]+)(?:/\d+)?\s}) {
54 my $outline = "$ip $1 $2";
55 $reported{$outline} .= "y";
56 } else {
57 chomp;
58 warn "unexpected output from addr $ip show: $_\n";
59 }
60 }
61 my $r = close $addrh;
62 die "addr $ip show failed $!\n" unless $r;
63 $o = '';
64 }
65 foreach my $k (keys %reported) {
66 local $_ = $reported{$k};
67 if (m/^r$/) {
68 $o .= "-$k\n";
69 delete $reported{$k};
70 } elsif (m/^y/) {
71 $o .= "+$k\n";
72 }
73 }
74 };
75 if ($@) {
76 print STDERR "$us: $@";
77 sleep 5;
78 next;
79 }
80 print $o or die $!;
81 STDOUT->flush or die $!;
82 }