diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index c87972d2e3e94efab775fe0cf1c89e5d0c74b3a1..32b61072d25b11f40649e13318562eb28f5c2a9c 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -11,8 +11,6 @@ require 'install_renamed' require 'cmd/tap' require 'hooks/bottles' require 'debrew' -require 'fcntl' -require 'socket' require 'sandbox' class FormulaInstaller @@ -458,9 +456,6 @@ class FormulaInstaller end def build - socket_path = "#{Dir.mktmpdir("homebrew", HOMEBREW_TEMP)}/socket" - server = UNIXServer.new(socket_path) - FileUtils.rm Dir["#{HOMEBREW_LOGS}/#{formula.name}/*"] @start_time = Time.now @@ -468,9 +463,6 @@ class FormulaInstaller # 1. formulae can modify ENV, so we must ensure that each # installation has a pristine ENV when it starts, forking now is # the easiest way to do this - read, write = IO.pipe - ENV["HOMEBREW_ERROR_PIPE"] = socket_path - args = %W[ nice #{RUBY_PATH} -W0 @@ -480,39 +472,13 @@ class FormulaInstaller #{formula.path} ].concat(build_argv) - pid = fork do - begin - server.close - read.close - write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) - if Sandbox.available? && ARGV.sandbox? - sandbox = Sandbox.new(formula) - sandbox.exec(*args) - else - exec(*args) - 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) + Utils.safe_fork do + if Sandbox.available? && ARGV.sandbox? + sandbox = Sandbox.new(formula) + sandbox.exec(*args) else - socket.send_io(write) + exec(*args) 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 raise "Empty installation" if Dir["#{formula.prefix}/*"].empty? @@ -524,9 +490,6 @@ class FormulaInstaller formula.rack.rmdir_if_possible end raise - ensure - server.close - FileUtils.rm_r File.dirname(socket_path) end def link(keg) diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb index 2cdd920c934479945462ddc53f1069af1ea60185..ad358581eac76d753977f5dfd539623eba49ac6c 100644 --- a/Library/Homebrew/utils.rb +++ b/Library/Homebrew/utils.rb @@ -4,6 +4,7 @@ require 'os/mac' require 'utils/json' require 'utils/inreplace' require 'utils/popen' +require 'utils/fork' require 'open-uri' class Tty diff --git a/Library/Homebrew/utils/fork.rb b/Library/Homebrew/utils/fork.rb new file mode 100644 index 0000000000000000000000000000000000000000..defe1fa799d9dad38119e37346e70ea28cbac1cb --- /dev/null +++ b/Library/Homebrew/utils/fork.rb @@ -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