-
Notifications
You must be signed in to change notification settings - Fork 7
/
util.rb
72 lines (63 loc) · 1.98 KB
/
util.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
require 'set'
module Rubysh
module Util
def self.to_fileno(file)
if file.respond_to?(:fileno)
file.fileno
else
file
end
end
# Leaks memory in 1.8 (needed to avoid Ruby 1.8's IO autoclose
# behavior), and so you should only use it in pre-1.9 right before
# execing.
def self.io_without_autoclose(fd_num)
fd_num = to_fileno(fd_num)
io = IO.new(fd_num)
if io.respond_to?(:autoclose)
io.autoclose = false
else
hold(io)
end
io
end
# Should really just shell out to dup2, but looks like we'd need a
# C extension to do so. The concurrency story here is a bit off,
# and this probably doesn't copy over all FD state
# properly. Should be fine for now.
def self.dup2(fildes, fildes2)
original = io_without_autoclose(fildes)
begin
copy = io_without_autoclose(fildes2)
rescue Errno::EBADF
else
copy.close
end
# For some reason, Ruby 1.9 doesn't seem to let you close
# stdout/sterr. So if we didn't manage to close it above, then
# just use reopen. We could get rid of the close attempt above,
# but I'd rather leave this code as close to doing the same
# thing everywhere as possible.
begin
copy = io_without_autoclose(fildes2)
rescue Errno::EBADF
res = original.fcntl(Fcntl::F_DUPFD, fildes2)
Rubysh.assert(res == fildes2, "Tried to open #{fildes2} but ended up with #{res} instead", true)
else
copy.reopen(original)
end
end
def self.set_cloexec(file, enable=true)
file = io_without_autoclose(file) unless file.kind_of?(IO)
value = enable ? Fcntl::FD_CLOEXEC : 0
file.fcntl(Fcntl::F_SETFD, value)
end
private
@references = []
def self.hold(*references)
# Needed for Ruby 1.8, where we can't set IO objects to not
# close the underlying FD on destruction
@references += references
end
end
end