Module: Open4

Defined in:
lib/switchman/open4.rb

Overview

This fixes a bug with exception handling, see github.com/ahoward/open4/pull/30

Class Method Summary collapse

Class Method Details

.do_popen(b = nil, exception_propagation_at = nil, closefds = false, &cmd) ⇒ Object



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
73
74
75
76
77
# File 'lib/switchman/open4.rb', line 6

def self.do_popen(b = nil, exception_propagation_at = nil, closefds=false, &cmd)
  pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe

  verbose = $VERBOSE
  begin
    $VERBOSE = nil

    cid = fork {
      if closefds
        exlist = [0, 1, 2] | [pw,pr,pe,ps].map{|p| [p.first.fileno, p.last.fileno] }.flatten
        ObjectSpace.each_object(IO){|io|
          io.close if (not io.closed?) and (not exlist.include? io.fileno) rescue nil
        }
      end

      pw.last.close
      STDIN.reopen pw.first
      pw.first.close

      pr.first.close
      STDOUT.reopen pr.last
      pr.last.close

      pe.first.close
      STDERR.reopen pe.last
      pe.last.close

      STDOUT.sync = STDERR.sync = true

      begin
        cmd.call(ps)
      rescue Exception => e
        begin
          Marshal.dump(e, ps.last)
          ps.last.flush
        rescue Errno::EPIPE
          raise e
        end
      ensure
        ps.last.close unless ps.last.closed?
      end

      exit!
    }
  ensure
    $VERBOSE = verbose
  end

  [ pw.first, pr.last, pe.last, ps.last ].each { |fd| fd.close }

  Open4.propagate_exception cid, ps.first if exception_propagation_at == :init

  pw.last.sync = true

  pi = [ pw.last, pr.first, pe.first ]

  begin
    return [cid, *pi] unless b

    begin
      b.call(cid, *pi)
    ensure
      pi.each { |fd| fd.close unless fd.closed? }
    end

    Open4.propagate_exception cid, ps.first if exception_propagation_at == :block

    Process.waitpid2(cid).last
  ensure
    ps.first.close unless ps.first.closed?
  end
end