class Nucleon::Util::Shell

Public Class Methods

connection(name = :core) click to toggle source
# File lib/core/util/shell.rb, line 51
def self.connection(name = :core)
  Nucleon.manager(@@supervisors, name, self)
end

Public Instance Methods

exec(command, options = {}, &code) click to toggle source
# File lib/core/util/shell.rb, line 63
def exec(command, options = {}, &code)
  config = Config.ensure(options)
  
  min   = config.get(:min, 1).to_i
  tries = config.get(:tries, min).to_i
  tries = ( min > tries ? min : tries )
  
  info_prefix  = config.get(:info_prefix, '')
  info_suffix  = config.get(:info_suffix, '')
  error_prefix = config.get(:error_prefix, '')
  error_suffix = config.get(:error_suffix, '')
  
  ui           = config.get(:ui, Nucleon.ui)
  
  conditions   = Nucleon.events(config.get(:exit, {}), true)
  
  $stdout.sync = true
  $stderr.sync = true
  
  system_result = Result.new(command)
  
  for i in tries.downto(1)
    logger.info(">> running shell: #{command}")
    
    begin
      t1, output_new, output_orig, output_reader = pipe_exec_stream($stdout, conditions, { 
        :prefix => info_prefix, 
        :suffix => info_suffix,
        :quiet  => config.get(:quiet, false) 
      }, 'output') do |data|
        system_result.append_output(data)
        code ? code.call(:output, command, data) : true
      end
      
      t2, error_new, error_orig, error_reader = pipe_exec_stream($stderr, conditions, { 
        :prefix => error_prefix, 
        :suffix => error_suffix,
        :quiet  => config.get(:quiet, false)  
      }, 'error') do |data|
        system_result.append_errors(data)
        code ? code.call(:error, command, data) : true
      end
      
      system_success       = system(command)
      system_result.status = $?.exitstatus
      
    ensure
      output_success = close_exec_pipe(t1, $stdout, output_orig, output_new, 'output')
      error_success  = close_exec_pipe(t2, $stderr, error_orig, error_new, 'error')
    end
    
    logger.warn("`#{command}` messages: #{system_result.errors}") if system_result.errors.length > 0
    logger.warn("`#{command}` status: #{system_result.status}") unless system_result.status == 0
      
    success = ( system_success && output_success && error_success )
                
    min -= 1
    break if success && min <= 0 && conditions.empty?
  end
  system_result
end
test_connection() click to toggle source
# File lib/core/util/shell.rb, line 57
def test_connection
  true
end

Protected Instance Methods

check_conditions(data, conditions, match_prefix = '', &code) click to toggle source
# File lib/core/util/shell.rb, line 161
def check_conditions(data, conditions, match_prefix = '', &code)
  prefix = ''
  
  unless ! conditions || conditions.empty?
    conditions.each do |key, event|
      if event.check(data)
        prefix = match_prefix
        conditions.delete(key)
      end
    end
  end
  
  result = true
  if code
    result = code.call
    
    unless prefix.empty?
      case result
      when Hash
        result[:prefix] = prefix
      else
        result = { :success => result, :prefix => prefix }
      end
    end
  end
  return result
end
close_exec_pipe(thread, output, original, write, label) click to toggle source
# File lib/core/util/shell.rb, line 148
def close_exec_pipe(thread, output, original, write, label)
  output.reopen(original)
   
  write.close
  success = thread.value
  
  original.close
  return success
end
pipe_exec_stream(output, conditions, options, label, &code) click to toggle source
# File lib/core/util/shell.rb, line 127
def pipe_exec_stream(output, conditions, options, label, &code)
  original     = output.dup
  read, write  = IO.pipe
  
  match_prefix = ( options[:match_prefix] ? options[:match_prefix] : 'EXIT' )
  
  thread = process_stream(read, original, options, label) do |data|
    check_conditions(data, conditions, match_prefix) do
      code ? code.call(data) : true
    end
  end
  
  thread.abort_on_exception = false
  
  output.reopen(write)    
  return thread, write, original, read
end
process_stream(input, output, options, label, &code) click to toggle source
# File lib/core/util/shell.rb, line 192
def process_stream(input, output, options, label, &code)
  return Thread.new do
    success        = true      
    default_prefix = ( options[:prefix] ? options[:prefix] : '' )
    default_suffix = ( options[:suffix] ? options[:suffix] : '' )
    
    begin
      while ( data = input.readpartial(1024) )
        message = data.strip
        newline = ( data[-1,1].match(/\n/) ? true : false )
                               
        unless message.empty?
          lines = message.split(/\n/)
          lines.each_with_index do |line, index|
            prefix  = default_prefix
            suffix  = default_suffix
            
            unless line.empty?
              if code
                result = code.call(line)
                                        
                if result && result.is_a?(Hash)
                  prefix = result[:prefix]
                  suffix = result[:suffix]
                  result = result[:success]                 
                end
                success = result if success
              end
          
              prefix = ( prefix && ! prefix.empty? ? prefix : '' )
              suffix = ( suffix && ! suffix.empty? ? suffix : '' )            
              eol    = ( index < lines.length - 1 || newline ? "\n" : ' ' )
              
              unless options[:quiet]
                output.write(prefix.lstrip + line + suffix.rstrip + eol)
              end
            end
          end
        end
      end
    rescue EOFError
    end
    
    input.close()
    success
  end
end