Use UNIXSocket to pass file descriptor
This is a more standard way to pass fd in UNIX world. At the same time, it helps to remove a few hacks and simplifies the code in the sandbox. Closes Homebrew/homebrew#38434. Signed-off-by: Xu Cheng <xucheng@me.com>
This commit is contained in:
parent
cd00abab55
commit
f5c8e3fdbd
@ -10,6 +10,7 @@ require "keg"
|
|||||||
require "extend/ENV"
|
require "extend/ENV"
|
||||||
require "debrew"
|
require "debrew"
|
||||||
require "fcntl"
|
require "fcntl"
|
||||||
|
require "socket"
|
||||||
|
|
||||||
class Build
|
class Build
|
||||||
attr_reader :formula, :deps, :reqs
|
attr_reader :formula, :deps, :reqs
|
||||||
@ -172,7 +173,7 @@ class Build
|
|||||||
end
|
end
|
||||||
|
|
||||||
begin
|
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)
|
error_pipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
||||||
|
|
||||||
# Invalidate the current sudo timestamp in case a build script calls sudo
|
# Invalidate the current sudo timestamp in case a build script calls sudo
|
||||||
|
@ -11,6 +11,8 @@ require 'install_renamed'
|
|||||||
require 'cmd/tap'
|
require 'cmd/tap'
|
||||||
require 'hooks/bottles'
|
require 'hooks/bottles'
|
||||||
require 'debrew'
|
require 'debrew'
|
||||||
|
require 'fcntl'
|
||||||
|
require 'socket'
|
||||||
|
|
||||||
class FormulaInstaller
|
class FormulaInstaller
|
||||||
include FormulaCellarChecks
|
include FormulaCellarChecks
|
||||||
@ -455,6 +457,9 @@ 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
|
||||||
@ -463,8 +468,7 @@ class FormulaInstaller
|
|||||||
# 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
|
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"] = socket_path
|
||||||
ENV['HOMEBREW_ERROR_PIPE'] = write.to_i.to_s
|
|
||||||
|
|
||||||
args = %W[
|
args = %W[
|
||||||
nice #{RUBY_PATH}
|
nice #{RUBY_PATH}
|
||||||
@ -475,14 +479,11 @@ class FormulaInstaller
|
|||||||
#{formula.path}
|
#{formula.path}
|
||||||
].concat(build_argv)
|
].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
|
pid = fork do
|
||||||
begin
|
begin
|
||||||
|
server.close
|
||||||
read.close
|
read.close
|
||||||
|
write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
||||||
exec(*args)
|
exec(*args)
|
||||||
rescue Exception => e
|
rescue Exception => e
|
||||||
Marshal.dump(e, write)
|
Marshal.dump(e, write)
|
||||||
@ -492,10 +493,17 @@ class FormulaInstaller
|
|||||||
end
|
end
|
||||||
|
|
||||||
ignore_interrupts(:quietly) do # the child will receive the interrupt and marshal it back
|
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
|
write.close
|
||||||
data = read.read
|
data = read.read
|
||||||
read.close
|
read.close
|
||||||
Process.wait(pid)
|
Process.wait(pid) unless socket.nil?
|
||||||
raise Marshal.load(data) unless data.nil? or data.empty?
|
raise Marshal.load(data) unless data.nil? or data.empty?
|
||||||
raise Interrupt if $?.exitstatus == 130
|
raise Interrupt if $?.exitstatus == 130
|
||||||
raise "Suspicious installation failure" unless $?.success?
|
raise "Suspicious installation failure" unless $?.success?
|
||||||
@ -510,6 +518,9 @@ 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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user