Sha256: af05b3eefcaba47f64f12a14d4731ab8b6b36f2d6ff8345a6377bfe8ad508ff5

Contents?: true

Size: 1.61 KB

Versions: 3

Compression:

Stored size: 1.61 KB

Contents

require 'fcntl'

# @api private
class R10K::Util::Subprocess::Runner

  attr_accessor :cwd

  attr_reader :io

  attr_reader :pid

  attr_reader :status

  def initialize(argv)
    @argv = argv

    @io = R10K::Util::Subprocess::IO.new
  end

  def start
    exec_r, exec_w = status_pipe()

    @pid = fork do
      exec_r.close
      execute_child(exec_w)
    end

    exec_w.close
    execute_parent(exec_r)
  end

  def wait
    if @pid
      _, @status = Process.waitpid2(@pid)
    end
  end

  def crashed?
    exit_code != 0
  end

  def exit_code
    @status.exitstatus
  end

  private

  def execute_child(exec_w)
    if @cwd
      Dir.chdir @cwd
    end

    # Create a new session for the forked child. This prevents children from
    # ever being the foreground process on a TTY, which is almost always what
    # we want in r10k.
    Process.setsid

    # Reopen file descriptors
    STDOUT.reopen(io.stdout)
    STDERR.reopen(io.stderr)

    executable = @argv.shift
    exec([executable, executable], *@argv)
  rescue SystemCallError => e
    exec_w.write(e.message)
  end

  def execute_parent(exec_r)

    if not exec_r.eof?
      msg = exec_r.read || "exec() failed"
      raise "Could not execute #{@argv.join(' ')}: #{msg}"
    end
  end

  # Create a pipe so that the parent can verify that the child process
  # successfully executed. The pipe will be closed on a successful exec(),
  # and will contain an error message on failure.
  #
  # @return [IO, IO] The reader and writer for this pipe
  def status_pipe
    r_pipe, w_pipe = ::IO.pipe

    w_pipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)

    [r_pipe, w_pipe]
  end
end

Version data entries

3 entries across 3 versions & 1 rubygems

Version Path
r10k-1.2.0 lib/r10k/util/subprocess/runner.rb
r10k-1.2.0rc2 lib/r10k/util/subprocess/runner.rb
r10k-1.2.0rc1 lib/r10k/util/subprocess/runner.rb