Wrapper around systemu that handles executing of system commands in a way that makes stdout, stderr and status available. Supports timeouts and sets a default sane environment.
s = Shell.new("date", opts) s.runcommand puts s.stdout puts s.stderr puts s.status.exitstatus
Options hash can have:
cwd - the working directory the command will be run from stdin - a string that will be sent to stdin of the program stdout - a variable that will receive stdout, must support << stderr - a variable that will receive stdin, must support << environment - the shell environment, defaults to include LC_ALL=C set to nil to clear the environment even of LC_ALL timeout - a timeout in seconds after which the subprocess is killed, the special value :on_thread_exit kills the subprocess when the invoking thread (typically the agent) has ended
(Not documented)
# File lib/mcollective/shell.rb, line 27 27: def initialize(command, options={}) 28: @environment = {"LC_ALL" => "C"} 29: @command = command 30: @status = nil 31: @stdout = "" 32: @stderr = "" 33: @stdin = nil 34: @cwd = Dir.tmpdir 35: @timeout = nil 36: 37: options.each do |opt, val| 38: case opt.to_s 39: when "stdout" 40: raise "stdout should support <<" unless val.respond_to?("<<") 41: @stdout = val 42: 43: when "stderr" 44: raise "stderr should support <<" unless val.respond_to?("<<") 45: @stderr = val 46: 47: when "stdin" 48: raise "stdin should be a String" unless val.is_a?(String) 49: @stdin = val 50: 51: when "cwd" 52: raise "Directory #{val} does not exist" unless File.directory?(val) 53: @cwd = val 54: 55: when "environment" 56: if val.nil? 57: @environment = {} 58: else 59: @environment.merge!(val.dup) 60: end 61: when "timeout" 62: raise "timeout should be a positive integer or the symbol :on_thread_exit symbol" unless val.eql?(:on_thread_exit) || ( val.is_a?(Fixnum) && val>0 ) 63: @timeout = val 64: end 65: end 66: end
Actually does the systemu call passing in the correct environment, stdout and stderr
# File lib/mcollective/shell.rb, line 69 69: def runcommand 70: opts = {"env" => @environment, 71: "stdout" => @stdout, 72: "stderr" => @stderr, 73: "cwd" => @cwd} 74: 75: opts["stdin"] = @stdin if @stdin 76: 77: 78: thread = Thread.current 79: # Start a double fork and exec with systemu which implies a guard thread. 80: # If a valid timeout is configured the guard thread will terminate the 81: # executing process and reap the pid. 82: # If no timeout is specified the process will run to completion with the 83: # guard thread reaping the pid on completion. 84: @status = systemu(@command, opts) do |cid| 85: begin 86: if timeout.is_a?(Fixnum) 87: # wait for the specified timeout 88: sleep timeout 89: else 90: # sleep while the agent thread is still alive 91: while(thread.alive?) 92: sleep 0.1 93: end 94: end 95: 96: # if the process is still running 97: if (Process.kill(0, cid)) 98: # and a timeout was specified 99: if timeout 100: if Util.windows? 101: Process.kill('KILL', cid) 102: else 103: # Kill the process 104: Process.kill('TERM', cid) 105: sleep 2 106: Process.kill('KILL', cid) if (Process.kill(0, cid)) 107: end 108: end 109: # only wait if the parent thread is dead 110: Process.waitpid(cid) unless thread.alive? 111: end 112: rescue SystemExit 113: rescue Errno::ESRCH 114: rescue Errno::ECHILD 115: Log.warn("Could not reap process '#{cid}'.") 116: rescue Exception => e 117: Log.info("Unexpected exception received while waiting for child process: #{e.class}: #{e}") 118: end 119: end 120: @status.thread.kill 121: @status 122: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.