lib/rbbt/util/cmd.rb in rbbt-util-3.1.0 vs lib/rbbt/util/cmd.rb in rbbt-util-3.2.0

- old
+ new

@@ -1,59 +1,79 @@ require 'rbbt/util/misc' require 'rbbt/util/log' require 'stringio' module CMD - class CMDError < RBBTError; end + class CMDError < RBBTError; end module SmartIO - def self.tie(io, pid = nil, cmd = "", post = nil) - io.instance_eval{ - @pid = pid - @cmd = cmd - @post = post - alias original_close close - def close - begin - self.original_read unless self.closed? or self.eof? - Process.waitpid(@pid) if @pid - rescue - end + attr_accessor :pid, :cmd, :post, :in, :out, :err + def self.tie(io, pid = nil, cmd = "", post = nil, sin = nil, out = nil, err = nil) + io.extend SmartIO + io.pid = pid + io.cmd = cmd + io.in = sin + io.out = out + io.err = err + io.post = post - if $? and not $?.success? - Log.debug "Raising exception" - exception = CMDError.new "Command [#{@pid}] #{@cmd} failed with error status #{$?.exitstatus}" - raise exception - end + io.class.send(:alias_method, :original_close, :close) + io.class.send(:alias_method, :original_read, :read) + io + end - @post.call if @post - original_close + def wait_and_status + if @pid + begin + Process.waitpid(@pid) + rescue end - def force_close - if @pid - Log.debug "Forcing close by killing '#{@pid}'" - Process.kill("KILL", @pid) - Process.waitpid(@pid) - end - @post.call if @post + Log.debug "Process #{ cmd } succeded" if $? and $?.success? + + if $? and not $?.success? + Log.debug "Raising exception" + exception = CMDError.new "Command [#{@pid}] #{@cmd} failed with error status #{$?.exitstatus}" original_close + raise exception end - - alias original_read read - def read - data = Misc.fixutf8(original_read) - self.close unless self.closed? - data - end + end + end - } - io + def close + self.original_read unless self.eof? + + wait_and_status + + @post.call if @post + + original_close unless self.closed? end - end + def force_close + if @pid + Log.debug "Forcing close by killing '#{@pid}'" + Process.kill("KILL", @pid) + Process.waitpid(@pid) + end + @post.call if @post + + original_close + end + + def read(*args) + data = original_read(*args) + + self.close if self.eof? + + data + end + + end + + def self.process_cmd_options(options = {}) string = "" options.each do |option, value| case when value.nil? || FalseClass === value @@ -89,82 +109,110 @@ cmd.sub!('\'{opt}\'', cmd_options) else cmd << " " << cmd_options end - sout, serr = IO.pipe, IO.pipe + in_content = StringIO.new in_content if String === in_content - case - when (false and (IO === in_content and not StringIO === in_content)) - sin = [in_content, nil] - else - sin = IO.pipe - end + sout, serr, sin = IO.pipe, IO.pipe, IO.pipe - pid = fork { begin + sin.last.close + sout.first.close + serr.first.close - sin.last.close if sin.last + io = in_content + while IO === io + if SmartIO === io + io.original_close unless io.closed? + io.out.close unless io.out.nil? or io.out.closed? + io.err.close unless io.err.nil? or io.err.closed? + io = io.in + else + io.close unless io.closed? + io = nil + end + end + STDIN.reopen sin.first sin.first.close - serr.first.close STDERR.reopen serr.last serr.last.close - sout.first.close STDOUT.reopen sout.last sout.last.close STDOUT.sync = STDERR.sync = true + exec(cmd) + + exit(-1) rescue Exception + Log.debug("CMDError: #{$!.message}") + ddd $!.backtrace raise CMDError, $!.message end } + sin.first.close sout.last.close serr.last.close + sin = sin.last + sout = sout.first + serr = serr.first + Log.debug "CMD: [#{pid}] #{cmd}" - case - when String === in_content - sin.last.write in_content - sin.last.close - when in_content.respond_to?(:gets) + if in_content.respond_to?(:read) Thread.new do - while not in_content.eof? - sin.last.write in_content.gets + begin + loop do + break if in_content.closed? + block = in_content.read 1024 + break if block.nil? or block.empty? + sin.write block + end + + sin.close unless sin.closed? + in_content.close unless in_content.closed? + rescue + Process.kill "INT", pid + raise $! end - sin.last.close end + else + sin.close end if pipe Thread.new do - while l = serr.first.gets - Log.log l, stderr if Integer === stderr + while line = serr.gets + Log.log line, stderr if Integer === stderr end - serr.first.close + serr.close + Thread.exit end - SmartIO.tie sout.first, pid, cmd, post - sout.first + SmartIO.tie sout, pid, cmd, post, in_content, sin, serr + sout else err = "" Thread.new do - while l = serr.first.gets - err << l if Integer === stderr + while not serr.eof? + err << serr.gets if Integer === stderr end - serr.first.close + serr.close + Thread.exit end - out = StringIO.new sout.first.read - SmartIO.tie out, pid, cmd, post + out = StringIO.new sout.read + sout.close unless sout.closed? + SmartIO.tie out, pid, cmd, post, in_content, sin, serr Process.waitpid pid if not $?.success? exception = CMDError.new "Command [#{pid}] #{cmd} failed with error status #{$?.exitstatus}"