--Multipart_Wed_Feb_23_17:41:16_2005-1
Content-Type: text/plain; charset=US-ASCII

Hello,

>>>>> On Thu, 17 Feb 2005 04:21:41 +0900
>>>>> matz / ruby-lang.org(Yukihiro Matsumoto)  said:
matz> |Initial Comment:
matz> |$ ruby -e 'require "open3"; stdin,stdout,stderr  pen3.popen3("/bin/true"); stdin.close; puts $?' # prints 256
matz> |256

matz> open3 uses the "double fork" technique, so that you cannot take the
matz> exit status of the command.  But it doesn't have to be 256.

Replacing exit! with exit!(true) is a simple solution.
Following patch is a better exit status solution,
but the child process remains after the grandchild exits.

Index: lib/open3.rb
RCS file: /src/ruby/lib/open3.rb,v
retrieving revision 1.10
diff -u -p -r1.10 open3.rb
--- lib/open3.rb	18 Sep 2003 13:43:42 -0000	1.10
+++ lib/open3.rb	23 Feb 2005 08:38:15 -0000
@@ -17,40 +17,54 @@ module Open3
     pr  O::pipe
     pe  O::pipe
 
-    pid  ork{
+    child_pid  ork{
       # child
-      fork{
+      pw[1].close
+      pr[0].close
+      pe[0].close
+      grandchild_pid  ork{
 	# grandchild
-	pw[1].close
 	STDIN.reopen(pw[0])
 	pw[0].close
 
-	pr[0].close
 	STDOUT.reopen(pr[1])
 	pr[1].close
 
-	pe[0].close
 	STDERR.reopen(pe[1])
 	pe[1].close
 
 	exec(*cmd)
+	exit!(false)
       }
-      exit!
+      pw[0].close
+      pr[1].close
+      pe[1].close
+      STDIN.close
+      STDOUT.close
+      STDERR.close
+      grandchild_pid, status  rocess.waitpid2(grandchild_pid)
+      if status.exited?
+	exit!(status.exitstatus)
+      else
+	# can not pass signaled, etc.
+	exit!(false)
+      end
     }
 
     pw[0].close
     pr[1].close
     pe[1].close
-    Process.waitpid(pid)
     pi  pw[1], pr[0], pe[0]]
     pw[1].sync  rue
-    if defined? yield
+    if block_given?
       begin
-	return yield(*pi)
+	ret  ield(*pi)
       ensure
 	pi.each{|p| p.close unless p.closed?}
+	Process.waitpid(child_pid)
       end
     end
+    Thread.start { Process.waitpid(child_pid) }
     pi
   end
   module_function :popen3



--Multipart_Wed_Feb_23_17:41:16_2005-1
Content-Type: text/x-ruby; charset=US-ASCII
Content-Disposition: attachment; filename="test_open3.rb"
Content-Transfer-Encoding: 7bit

require 'test/unit'
require 'open3'
$:.replace([File.join(File.dirname(File.expand_path(__FILE__)), 'ruby')] | $:)
require 'envutil'

class TestOpen3 < Test::Unit::TestCase
  ruby  nvUtil.rubybin
  system("ruby", "-e", "exit(true)")
  EXIT_SUCCESS  ?.exitstatus
  system("ruby", "-e", "exit(false)")
  EXIT_FAILURE  ?.exitstatus

  def test_popen3_without_block
    w, r, e  pen3.popen3("cat")
    assert_nothing_raised do
      Thread.pass
      w.puts "test!"
    end
    assert_equal("test!\n", r.gets)

    assert_nothing_raised do
      Thread.pass
      w.puts "test!"
      w.close
    end
    assert_equal("test!\n", r.read)
    assert_nothing_raised { Thread.pass } # call waitpid

    # can not use $? since $? is in a thread local scope
  end

  def test_popen3_with_block
    Open3.popen3("cat") do |w, r, e|
      assert_nothing_raised do
        w.puts "test!"
      end
      assert_equal("test!\n", r.gets)

      assert_nothing_raised do
        w.puts "test!"
        w.close
      end
      assert_equal("test!\n", r.read)
    end

    assert_equal(true, $?.exited?)
    assert_equal(EXIT_SUCCESS, $?.exitstatus)
  end

  def test_popen3_with_false
    Open3.popen3("false") {|w, r, e| }
    assert_equal(true, $?.exited?)
    assert_equal(1, $?.exitstatus)
  end

  def test_popen3_with_true
    Open3.popen3("true") {|w, r, e| }
    assert_equal(true, $?.exited?)
    assert_equal(EXIT_SUCCESS, $?.exitstatus)
  end

  def test_popen3_with_exit_3
    ruby  nvUtil.rubybin
    Open3.popen3(ruby, "-e", "exit(3)") {|w, r, e| }
    assert_equal(true, $?.exited?)
    assert_equal(3, $?.exitstatus)
  end

  def test_system_with_signal
    ruby  nvUtil.rubybin
    system(ruby, "-e", "Process.kill(:TERM, $$)")
    assert_equal(false, $?.exited?)
    assert_equal(true, $?.signaled?)
    assert_equal(Signal.list['TERM'], $?.termsig)
  end

  def test_popen3_with_signal
    ruby  nvUtil.rubybin
    Open3.popen3(ruby, "-e", "Process.kill(:SEGV, $$)") {|w, r, e| }
    assert_equal(true, $?.exited?)
    assert_equal(false, $?.signaled?)
    assert_equal(EXIT_FAILURE, $?.exitstatus)
  end
end

--Multipart_Wed_Feb_23_17:41:16_2005-1--