diff --git a/Library/Homebrew/build.rb b/Library/Homebrew/build.rb index f45dead83f..fd0fc05b17 100644 --- a/Library/Homebrew/build.rb +++ b/Library/Homebrew/build.rb @@ -10,6 +10,7 @@ require "keg" require "extend/ENV" require "debrew" require "fcntl" +require "socket" class Build attr_reader :formula, :deps, :reqs @@ -172,7 +173,7 @@ class Build end begin - error_pipe = IO.new(ENV["HOMEBREW_ERROR_PIPE"].to_i, "w") + error_pipe = UNIXSocket.open(ENV["HOMEBREW_ERROR_PIPE"], &:recv_io) error_pipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) # Invalidate the current sudo timestamp in case a build script calls sudo diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 515239da77..38bf169964 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -11,6 +11,8 @@ require 'install_renamed' require 'cmd/tap' require 'hooks/bottles' require 'debrew' +require 'fcntl' +require 'socket' class FormulaInstaller include FormulaCellarChecks @@ -455,6 +457,9 @@ 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 @@ -463,8 +468,7 @@ class FormulaInstaller # installation has a pristine ENV when it starts, forking now is # the easiest way to do this read, write = IO.pipe - # I'm guessing this is not a good way to do this, but I'm no UNIX guru - ENV['HOMEBREW_ERROR_PIPE'] = write.to_i.to_s + ENV["HOMEBREW_ERROR_PIPE"] = socket_path args = %W[ nice #{RUBY_PATH} @@ -475,14 +479,11 @@ class FormulaInstaller #{formula.path} ].concat(build_argv) - # Ruby 2.0+ sets close-on-exec on all file descriptors except for - # 0, 1, and 2 by default, so we have to specify that we want the pipe - # to remain open in the child process. - args << { write => write } if RUBY_VERSION >= "2.0" - pid = fork do begin + server.close read.close + write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) exec(*args) rescue Exception => e Marshal.dump(e, write) @@ -492,10 +493,17 @@ class FormulaInstaller 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) + 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? @@ -510,6 +518,9 @@ class FormulaInstaller formula.rack.rmdir_if_possible end raise + ensure + server.close + FileUtils.rm_r File.dirname(socket_path) end def link(keg)