Project

General

Profile

Bug #1856 ยป seg-fault.rb

Source code that re-produces the problem - anthonywright (Anthony Wright), 08/01/2009 10:25 PM

 
1
# Ruby Treasures 0.4
2
# Copyright (C) 2002 Paul Brannan <paul@atdesk.com>
3
# 
4
# You may distribute this software under the same terms as Ruby (see the file
5
# COPYING that was distributed with this library).
6
# 
7
require 'fcntl'
8

    
9
module Open3Z
10
  PROCESSES = Hash.new
11

    
12
  # Make sure we clean up our child processes, and ONLY our child processes.
13
  orig_sigchld_handler = trap "CLD" do |*args|
14
    begin
15
      PROCESSES.each do |pid, foo|
16
        if Process.waitpid(pid, Process::WNOHANG) == pid then
17
          PROCESSES.delete(pid)
18
        end
19
      end
20
    rescue Errno::ECHILD
21
    end
22
    orig_sigchld_handler.call(*args) if not orig_sigchld_handler.nil?
23
  end
24

    
25
  ##
26
  # A version of popen3 that yields (or returns) rd, wr, err, pid
27
  #
28
  def popen3_with_pid(*cmd)
29
    rd  = IO.pipe() # stdin  (child reads from 0, parent writes to 1)
30
    wr  = IO.pipe() # stdout (child writes to 1, parent reads from 0)
31
    err = IO.pipe() # stderr (child writes to 1, parent reads from 0)
32
    ps  = IO.pipe() # status (child writes to 1, parent reads from 0)
33

    
34
    # Set "close on exec".  As soon as the pipe is closed, we know that the
35
    # child process has made a successful call to exec (if we ever read data
36
    # from the pipe, though, it is an exception that was Marshaled from the
37
    # child).
38
    ps[1].fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
39

    
40
    pid = fork do
41
      # child
42
      rd[1].close  ; STDIN .reopen(rd[0])  ; rd[0].close
43
      wr[0].close  ; STDOUT.reopen(wr[1])  ; wr[1].close
44
      err[0].close ; STDERR.reopen(err[1]) ; err[1].close
45

    
46
      begin
47
        exec(*cmd)
48
        raise "exec returned!"
49
      rescue Exception
50
        Marshal.dump($!, ps[1])
51
        ps[1].flush
52
      end
53
      ps[1].close unless ps[1].closed?
54
      exit!
55
    end
56

    
57
    # parent
58
    rd[0].close
59
    wr[1].close
60
    err[1].close
61
    ps[1].close
62

    
63
    exc = nil
64
    begin
65
      exc = Marshal.load(ps[0])
66
    rescue EOFError
67
      # If we get an EOF error, then the exec was successful.
68
    end
69

    
70
    # If exc is set, then the exec was NOT successful.
71
    if not exc.nil? then
72
      raise exc
73
    end
74

    
75
    pi = [rd[1], wr[0], err[0], pid]
76
    if block_given? then
77
      begin
78
        return yield(*pi)
79
      ensure
80
        [rd[1], wr[0], err[0]].each do |p|
81
          p.close unless p.closed?
82
        end
83
      end
84
    end
85

    
86
    return pi
87
  end
88

    
89
  ##
90
  # The original popen3 that yields (or returns) rd, wr, err
91
  #
92
  def popen3(*cmd, &block)
93
    if block_given? then
94
      popen3_with_pid(*cmd) do |rd, wr, err, pid|
95
        yield rd, wr, err
96
        Process.waitpid(pid)
97
      end
98
    else
99
      rd, wr, err, pid = popen3_with_pid(*cmd)
100
      PROCESSES[pid] = true
101
      if Process.waitpid(pid, Process::WNOHANG) == pid then
102
        PROCESSES.delete(pid)
103
      end
104
      return rd, wr, err
105
    end
106
  end
107

    
108
  module_function :popen3_with_pid
109
  module_function :popen3
110
end
111

    
112

    
113
while true
114
	Open3Z.popen3("/bin/ls -l /bin") { |stdin,stdout,stderr|
115
		stdout.readlines.each { |line|
116
			puts line
117
		}
118
	}
119
end