[#4479] Requesting addition to IRB (configurable standard output) — Sascha Ebach <se@...>

Hello,

13 messages 2005/02/24
[#4482] Re: Requesting addition to IRB (configurable standard output) — Sam Roberts <sroberts@...> 2005/02/25

Quoting se@digitale-wertschoepfung.de, on Fri, Feb 25, 2005 at 01:22:34AM +0900:

[#4483] Re: Requesting addition to IRB (configurable standard output) — Eric Hodel <drbrain@...7.net> 2005/02/25

On 24 Feb 2005, at 19:51, Sam Roberts wrote:

[#4488] Re: Requesting addition to IRB (configurable standard output) — Sam Roberts <sroberts@...> 2005/02/26

Quoting drbrain@segment7.net, on Sat, Feb 26, 2005 at 02:43:31AM +0900:

[#4489] Re: Requesting addition to IRB (configurable standard output) — Eric Hodel <drbrain@...7.net> 2005/02/26

On 25 Feb 2005, at 16:03, Sam Roberts wrote:

Re: [ ruby-Bugs-1504 ] Open3.popen() doesn't set $?

From: Kazuhiro NISHIYAMA <zn@...>
Date: 2005-02-23 08:41:29 UTC
List: ruby-core #4467
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 = Open3.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 = IO::pipe
     pe = IO::pipe
 
-    pid = fork{
+    child_pid = fork{
       # child
-      fork{
+      pw[1].close
+      pr[0].close
+      pe[0].close
+      grandchild_pid = fork{
 	# 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 = Process.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 = true
-    if defined? yield
+    if block_given?
       begin
-	return yield(*pi)
+	ret = yield(*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


Attachments (1)

test_open3.rb (2.08 KB, text/x-ruby)
require 'test/unit'
require 'open3'
$:.replace([File.join(File.dirname(File.expand_path(__FILE__)), 'ruby')] | $:)
require 'envutil'

class TestOpen3 < Test::Unit::TestCase
  ruby = EnvUtil.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 = Open3.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 = EnvUtil.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 = EnvUtil.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 = EnvUtil.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

In This Thread

Prev Next