class SafeExec::PipeExecutor

Constants

PAGE_SIZE

Public Class Methods

new(stdin, stdout, stderr) click to toggle source
# File lib/safe_exec/pipe_executor.rb, line 11
def initialize(stdin, stdout, stderr)
  @stdin, @stdout, @stderr = stdin, stdout, stderr
  @mutex = Mutex.new
end

Public Instance Methods

run(cmd, *args) { |wait_thr| ... } click to toggle source
# File lib/safe_exec/pipe_executor.rb, line 16
def run(cmd, *args)
  assert_untainted_command(cmd, *args)
  @mutex.synchronize do
    Open3.popen3([cmd, cmd], *args) do |stdin, stdout, stderr, wait_thr|
      @threads = [
        pusher(@stdin, stdin),
        drainer(stdout, @stdout),
        drainer(stderr, @stderr)
      ]
      @threads.each { |t| t.abort_on_exception = true }

      yield wait_thr if block_given?

      wait_thr.value.tap { @threads.each { |t| t.join } }
    end
  end
end
timeout(seconds, exception = Timeout::Error) click to toggle source
# File lib/safe_exec/pipe_executor.rb, line 34
def timeout(seconds, exception = Timeout::Error)
  TimeoutDelegate.new(self, seconds, exception)
end

Private Instance Methods

abort() click to toggle source
# File lib/safe_exec/pipe_executor.rb, line 71
def abort
  @threads.each { |t| t.kill }
end
assert_untainted_command(*components) click to toggle source
# File lib/safe_exec/pipe_executor.rb, line 65
def assert_untainted_command(*components)
  components.each do |component|
    raise SecurityError.new("refusing to construct a command line from tainted component #{component}") if component.tainted?
  end
end
drainer(input, output) click to toggle source
# File lib/safe_exec/pipe_executor.rb, line 47
def drainer(input, output)
  Thread.new do
    stream(input, output)
    output.flush
  end
end
pusher(input, output) click to toggle source
# File lib/safe_exec/pipe_executor.rb, line 40
def pusher(input, output)
  Thread.new do
    stream(input, output)
    output.close unless output.closed?
  end
end
stream(input, output) click to toggle source
# File lib/safe_exec/pipe_executor.rb, line 54
def stream(input, output)
  until input.closed?
    page = input.read(PAGE_SIZE)
    if page
      output.write(page)
    else
      break
    end
  end
end