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