Class: LittleGhost::Sandbox::ProcessRunner
- Inherits:
-
Object
- Object
- LittleGhost::Sandbox::ProcessRunner
- Defined in:
- lib/little_ghost/sandbox/process_runner.rb
Overview
Runs one bounded child process and terminates its entire process group when the Run is cancelled or the command exceeds its timeout.
Constant Summary collapse
- POLL_INTERVAL =
:nodoc:
0.01- TERMINATION_GRACE =
0.5
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(command:, timeout:, context: nil, max_output_bytes: 1_000_000, environment: {}, inherit_environment: false, chdir: nil) ⇒ ProcessRunner
constructor
A new instance of ProcessRunner.
- #run ⇒ Object
Constructor Details
#initialize(command:, timeout:, context: nil, max_output_bytes: 1_000_000, environment: {}, inherit_environment: false, chdir: nil) ⇒ ProcessRunner
Returns a new instance of ProcessRunner.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/little_ghost/sandbox/process_runner.rb', line 15 def initialize(command:, timeout:, context: nil, max_output_bytes: 1_000_000, environment: {}, inherit_environment: false, chdir: nil) @command = Array(command).map(&:to_s) @timeout = Float(timeout) @context = context @max_output_bytes = Integer(max_output_bytes) @environment = environment.transform_keys(&:to_s).transform_values(&:to_s) @inherit_environment = inherit_environment @chdir = chdir raise ToolError, "Command must contain an executable" if @command.empty? || @command.first.empty? raise ArgumentError, "timeout must be positive" unless @timeout.positive? && @timeout.finite? raise ArgumentError, "max_output_bytes must be positive" unless @max_output_bytes.positive? end |
Class Method Details
.run ⇒ Object
13 |
# File 'lib/little_ghost/sandbox/process_runner.rb', line 13 def self.run(...) = new(...).run |
Instance Method Details
#run ⇒ Object
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/little_ghost/sandbox/process_runner.rb', line 30 def run result = nil = {pgroup: true, unsetenv_others: !@inherit_environment} [:chdir] = @chdir if @chdir Open3.popen3(@environment, *@command, **) do |stdin, stdout, stderr, wait_thread| stdin.close stdout_reader = Thread.new { drain(stdout) } stderr_reader = Thread.new { drain(stderr) } wait_for(wait_thread, [stdout_reader, stderr_reader]) result = Execution.new( stdout: stdout_reader.value, stderr: stderr_reader.value, exit_code: wait_thread.value.exitstatus ) ensure stdout_reader&.kill stderr_reader&.kill end result rescue Errno::ENOENT Execution.new(stdout: "", stderr: "#{@command.first}: command not found\n", exit_code: 127) end |