move safe_fork into a standalone method

This commit is contained in:
Xu Cheng 2015-04-13 18:02:46 +08:00
parent c952fda202
commit 06f72ab38f
3 changed files with 50 additions and 42 deletions

View File

@ -11,8 +11,6 @@ require 'install_renamed'
require 'cmd/tap' require 'cmd/tap'
require 'hooks/bottles' require 'hooks/bottles'
require 'debrew' require 'debrew'
require 'fcntl'
require 'socket'
require 'sandbox' require 'sandbox'
class FormulaInstaller class FormulaInstaller
@ -458,9 +456,6 @@ class FormulaInstaller
end end
def build def build
socket_path = "#{Dir.mktmpdir("homebrew", HOMEBREW_TEMP)}/socket"
server = UNIXServer.new(socket_path)
FileUtils.rm Dir["#{HOMEBREW_LOGS}/#{formula.name}/*"] FileUtils.rm Dir["#{HOMEBREW_LOGS}/#{formula.name}/*"]
@start_time = Time.now @start_time = Time.now
@ -468,9 +463,6 @@ class FormulaInstaller
# 1. formulae can modify ENV, so we must ensure that each # 1. formulae can modify ENV, so we must ensure that each
# installation has a pristine ENV when it starts, forking now is # installation has a pristine ENV when it starts, forking now is
# the easiest way to do this # the easiest way to do this
read, write = IO.pipe
ENV["HOMEBREW_ERROR_PIPE"] = socket_path
args = %W[ args = %W[
nice #{RUBY_PATH} nice #{RUBY_PATH}
-W0 -W0
@ -480,39 +472,13 @@ class FormulaInstaller
#{formula.path} #{formula.path}
].concat(build_argv) ].concat(build_argv)
pid = fork do Utils.safe_fork do
begin
server.close
read.close
write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
if Sandbox.available? && ARGV.sandbox? if Sandbox.available? && ARGV.sandbox?
sandbox = Sandbox.new(formula) sandbox = Sandbox.new(formula)
sandbox.exec(*args) sandbox.exec(*args)
else else
exec(*args) exec(*args)
end end
rescue Exception => e
Marshal.dump(e, write)
write.close
exit! 1
end
end
ignore_interrupts(:quietly) do # the child will receive the interrupt and marshal it back
begin
socket = server.accept_nonblock
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR
retry unless Process.waitpid(pid, Process::WNOHANG)
else
socket.send_io(write)
end
write.close
data = read.read
read.close
Process.wait(pid) unless socket.nil?
raise Marshal.load(data) unless data.nil? or data.empty?
raise Interrupt if $?.exitstatus == 130
raise "Suspicious installation failure" unless $?.success?
end end
raise "Empty installation" if Dir["#{formula.prefix}/*"].empty? raise "Empty installation" if Dir["#{formula.prefix}/*"].empty?
@ -524,9 +490,6 @@ class FormulaInstaller
formula.rack.rmdir_if_possible formula.rack.rmdir_if_possible
end end
raise raise
ensure
server.close
FileUtils.rm_r File.dirname(socket_path)
end end
def link(keg) def link(keg)

View File

@ -4,6 +4,7 @@ require 'os/mac'
require 'utils/json' require 'utils/json'
require 'utils/inreplace' require 'utils/inreplace'
require 'utils/popen' require 'utils/popen'
require 'utils/fork'
require 'open-uri' require 'open-uri'
class Tty class Tty

View File

@ -0,0 +1,44 @@
require "fcntl"
require "socket"
module Utils
def self.safe_fork(&block)
socket_path = "#{Dir.mktmpdir("homebrew", HOMEBREW_TEMP)}/socket"
server = UNIXServer.new(socket_path)
ENV["HOMEBREW_ERROR_PIPE"] = socket_path
read, write = IO.pipe
pid = fork do
begin
server.close
read.close
write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
yield
rescue Exception => e
Marshal.dump(e, write)
write.close
exit! 1
end
end
ignore_interrupts(:quietly) do # the child will receive the interrupt and marshal it back
begin
socket = server.accept_nonblock
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR
retry unless Process.waitpid(pid, Process::WNOHANG)
else
socket.send_io(write)
end
write.close
data = read.read
read.close
Process.wait(pid) unless socket.nil?
raise Marshal.load(data) unless data.nil? or data.empty?
raise Interrupt if $?.exitstatus == 130
raise "Suspicious failure" unless $?.success?
end
ensure
server.close
FileUtils.rm_r File.dirname(socket_path)
end
end