class Aruba::Processes::SpawnProcess

Spawn a process for command

`SpawnProcess` is not meant for direct use - `SpawnProcess.new` - by users. Only it's public methods are part of the public API of aruba, e.g. `#stdin`, `#stdout`.

@private

Public Class Methods

match?(mode) click to toggle source

Use as default launcher

# File lib/aruba/processes/spawn_process.rb, line 22
def self.match?(mode)
  true
end
new(cmd, exit_timeout, io_wait_timeout, working_directory, environment = ENV.to_hash.dup, main_class = nil, stop_signal = nil, startup_wait_time = 0) click to toggle source

Create process

@param [String] cmd

Command string

@param [Numeric] exit_timeout

The timeout until we expect the command to be finished

@param [Numeric] io_wait_timeout

The timeout until we expect the io to be finished

@param [String] working_directory

The directory where the command will be executed

@param [Hash] environment

Environment variables

@param [Class] main_class

E.g. Cli::App::Runner

@param [String] stop_signal

Name of signal to send to stop process. E.g. 'HUP'.

@param [Numeric] startup_wait_time

The amount of seconds to wait after Aruba has started a command.
Calls superclass method Aruba::Processes::BasicProcess::new
# File lib/aruba/processes/spawn_process.rb, line 51
def initialize(cmd, exit_timeout, io_wait_timeout, working_directory, environment = ENV.to_hash.dup, main_class = nil, stop_signal = nil, startup_wait_time = 0)
  super

  @process      = nil
  @stdout_cache = nil
  @stderr_cache = nil
end

Public Instance Methods

close_io(name) click to toggle source

Close io

# File lib/aruba/processes/spawn_process.rb, line 175
def close_io(name)
  return if stopped?

  if RUBY_VERSION < '1.9'
    @process.io.send(name.to_sym).close
  else
    @process.io.public_send(name.to_sym).close
  end
end
content() click to toggle source

Content of command

@return [String]

The content of the script/command. This might be binary output as
string if your command is a binary executable.
# File lib/aruba/processes/spawn_process.rb, line 263
def content
  File.read command_string.to_s
end
filesystem_status() click to toggle source

Return file system stats for the given command

@return [Aruba::Platforms::FilesystemStatus]

This returns a File::Stat-object
# File lib/aruba/processes/spawn_process.rb, line 254
def filesystem_status
  Aruba.platform.filesystem_status.new(command_string.to_s)
end
pid() click to toggle source

Output pid of process

This is the PID of the spawned process.

# File lib/aruba/processes/spawn_process.rb, line 234
def pid
  @process.pid
end
read_stdout() click to toggle source
# File lib/aruba/processes/spawn_process.rb, line 157
def read_stdout
  # rubocop:disable Metrics/LineLength
  Aruba.platform.deprecated('The use of "#read_stdout" is deprecated. Use "#stdout" instead. To reduce the time to wait for io, pass `:wait_for_io => 0` or some suitable for your use case')
  # rubocop:enable Metrics/LineLength

  stdout(:wait_for_io => 0)
end
send_signal(signal) click to toggle source

Send command a signal

@param [String] signal

The signal, i.e. 'TERM'
# File lib/aruba/processes/spawn_process.rb, line 242
def send_signal(signal)
  fail CommandAlreadyStoppedError, %(Command "#{commandline}" with PID "#{pid}" has already stopped.) if @process.exited?

  Process.kill signal, pid
rescue Errno::ESRCH
  raise CommandAlreadyStoppedError, %(Command "#{commandline}" with PID "#{pid}" has already stopped.)
end
start() { |self| ... } click to toggle source

Run the command

@yield [SpawnProcess]

Run code for process which was started

rubocop:disable Metrics/MethodLength

# File lib/aruba/processes/spawn_process.rb, line 65
def start
  # rubocop:disable Metrics/LineLength
  fail CommandAlreadyStartedError, %(Command "#{commandline}" has already been started. Please `#stop` the command first and `#start` it again. Alternatively use `#restart`.\n#{caller.join("\n")}) if started?
  # rubocop:enable Metrics/LineLength

  @started = true

  @process = ChildProcess.build(*[command_string.to_a, arguments].flatten)
  @stdout_file = Tempfile.new('aruba-stdout-')
  @stderr_file = Tempfile.new('aruba-stderr-')

  @stdout_file.sync = true
  @stderr_file.sync = true

  if RUBY_VERSION >= '1.9'
    @stdout_file.set_encoding('ASCII-8BIT')
    @stderr_file.set_encoding('ASCII-8BIT')
  end

  @exit_status = nil
  @duplex      = true

  before_run

  @process.leader    = true
  @process.io.stdout = @stdout_file
  @process.io.stderr = @stderr_file
  @process.duplex    = @duplex
  @process.cwd       = @working_directory

  @process.environment.update(environment)

  begin
    Aruba.platform.with_environment(environment) do
      @process.start
      sleep startup_wait_time
    end
  rescue ChildProcess::LaunchError => e
    raise LaunchError, "It tried to start #{cmd}. " + e.message
  end

  after_run

  yield self if block_given?
end
stderr(opts = {}) click to toggle source

Access to stderr of process

@param [Hash] opts

Options

@option [Integer] wait_for_io

Wait for IO to be finished

@return [String]

The content of stderr
# File lib/aruba/processes/spawn_process.rb, line 148
def stderr(opts = {})
  return @stderr_cache if stopped?

  wait_for_io opts.fetch(:wait_for_io, io_wait_timeout) do
    @process.io.stderr.flush
    open(@stderr_file.path).read
  end
end
stdin() click to toggle source

Access to stdin of process

# File lib/aruba/processes/spawn_process.rb, line 113
def stdin
  return if @process.exited?

  @process.io.stdin
end
stdout(opts = {}) click to toggle source

Access to stdout of process

@param [Hash] opts

Options

@option [Integer] wait_for_io

Wait for IO to be finished

@return [String]

The content of stdout
# File lib/aruba/processes/spawn_process.rb, line 129
def stdout(opts = {})
  return @stdout_cache if stopped?

  wait_for_io opts.fetch(:wait_for_io, io_wait_timeout) do
    @process.io.stdout.flush
    open(@stdout_file.path).read
  end
end
stop(*) click to toggle source

Stop command

# File lib/aruba/processes/spawn_process.rb, line 186
def stop(*)
  return @exit_status if stopped?

  begin
    @process.poll_for_exit(@exit_timeout)
  rescue ChildProcess::TimeoutError
    @timed_out = true
  end

  terminate
end
terminate() click to toggle source

Terminate command

# File lib/aruba/processes/spawn_process.rb, line 204
def terminate
  return @exit_status if stopped?

  unless @process.exited?
    if @stop_signal
      # send stop signal ...
      send_signal @stop_signal
      # ... and set the exit status
      wait
    else
      @process.stop
    end
  end

  @exit_status  = @process.exit_code

  @stdout_cache = read_temporary_output_file @stdout_file
  @stderr_cache = read_temporary_output_file @stderr_file

  # @stdout_file = nil
  # @stderr_file = nil

  @started = false

  @exit_status
end
wait() click to toggle source

Wait for command to finish

# File lib/aruba/processes/spawn_process.rb, line 199
def wait
  @process.wait
end
write(input) click to toggle source
# File lib/aruba/processes/spawn_process.rb, line 165
def write(input)
  return if stopped?

  @process.io.stdin.write(input)
  @process.io.stdin.flush

  self
end

Private Instance Methods

command_string() click to toggle source
# File lib/aruba/processes/spawn_process.rb, line 269
def command_string
  # gather fully qualified path
  cmd = Aruba.platform.which(command, environment['PATH'])

  fail LaunchError, %(Command "#{command}" not found in PATH-variable "#{environment['PATH']}".) if cmd.nil?

  Aruba.platform.command_string.new(cmd)
end
read_temporary_output_file(file) click to toggle source
# File lib/aruba/processes/spawn_process.rb, line 283
def read_temporary_output_file(file)
  file.flush
  file.rewind
  data = file.read
  file.close

  if RUBY_VERSION >= '1.9'
    data.force_encoding('UTF-8')
  else
    data
  end
end
wait_for_io(time_to_wait) { || ... } click to toggle source
# File lib/aruba/processes/spawn_process.rb, line 278
def wait_for_io(time_to_wait, &block)
  sleep time_to_wait
  yield
end