| 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 | } |