Methods

Included Modules

OpenShift::Utils

Public Class Methods

oo_spawn(command, options = {}) click to toggle source

oo_spawn(command, [, options]) -> [stdout, stderr, exit status]

spawn executes specified command and return its stdout, stderr and exit status. Or, raise exceptions if certain conditions are not met.

command: command line string which is passed to the standard shell

options: hash

:env: hash
  name => val : set the environment variable
  name => nil : unset the environment variable
:unsetenv_others => true   : clear environment variables except specified by :env
:chdir => path             : set current directory when running command
:expected_exitstatus       : An Integer value for the expected return code of command
                           : If not set spawn() returns exitstatus from command otherwise
                           : raise an error if exitstatus is not expected_exitstatus
:timeout                   : Maximum number of seconds to wait for command to finish. default: 3600
:uid                       : spawn command as given user in a SELinux context using runuser/runcon,
                           : stdin for the command is /dev/null
:out                       : If specified, STDOUT from the child process will be redirected to the
                             provided +IO+ object.
:err                       : If specified, STDERR from the child process will be redirected to the
                             provided +IO+ object.

NOTE: If the out or err options are specified, the corresponding return value from oo_spawn will be the incoming/provided IO objects instead of the buffered String output. It's the responsibility of the caller to correctly handle the resulting data type.

# File lib/openshift-origin-node/utils/shell_exec.rb, line 80
def self.oo_spawn(command, options = {})

  options[:env]         ||= {}
  options[:timeout]     ||= 3600
  options[:buffer_size] ||= 32768

  opts                   = {}
  opts[:unsetenv_others] = (options[:unsetenv_others] || false)
  opts[:close_others]    = true
  opts[:in]              = (options[:in] || '/dev/null')
  opts[:chdir]           = options[:chdir] if options[:chdir]

  IO.pipe do |read_stderr, write_stderr|
    IO.pipe do |read_stdout, write_stdout|
      opts[:out] = write_stdout
      opts[:err] = write_stderr

      if options[:uid]
        # lazy init otherwise we end up with a cyclic require...
        require 'openshift-origin-node/utils/selinux'

        current_context  = SELinux.getcon
        target_context   = SELinux.context_from_defaults(SELinux.get_mcs_label(options[:uid]))
        
        # Only switch contexts if necessary
        if (current_context != target_context) || (Process.uid != options[:uid])
          target_name = Etc.getpwuid(options[:uid]).name
          exec        = %{exec /usr/bin/runcon '#{target_context}' /bin/sh -c \\"#{command}\\"}
          command     = %{/sbin/runuser -m -s /bin/sh #{target_name} -c "#{exec}"}
        end
      end

      NodeLogger.trace_logger.debug { "oo_spawn running #{command}: #{opts}" }
      pid = Kernel.spawn(options[:env], command, opts)

      unless pid
        raise OpenShift::Utils::ShellExecutionException.new(
                  "Kernel.spawn failed for command '#{command}'")
      end

      begin
        write_stdout.close
        write_stderr.close

        out, err, status = read_results(pid, read_stdout, read_stderr, options)
        NodeLogger.logger.debug { "Shell command '#{command}' ran. rc=#{status.exitstatus} out=#{out}" }

        if (!options[:expected_exitstatus].nil?) && (status.exitstatus != options[:expected_exitstatus])
          raise OpenShift::Utils::ShellExecutionException.new(
                    "Shell command '#{command}' returned an error. rc=#{status.exitstatus}",
                    status.exitstatus, out, err)
        end

        return [out, err, status.exitstatus]
      rescue TimeoutExceeded => e
        ShellExec.kill_process_tree(pid)
        raise OpenShift::Utils::ShellExecutionException.new(
                  "Shell command '#{command}'' exceeded timeout of #{e.seconds}", -1, out, err)
      end
    end
  end
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.