# Subexec
by Peter Kieltyka
## Description
Subexec is a simple library that spawns an external command with an optional timeout parameter. It relies on Ruby 1.9’s Process.spawn method. Also, it works with synchronous and asynchronous code.
Useful for libraries that are Ruby wrappers for CLI’s. For example, resizing images with ImageMagick’s mogrify command sometimes stalls and never returns control back to the original process. Subexec executes mogrify and preempts if it gets lost.
## Usage
# Print hello sub = Subexec.run “echo ‘hello’ && sleep 3”, :timeout => 5 puts sub.output # returns: hello puts sub.exitstatus # returns: 0
# Timeout process after a second sub = Subexec.run “echo ‘hello’ && sleep 3”, :timeout => 1 puts sub.output # returns: puts sub.exitstatus # returns:
# File lib/subexec.rb, line 45 45: def initialize(command, options={}) 46: self.command = command 47: self.lang = options[:lang] || "C" 48: self.timeout = options[:timeout] || 1 # default is to never timeout 49: self.log_file = options[:log_file] 50: self.exitstatus = 0 51: end
# File lib/subexec.rb, line 116 116: def exec 117: if !(RUBY_PLATFORM =~ /win32|mswin|mingw/).nil? 118: self.output = `set LANG=#{lang} && #{command} 2>&1` 119: else 120: self.output = `LANG=#{lang} && export $LANG && #{command} 2>&1` 121: end 122: self.exitstatus = $?.exitstatus 123: end
# File lib/subexec.rb, line 64 64: def spawn 65: # TODO: weak implementation for log_file support. 66: # Ideally, the data would be piped through to both descriptors 67: r, w = IO.pipe 68: if !log_file.nil? 69: self.pid = Process.spawn({'LANG' => self.lang}, command, [:out, :err] => [log_file, 'a']) 70: else 71: self.pid = Process.spawn({'LANG' => self.lang}, command, STDERR=>w, STDOUT=>w) 72: end 73: w.close 74: 75: @timer = Time.now + timeout 76: timed_out = false 77: 78: waitpid = Proc.new do 79: begin 80: flags = (timeout > 0 ? Process::WUNTRACED|Process::WNOHANG : 0) 81: Process.waitpid(pid, flags) 82: rescue Errno::ECHILD 83: break 84: end 85: end 86: 87: if timeout > 0 88: loop do 89: ret = waitpid.call 90: 91: break if ret == pid 92: sleep 0.01 93: if Time.now > @timer 94: timed_out = true 95: break 96: end 97: end 98: else 99: waitpid.call 100: end 101: 102: if timed_out 103: # The subprocess timed out -- kill it 104: Process.kill(9, pid) rescue Errno::ESRCH 105: self.exitstatus = nil 106: else 107: # The subprocess exited on its own 108: self.exitstatus = $?.exitstatus 109: self.output = r.readlines.join("") 110: end 111: r.close 112: 113: self 114: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.