chiark / gitweb /
polypath: Provide Linux interface monitor
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 29 Sep 2014 14:17:13 +0000 (15:17 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 21 Oct 2014 00:07:10 +0000 (01:07 +0100)
This script monitors the local network interfaces and prints output as
interfaces and addresses appear and disappear.

Install it in $(datarootdir).  Provide $(datarootdir) to C code via a
-D compiler flag.  Provide the appropriate path as the default setting
for `monitor-command' in the polypath module.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Makefile.in
polypath-interface-monitor-linux [new file with mode: 0755]
polypath.c

index 841a49b..f68022a 100644 (file)
@@ -30,11 +30,13 @@ RM:=@RM@
 CC:=@CC@
 INSTALL:=@INSTALL@
 INSTALL_PROGRAM:=@INSTALL_PROGRAM@
+INSTALL_SCRIPT:=@INSTALL_SCRIPT@
 
 prefix:=@prefix@
 exec_prefix:=@exec_prefix@
 sbindir:=@sbindir@
 sysconfdir:=@sysconfdir@
+datarootdir:=@datarootdir@
 transform:=@program_transform_name@
 mandir:=@mandir@
 
@@ -47,7 +49,7 @@ CFLAGS:=-Wall @WRITESTRINGS@ @CFLAGS@ -Werror \
        -Wno-strict-aliasing -fno-strict-aliasing \
        -MMD
 ALL_CFLAGS:=@DEFS@ -I$(srcdir) -I. $(CFLAGS) $(EXTRA_CFLAGS)
-CPPFLAGS:=@CPPFLAGS@ $(EXTRA_CPPFLAGS)
+CPPFLAGS:=@CPPFLAGS@ -DDATAROOTDIR='"$(datarootdir)"' $(EXTRA_CPPFLAGS)
 LDFLAGS:=@LDFLAGS@ $(EXTRA_LDFLAGS)
 LDLIBS:=@LIBS@ $(EXTRA_LDLIBS)
 
@@ -166,6 +168,7 @@ check-ipaddrset: ipaddrset-test.py ipaddrset.py ipaddrset-test.expected
 installdirs:
        $(INSTALL) -d $(prefix)/share/secnet $(sbindir)
        $(INSTALL) -d $(mandir)/man8
+       $(INSTALL) -d $(datarootdir)/secnet
 
 install: installdirs
        set -e; ok=true; for f in $(STALE_PYTHON_FILES); do \
@@ -179,6 +182,8 @@ install: installdirs
        $(INSTALL_PROGRAM) secnet $(sbindir)/`echo secnet|sed '$(transform)'`
        $(INSTALL_PROGRAM) ${srcdir}/make-secnet-sites $(sbindir)/`echo make-secnet-sites|sed '$(transform)'`
        $(INSTALL) ${srcdir}/ipaddrset.py $(prefix)/share/secnet/ipaddrset.py
+       $(INSTALL_SCRIPT) ${srcdir}/polypath-interface-monitor-linux \
+               $(datarootdir)/secnet/.
        $(INSTALL) secnet.8 $(mandir)/man8/secnet.8
 
 install-force:
diff --git a/polypath-interface-monitor-linux b/polypath-interface-monitor-linux
new file mode 100755 (executable)
index 0000000..1b02bfa
--- /dev/null
@@ -0,0 +1,82 @@
+#!/usr/bin/perl -w
+use strict;
+use IO::Handle;
+
+my $us = $0;
+$us =~ s{.*/}{};
+
+die "$us: no arguments permitted\n" if @ARGV;
+
+our ($monh,$monchild);
+
+our %reported;
+#  no entry: not reported, does not exist
+#  /ry+/: reported, entry exists
+# during processing only:
+#  /r/: reported, may not still exist
+#  /y+/: not reported, entry exists
+
+sub killmonitor () {
+    return unless $monchild;
+    kill 9, $monchild
+       or warn "$us: cannot kill monitor child [$monchild]: $!\n";
+    $monchild=undef;
+    close $monh;
+}
+
+END { killmonitor(); }
+
+my $restart;
+
+for (;;) {
+    my $o;
+    eval {
+       if (!$monh) {
+           killmonitor();
+           $monh = new IO::File;
+           $monchild = open $monh, "-|", qw(ip -o monitor addr)
+               or die "spawn monitor: $!\n";
+           sleep(1) if $restart++;
+       } else {
+           my $discard;
+           my $got = sysread $monh, $discard, 4096;
+           die "read monitor: $!\n" unless defined $got;
+           die "monitor failed\n" unless $got;
+       }
+       $_='r' foreach values %reported;
+       foreach my $ip (qw(4 6)) {
+           my $addrh = new IO::File;
+           open $addrh, "-|", qw(ip -o), "-$ip", qw(addr show)
+               or die "spawn addr $ip show: $!\n";
+           my $afstr = $ip==4 ? 'inet' : $ip==6 ? 'inet6' : die;
+           while (<$addrh>) {
+               if (m{^\d+\:\s*(\S+)\s+$afstr\s+([0-9a-z.:]+)(?:/\d+)?\s}) {
+                   my $outline = "$ip $1 $2";
+                   $reported{$outline} .= "y";
+               } else {
+                   chomp;
+                   warn "unexpected output from addr $ip show: $_\n";
+               }
+           }
+           my $r = close $addrh;
+           die "addr $ip show failed $!\n" unless $r;
+           $o = '';
+       }
+       foreach my $k (keys %reported) {
+           local $_ = $reported{$k};
+           if (m/^r$/) {
+               $o .= "-$k\n";
+               delete $reported{$k};
+           } elsif (m/^y/) {
+               $o .= "+$k\n";
+           }
+       }
+    };
+    if ($@) {
+       print STDERR "$us: $@";
+       sleep 5;
+       next;
+    }
+    print $o or die $!;
+    STDOUT->flush or die $!;
+}
index 27c3f46..3bd835e 100644 (file)
@@ -43,7 +43,11 @@ static const char *const default_ifname_pats[] = {
 };
 
 static const char *const default_monitor_command[] = {
+#if __linux__
+    DATAROOTDIR "/secnet/" "polypath-interface-monitor-linux", 0
+#else
     0
+#endif
 };
 
 static const char *polypath_addr_to_string(void *commst,