def popen4(*cmd, &b)
pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
verbose = $VERBOSE
begin
$VERBOSE = nil
ps.first.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
cid = fork {
pw.last.close
STDIN.reopen pw.first
pw.first.close
pr.first.close
STDOUT.reopen pr.last
pr.last.close
pe.first.close
STDERR.reopen pe.last
pe.last.close
STDOUT.sync = STDERR.sync = true
begin
exec(*cmd)
raise 'forty-two'
rescue Exception => e
Marshal.dump(e, ps.last)
ps.last.flush
end
ps.last.close unless (ps.last.closed?)
exit!
}
ensure
$VERBOSE = verbose
end
[pw.first, pr.last, pe.last, ps.last].each{|fd| fd.close}
begin
e = Marshal.load ps.first
raise(Exception === e ? e : "unknown failure!")
rescue EOFError
42
ensure
ps.first.close
end
pw.last.sync = true
pi = [pw.last, pr.first, pe.first]
if b
begin
b[cid, *pi]
Process.waitpid2(cid).last
ensure
pi.each{|fd| fd.close unless fd.closed?}
end
else
[cid, pw.last, pr.first, pe.first]
end
end