_exit(0);
}
+# Returns in the executor process
sub become_monitor () {
-
+ my $child = fork // fail("fork executor: $!");or
+ if (!$child) {
+ #---- executor ----
+ open ::STDIN , "<& $call_fds[0]" or fail("dup for fd0");
+ open ::STDOUT, ">& $call_fds[1]" or fail("dup for fd1");
+ open ::STDERR, ">& $call_fds[2]" or fail("dup for fd2");
+ POSIX::close($_) foreach @call_fds;
+ close CALL;
+ return;
+ }
+
+ #---- monitor [2] ----
+ my $got = waitpid $child, 0 // fail("wait for executor: $!");
+ $got == $child or fail("wait for esecutor gave $got, expected $child");
+
+ protocol_write(pack "L", $?);
+ _exit(0);
+}
+
+sub protocol_write ($) {
+ my ($d) = @_;
+ return if (print CALL $d and flush CALL);
+ _exit(0) if $!==EPIPE || $!==ECONNRESET;
+ fail("protocol write: $!");
+}
+
+sub eintr_retry ($) {
+ my ($f) = @_;
+ for (;;) {
+ my $r = $f->();
+ return $r if defined $r;
+ next if $!==EINTR;
+ return $r;
+ }
+}
+
+sub protocol_read_fail ($) {
+ my ($what) = @_;
+ _exit(0) if $!==ECONNRESET;
+ fail("recv $what: $!");
+}
+
+sub protocol_exchange () {
+ protocol_write('\n');
+
+ @call_fds = map {
+ my $r;
+ for (;;) {
+ $! = 0;
+ $r = IO::FDPass::recv(fileno(CALL));
+ last if $r >= 0;
+ _exit(0) if $!==0;
+ protocol_read_fail("fd $_");
+ }
+ $r;
+ } 0..2;
+
+ my $len;
+ $r = read(CALL, $len, 4) // protocol_read_fail("message length");
+ $r == 4 or _exit(0);
+
+ $len = unpack "L", $len;
+ my $data;
+ $r = read(CALL, $data, $len) // protocol_read_fail("message data ($len)");
+ $r == $len or _exit(0);
+
+ @ARGV = split /\0/, $data;
+ @ARGV >= 2 or fail("message data has too few strings");
+ length(pop(@ARGV)) and fail("message data missing trailing nul");
+ %ENV = ();
+ while (my $s = shift @ARGV) {
+ last if !length $s;
+ $s =~ m/=/ or fail("message data env var missing equals");
+ $ENV{$`} = $';
+ }
}
sub initialisation_complete {
$child = fork // croak "second fork failed: $!";
if (!$child) {
# we are the child, i.e. the one fa-monitor
- become_monitor();
+ return become_monitor();
}
our %children;
if (accept(CALL, LISTEN)) {
$child = fork // fail("fork for accepted call failed: $!");
if (!$child) {
- become_monitor();
+ #---- monitor [1] ----
+ close LISTEN;
+ protocol_exchange();
+ return become_monitor();
}
close(CALL);
$errcount = 0;