Build system: Makefile: support DESTDIR.
[secnet] / polypath-interface-monitor-linux
CommitLineData
e61a41a4
IJ
1#!/usr/bin/perl -w
2use strict;
3use IO::Handle;
4
5my $us = $0;
6$us =~ s{.*/}{};
7
90666d10
IJ
8open DEBUG, ">/dev/null" or die $!;
9
10if (@ARGV && $ARGV[0] eq '-D') {
11 shift @ARGV;
12 open DEBUG, ">&STDERR" or die $!;
13}
14
e61a41a4
IJ
15die "$us: no arguments permitted\n" if @ARGV;
16
17our ($monh,$monchild);
18
19our %reported;
20# no entry: not reported, does not exist
21# /ry+/: reported, entry exists
22# during processing only:
23# /r/: reported, may not still exist
24# /y+/: not reported, entry exists
25
26sub killmonitor () {
27 return unless $monchild;
28 kill 9, $monchild
29 or warn "$us: cannot kill monitor child [$monchild]: $!\n";
30 $monchild=undef;
31 close $monh;
32}
33
34END { killmonitor(); }
35
36my $restart;
37
38for (;;) {
39 my $o;
40 eval {
41 if (!$monh) {
42 killmonitor();
43 $monh = new IO::File;
44 $monchild = open $monh, "-|", qw(ip -o monitor addr)
45 or die "spawn monitor: $!\n";
46 sleep(1) if $restart++;
47 } else {
48 my $discard;
49 my $got = sysread $monh, $discard, 4096;
50 die "read monitor: $!\n" unless defined $got;
51 die "monitor failed\n" unless $got;
52 }
53 $_='r' foreach values %reported;
90666d10 54 print DEBUG "#########################################\n";
e61a41a4 55 foreach my $ip (qw(4 6)) {
90666d10 56 print DEBUG "###### $ip:\n";
e61a41a4
IJ
57 my $addrh = new IO::File;
58 open $addrh, "-|", qw(ip -o), "-$ip", qw(addr show)
59 or die "spawn addr $ip show: $!\n";
60 my $afstr = $ip==4 ? 'inet' : $ip==6 ? 'inet6' : die;
61 while (<$addrh>) {
90666d10 62 print DEBUG "#$_";
e61a41a4 63 if (m{^\d+\:\s*(\S+)\s+$afstr\s+([0-9a-z.:]+)(?:/\d+)?\s}) {
caa97633 64 my $rhs=$'; #';
e61a41a4 65 my $outline = "$ip $1 $2";
caa97633
IJ
66 # "ip -o addr show" has a ridiculous output format. In
67 # particular, it mixes output keywords which introduce
68 # values with ones which don't, and there seems to be
69 # no way to tell without knowing all the possible
70 # keywords. We hope that before the \ there is nothing
71 # which contains arbitrary text (specifically, which
72 # might be `tentative' other than to specify IPv6
73 # tentativeness). We have to do this for IPv6 only
74 # because in the IPv4 output, the interface name
75 # appears here!
76 next if $ip==6 && $rhs=~m{[^\\]* tentative\s};
e61a41a4
IJ
77 $reported{$outline} .= "y";
78 } else {
79 chomp;
80 warn "unexpected output from addr $ip show: $_\n";
81 }
82 }
83 my $r = close $addrh;
84 die "addr $ip show failed $!\n" unless $r;
85 $o = '';
86 }
87 foreach my $k (keys %reported) {
88 local $_ = $reported{$k};
89 if (m/^r$/) {
90 $o .= "-$k\n";
91 delete $reported{$k};
92 } elsif (m/^y/) {
93 $o .= "+$k\n";
94 }
95 }
96 };
97 if ($@) {
98 print STDERR "$us: $@";
99 sleep 5;
100 next;
101 }
102 print $o or die $!;
103 STDOUT->flush or die $!;
104}