Class: Cute::Execute
- Inherits:
-
Object
- Object
- Cute::Execute
- Defined in:
- lib/cute/execute.rb
Constant Summary collapse
- EXECDEBUG =
false
- @@forkmutex =
Mutex.new
Instance Attribute Summary collapse
-
#command ⇒ Object
readonly
Returns the value of attribute command.
-
#emptypipes ⇒ Object
readonly
Returns the value of attribute emptypipes.
-
#exec_pid ⇒ Object
readonly
Returns the value of attribute exec_pid.
-
#status ⇒ Object
readonly
Returns the value of attribute status.
-
#stderr ⇒ Object
readonly
Returns the value of attribute stderr.
-
#stdout ⇒ Object
readonly
Returns the value of attribute stdout.
Class Method Summary collapse
-
.[](*cmd) ⇒ Object
Same as new function.
-
.init_ios(opts = {:stdin => false}) ⇒ Object
Initialize the pipes and return one array for parent and one array for child.
-
.kill_recursive(pid) ⇒ Object
kill a tree of processes.
Instance Method Summary collapse
-
#close_stdin ⇒ Object
Close stdin of programme if it opened.
-
#free ⇒ Object
Free the command, stdout stderr string.
-
#initialize(*cmd) ⇒ Execute
constructor
A new instance of Execute.
-
#kill ⇒ Object
Kill the launched process.
-
#run(opts = {:stdin => false}) ⇒ Object
Launch the command provided by the constructor.
-
#run!(opts = {:stdin => false}) ⇒ Object
Run the command and return the Execute object.
-
#wait(opts = {:checkstatus => true}) ⇒ Array
Wait the end of process.
-
#write_stdin(str) ⇒ Object
Write to stdin.
Constructor Details
#initialize(*cmd) ⇒ Execute
Returns a new instance of Execute.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/cute/execute.rb', line 9 def initialize(*cmd) @command = *cmd @exec_pid = nil @stdout = nil @stderr = nil @status = nil @run_thread = nil @killed = false @child_io = nil @parent_io = nil @lock = Mutex.new @emptypipes = false end |
Instance Attribute Details
#command ⇒ Object (readonly)
Returns the value of attribute command.
6 7 8 |
# File 'lib/cute/execute.rb', line 6 def command @command end |
#emptypipes ⇒ Object (readonly)
Returns the value of attribute emptypipes.
6 7 8 |
# File 'lib/cute/execute.rb', line 6 def emptypipes @emptypipes end |
#exec_pid ⇒ Object (readonly)
Returns the value of attribute exec_pid.
6 7 8 |
# File 'lib/cute/execute.rb', line 6 def exec_pid @exec_pid end |
#status ⇒ Object (readonly)
Returns the value of attribute status.
6 7 8 |
# File 'lib/cute/execute.rb', line 6 def status @status end |
#stderr ⇒ Object (readonly)
Returns the value of attribute stderr.
6 7 8 |
# File 'lib/cute/execute.rb', line 6 def stderr @stderr end |
#stdout ⇒ Object (readonly)
Returns the value of attribute stdout.
6 7 8 |
# File 'lib/cute/execute.rb', line 6 def stdout @stdout end |
Class Method Details
.[](*cmd) ⇒ Object
Same as new function
34 35 36 |
# File 'lib/cute/execute.rb', line 34 def self.[](*cmd) self.new(*cmd) end |
.init_ios(opts = {:stdin => false}) ⇒ Object
Initialize the pipes and return one array for parent and one array for child.
39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/cute/execute.rb', line 39 def self.init_ios(opts={:stdin => false}) if opts[:stdin] in_r, in_w = IO::pipe in_w.sync = true else in_r, in_w = [nil,nil] end out_r, out_w = opts[:stdout] == false ? [nil,nil] : IO::pipe err_r, err_w = opts[:stderr] == false ? [nil,nil] : IO::pipe [ [in_r,out_w,err_w], [in_w,out_r,err_r] ] end |
.kill_recursive(pid) ⇒ Object
kill a tree of processes. The killing is done in three steps: 1) STOP the target process 2) recursively kill all children 3) KILL the target process
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/cute/execute.rb', line 160 def self.kill_recursive(pid) puts "Killing PID #{pid} from PID #{$$}" if EXECDEBUG # SIGSTOPs the process to avoid it creating new children begin Process.kill('STOP',pid) rescue Errno::ESRCH # "no such process". The process was already killed, return. puts "got ESRCH on STOP" if EXECDEBUG return end # Gather the list of children before killing the parent in order to # be able to kill children that will be re-attached to init children = `ps --ppid #{pid} -o pid=`.split("\n").collect!{|p| p.strip.to_i} children.compact! puts "Children: #{children}" if EXECDEBUG # Check that the process still exists # Directly kill the process not to generate <defunct> children children.each do |cpid| kill_recursive(cpid) end if children begin Process.kill('KILL',pid) rescue Errno::ESRCH # "no such process". The process was already killed, return. puts "got ESRCH on KILL" if EXECDEBUG return end end |
Instance Method Details
#close_stdin ⇒ Object
Close stdin of programme if it opened.
112 113 114 115 116 117 118 119 |
# File 'lib/cute/execute.rb', line 112 def close_stdin() @lock.synchronize do if @parent_io and @parent_io[0] and !@parent_io[0].closed? @parent_io[0].close @parent_io[0] = nil end end end |
#free ⇒ Object
Free the command, stdout stderr string.
27 28 29 30 31 |
# File 'lib/cute/execute.rb', line 27 def free @command = nil @stdout = nil @stderr = nil end |
#kill ⇒ Object
Kill the launched process.
190 191 192 |
# File 'lib/cute/execute.rb', line 190 def kill() @lock.synchronize{ kill! } end |
#run(opts = {:stdin => false}) ⇒ Object
Launch the command provided by the constructor
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/cute/execute.rb', line 60 def run(opts={:stdin => false}) @lock.synchronize do if @run_thread raise "Already launched" else begin ensure #We can't interrupt this process here before run was launched. @child_io, @parent_io = Execute.init_ios(opts) @@forkmutex.synchronize do @exec_pid = fork do run_fork() end end @run_thread = Thread.new do @child_io.each do |io| io.close if io and !io.closed? end @child_io = nil emptypipes = true @stdout,emptypipes = read_parent_io(1,opts[:stdout_size],emptypipes) @stderr,emptypipes = read_parent_io(2,opts[:stderr_size],emptypipes) _, @status = Process.wait2(@exec_pid) @exec_pid = nil @parent_io.each do |io| io.close if io and !io.closed? end @parent_io = nil @emptypipes = emptypipes end end end end [@exec_pid, *@parent_io] end |
#run!(opts = {:stdin => false}) ⇒ Object
Run the command and return the Execute object.
123 124 125 126 |
# File 'lib/cute/execute.rb', line 123 def run!(opts={:stdin => false}) run(opts) self end |
#wait(opts = {:checkstatus => true}) ⇒ Array
Wait the end of process
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/cute/execute.rb', line 133 def wait(opts={:checkstatus => true}) begin wkilled=true close_stdin() @run_thread.join wkilled=false ensure @lock.synchronize do if wkilled && !@killed kill!() end end @run_thread.join if !@killed # raise SignalException if the process was terminated by a signal and the kill function was not called. raise SignalException.new(@status.termsig) if @status and @status.signaled? raise "Command #{@command.inspect} exited with status #{@status.exitstatus}" if opts[:checkstatus] and !@status.success? end end [ @status, @stdout, @stderr, @emptypipes ] end |
#write_stdin(str) ⇒ Object
Write to stdin
101 102 103 104 105 106 107 108 109 |
# File 'lib/cute/execute.rb', line 101 def write_stdin(str) @lock.synchronize do if @parent_io and @parent_io[0] and !@parent_io[0].closed? @parent_io[0].write(str) else raise "Stdin is closed" end end end |