Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Process#exec: set close-on-exec to false for fd redirection #1805

Merged
merged 2 commits into from
Nov 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Bug fixes:
* Don't clone methods when setting method to the same visibility (#1794, @XrXr).
* BigDecimal() deal with large rationals precisely (#1797, @XrXr).
* Make it possible to call `instance_exec` with `rb_block_call` (#1802, @XrXr).
* Process#exec: set close-on-exec to false for fd redirection (#1805, @XrXr, @rafaelfranca).

Compatibility:

Expand Down
29 changes: 29 additions & 0 deletions spec/ruby/core/process/exec_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,35 @@

File.read(@name).should == "writing to fd: #{child_fd}"
end

it "lets the process after exec have specified file descriptor dispite close_on_exec" do
map_fd_fixture = fixture __FILE__, "map_fd.rb"
cmd = <<-EOC
f = File.open('#{@name}', 'w+')
puts(f.fileno, f.close_on_exec?)
STDOUT.flush
Process.exec("#{ruby_cmd(map_fd_fixture)} \#{f.fileno}", f.fileno => f.fileno)
EOC

output = ruby_exe(cmd, escape: true)
child_fd, close_on_exec = output.split

child_fd.to_i.should > STDERR.fileno
close_on_exec.should == 'true'
File.read(@name).should == "writing to fd: #{child_fd}"
end

it "sets close_on_exec to false on specified fd even when it fails" do
cmd = <<-EOC
f = File.open('#{__FILE__}', 'r')
puts(f.close_on_exec?)
Process.exec('/', f.fileno => f.fileno) rescue
puts(f.close_on_exec?)
EOC

output = ruby_exe(cmd, escape: true)
output.split.should == ['true', 'false']
end
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions spec/tags/core/process/exec_tags.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ slow:Process.exec with a command array uses the first element as the command nam
slow:Process.exec with a command array coerces the argument using to_ary
slow:Process.exec with a command array raises an ArgumentError if the Array does not have exactly two elements
slow:Process.exec with an options Hash with Integer option keys maps the key to a file descriptor in the child that inherits the file descriptor from the parent specified by the value
slow:Process.exec with an options Hash with Integer option keys lets the process after exec have specified file descriptor dispite close_on_exec
slow:Process.exec with an options Hash with Integer option keys sets close_on_exec to false on specified fd even when it fails
11 changes: 8 additions & 3 deletions src/main/ruby/truffleruby/core/truffle/process_operations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ def spawn_setup(alter_process)

if redirect_fd = @options[:redirect_fd]
redirect_fd.each_slice(2) do |from, to|
redirect_file_descriptor(from, to)
redirect_file_descriptor(from, to, alter_process)
end
end
end
Expand All @@ -415,9 +415,14 @@ def safe_close_on_exec?(fd)
(flags & File::FD_CLOEXEC) != 0
end

def redirect_file_descriptor(from, to)
def redirect_file_descriptor(from, to, alter_process)
to = (-to + 1) if to < 0

if alter_process && from == to
flags = Truffle::POSIX.fcntl(from, File::F_GETFD, 0)
unless flags < 0 || flags & File::FD_CLOEXEC == 0
Truffle::POSIX.fcntl(from, File::F_SETFD, flags ^ File::FD_CLOEXEC)
end
end
result = Truffle::POSIX.dup2(to, from)
Errno.handle if result < 0
end
Expand Down