def popen4(cmd, args={}, &b)
args[:user] ||= nil
unless args[:user].kind_of?(Integer)
args[:user] = Etc.getpwnam(args[:user]).uid if args[:user]
end
args[:group] ||= nil
unless args[:group].kind_of?(Integer)
args[:group] = Etc.getgrnam(args[:group]).gid if args[:group]
end
args[:environment] ||= {}
unless args[:environment].has_key?("LC_ALL")
args[:environment]["LC_ALL"] = "C"
end
pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
verbose = $VERBOSE
begin
$VERBOSE = nil
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
if args[:user]
Process.euid = args[:user]
Process.uid = args[:user]
end
if args[:group]
Process.egid = args[:group]
Process.gid = args[:group]
end
args[:environment].each do |key,value|
ENV[key] = value
end
begin
if cmd.kind_of?(Array)
exec(*cmd)
else
exec(cmd)
end
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
Process.waitpid(cid)
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