summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--process.c38
-rw-r--r--test/ruby/test_process.rb25
3 files changed, 50 insertions, 17 deletions
diff --git a/ChangeLog b/ChangeLog
index d364790181..4363053491 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+Thu Dec 4 19:16:28 2008 Tanaka Akira <akr@fsij.org>
+
+ * process.c (check_exec_fds): resolve cascaded child fd reference.
+
Thu Dec 4 16:58:12 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
* lib/rubygems/validator.rb (Gem#remove_leading_dot_dir): make
diff --git a/process.c b/process.c
index 7999d75e11..8767a1bac3 100644
--- a/process.c
+++ b/process.c
@@ -1516,7 +1516,12 @@ check_exec_fds(VALUE options)
if (RTEST(rb_hash_lookup(h, INT2FIX(fd)))) {
rb_raise(rb_eArgError, "fd %d specified twice", fd);
}
- rb_hash_aset(h, INT2FIX(fd), INT2FIX(index));
+ if (index == EXEC_OPTION_OPEN || index == EXEC_OPTION_DUP2)
+ rb_hash_aset(h, INT2FIX(fd), Qtrue);
+ else if (index == EXEC_OPTION_DUP2_CHILD)
+ rb_hash_aset(h, INT2FIX(fd), RARRAY_PTR(elt)[1]);
+ else /* index == EXEC_OPTION_CLOSE */
+ rb_hash_aset(h, INT2FIX(fd), INT2FIX(-1));
if (maxhint < fd)
maxhint = fd;
if (index == EXEC_OPTION_DUP2 || index == EXEC_OPTION_DUP2_CHILD) {
@@ -1527,20 +1532,33 @@ check_exec_fds(VALUE options)
}
}
- /* support cascaded mapping in future?
- * fd1 => [:child, fd2],
- * fd2 => [:child, fd3],
- * fd3 => "/dev/null"
- */
ary = rb_ary_entry(options, EXEC_OPTION_DUP2_CHILD);
if (!NIL_P(ary)) {
for (i = 0; i < RARRAY_LEN(ary); i++) {
VALUE elt = RARRAY_PTR(ary)[i];
+ int newfd = FIX2INT(RARRAY_PTR(elt)[0]);
int oldfd = FIX2INT(RARRAY_PTR(elt)[1]);
- VALUE vindex = rb_hash_lookup(h, INT2FIX(oldfd));
- if (vindex != INT2FIX(EXEC_OPTION_DUP2) &&
- vindex != INT2FIX(EXEC_OPTION_OPEN)) {
+ int lastfd = oldfd;
+ VALUE val = rb_hash_lookup(h, INT2FIX(lastfd));
+ long depth = 0;
+ while (FIXNUM_P(val) && 0 <= FIX2INT(val)) {
+ lastfd = FIX2INT(val);
+ val = rb_hash_lookup(h, val);
+ if (RARRAY_LEN(ary) < depth)
+ rb_raise(rb_eArgError, "cyclic child fd redirection from %d", oldfd);
+ depth++;
+ }
+ if (val != Qtrue)
rb_raise(rb_eArgError, "child fd %d is not redirected", oldfd);
+ if (oldfd != lastfd) {
+ VALUE val2;
+ rb_ary_store(elt, 1, INT2FIX(lastfd));
+ rb_hash_aset(h, INT2FIX(newfd), INT2FIX(lastfd));
+ val = INT2FIX(oldfd);
+ while (FIXNUM_P(val2 = rb_hash_lookup(h, val))) {
+ rb_hash_aset(h, val, INT2FIX(lastfd));
+ val = val2;
+ }
}
}
}
@@ -2941,6 +2959,8 @@ rb_f_system(int argc, VALUE *argv)
* pid = spawn(command, STDOUT=>["log", "w"], STDERR=>[:child, STDOUT])
*
* [:child, STDOUT] can be used to merge STDERR into STDOUT in IO.popen.
+ * In this case, IO.popen redirects STDOUT to a pipe in the child process
+ * and [:child, STDOUT] refers the redirected STDOUT.
*
* io = IO.popen(["sh", "-c", "echo out; echo err >&2", STDERR=>[:child, STDOUT]])
* p io.read #=> "out\nerr\n"
diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb
index 827dde4844..ea90244655 100644
--- a/test/ruby/test_process.rb
+++ b/test/ruby/test_process.rb
@@ -417,21 +417,30 @@ class TestProcess < Test::Unit::TestCase
def test_execopts_redirect_dup2_child
with_tmpchdir {|d|
- Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", STDOUT=>"out", STDERR=>[:child, STDOUT])
+ Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'",
+ STDOUT=>"out", STDERR=>[:child, STDOUT])
assert_equal("errout", File.read("out"))
- Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", STDERR=>"out", STDOUT=>[:child, STDERR])
+
+ Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'",
+ STDERR=>"out", STDOUT=>[:child, STDERR])
+ assert_equal("errout", File.read("out"))
+
+ Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'",
+ STDOUT=>"out",
+ STDERR=>[:child, 3],
+ 3=>[:child, 4],
+ 4=>[:child, STDOUT]
+ )
assert_equal("errout", File.read("out"))
IO.popen([RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", STDERR=>[:child, STDOUT]]) {|io|
assert_equal("errout", io.read)
}
- assert_raise(ArgumentError) {
- Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, STDOUT])
- }
- assert_raise(ArgumentError) {
- Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, 3])
- }
+ assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, STDOUT]) }
+ assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, 3=>[:child, 4], 4=>[:child, 3]) }
+ assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, 3=>[:child, 4], 4=>[:child, 5], 5=>[:child, 3]) }
+ assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, 3]) }
}
end