Class: Spawn

Inherits:
Object
  • Object
show all
Defined in:
lib/spawn.rb

Class Method Summary collapse

Class Method Details

.spawn(*cmd, &b) ⇒ Object



4
5
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
# File 'lib/spawn.rb', line 4

def self.spawn(*cmd, &b)
  pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe

ps.first.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)

cid = fork {
  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
    # WARNING: detect max open fd no - is thre a better way?

    r, w = IO.pipe
    max_fd = r.to_i - 1
    r.close
    w.close

    #puts "setting close on exec for fd's from 3 to #{max_fd}"

    (3..max_fd).each do |fd|
      begin
        IO.for_fd(fd).fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
        #puts "fd #{fd} set for closure on exec"

      rescue Errno::EBADF
      end
    end

    exec(*cmd)
    raise 'forty-two' 
  rescue Exception => e
    Marshal.dump(e, ps.last)
    ps.last.flush
  end
  ps.last.close unless (ps.last.closed?)
  exit!
}

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

  begin
    e = Marshal.load ps.first
    raise(Exception === e ? e : "unknown failure!")
  rescue EOFError # If we get an EOF error, then the exec was successful

    42
  ensure
    ps.first.close
  end

  pw.last.sync = true

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

  if b 
    begin
      b[cid, *pi]
      Process.waitpid2(cid).last
    ensure
      pi.each{|fd| fd.close unless fd.closed?}
    end
  else
    [cid, pw.last, pr.first, pe.first]
  end
end