class Unobtainium::Support::Runner

Runs a shell command and detaches. Offers methods for managing the process lifetime. Implements destroy, so you can use it with the Runtime class to kill the shell command upon script end.

Attributes

command[R]

@return [Array] the command passed to the constructor

id[R]

@return [String] the ID passed to the constructor

pid[R]

@return [Integer] if the command is started, the pid of the process,

or nil otherwise.
stderr[R]

@return [IO] if the command is started, an IO object to read the

commands error output from, or nil otherwise.
stdout[R]

@return [IO] if the command is started, an IO object to read the

commands output from, or nil otherwise.

Public Class Methods

new(id, *command) click to toggle source

Initialize with a shell command, but do not run yet.

@param id [String] a unique ID for the command. This is to differentiate

multiple similar commands from each other.

@param command [Array] the remaining parameters are the command and its

arguments.
# File lib/unobtainium/support/runner.rb, line 45
def initialize(id, *command)
  if command.empty?
    raise ArgumentError, "Command may not be empty!"
  end

  @id = id
  @command = command
  @pid = nil
  @stdout = nil
  @stderr = nil
  @wout = nil
  @werr = nil
end

Public Instance Methods

destroy() click to toggle source

(see kill) Use together with Runtime class to clean up any commands at exit.

# File lib/unobtainium/support/runner.rb, line 149
def destroy
  kill
  reset
end
kill() click to toggle source

Send the “KILL” signal to the command process and all its children

# File lib/unobtainium/support/runner.rb, line 101
def kill
  signal("KILL", scope: :all)
  cleanup
end
reset() click to toggle source

Resets stdout, stderr, etc. - does not kill a process, see kill instead.

# File lib/unobtainium/support/runner.rb, line 86
def reset
  cleanup(true)
end
signal(signal, scope: :self) click to toggle source

Send the given signal to the process, and/or it's children. @param signal [String] the signal to send @param scope [Symbol] one of :self (the command process),

:children (it's children *only) or :all (the process and its
children.
# File lib/unobtainium/support/runner.rb, line 112
def signal(signal, scope: :self)
  if @pid.nil?
    raise "No command is running!"
  end

  if not %i[self children all].include?(scope)
    raise ArgumentError, "The :scope argument must be one of :self, "\
        ":children or :all!"
  end

  # Figure out which pids to send the signal to. That is usually @pid,
  # but possibly its children.
  to_send = []
  if %i[self all].include?(scope)
    to_send << @pid
  end

  if %i[children all].include?(scope)
    children = ::Sys::ProcTable.ps.select { |p| p.ppid == @pid }
    to_send += children.collect(&:pid)
  end

  # Alright, send the signal!
  to_send.each do |pid|
    # rubocop:disable Lint/HandleExceptions
    begin
      Process.kill(signal, pid)
    rescue
      # If the kill didn't work, we don't really care.
    end
    # rubocop:enable Lint/HandleExceptions
  end
end
start() click to toggle source

Start the command. Afterwards, pid, stdout, and stderr should be non-nil. @return [Integer] the pid of the command process

# File lib/unobtainium/support/runner.rb, line 63
def start
  if not @pid.nil?
    raise "Command already running!"
  end

  # Reset everything
  reset

  # Capture options; pipes for stdout and stderr
  @stdout, @wout = IO.pipe
  @stderr, @werr = IO.pipe
  opts = {
    out: @wout,
    err: @werr,
  }

  @pid = spawn({}, *@command, opts)
  return @pid
end
wait() click to toggle source

Wait for the command to exit. @return [Process::Status] exit status of the command.

# File lib/unobtainium/support/runner.rb, line 93
def wait
  _, status = Process.wait2(@pid)
  cleanup
  return status
end

Private Instance Methods

cleanup(all = false) click to toggle source
# File lib/unobtainium/support/runner.rb, line 156
def cleanup(all = false)
  @pid = nil
  # rubocop:disable Style/GuardClause
  if not @wout.nil?
    @wout.close
    @wout = nil
  end
  if not @werr.nil?
    @werr.close
    @werr = nil
  end

  if not all
    return
  end

  if not @stdout.nil?
    @stdout.close
    @stdout = nil
  end
  if not @stderr.nil?
    @stderr.close
    @stderr = nil
  end
  # rubocop:enable Style/GuardClause
end