class Toys::Utils::Exec::Executor
An object that manages the execution of a subcommand @private
Public Class Methods
new(exec_opts, spawn_cmd, block)
click to toggle source
# File lib/toys/utils/exec.rb, line 938 def initialize(exec_opts, spawn_cmd, block) @fork_func = spawn_cmd.respond_to?(:call) ? spawn_cmd : nil @spawn_cmd = spawn_cmd.respond_to?(:call) ? nil : spawn_cmd @config_opts = exec_opts.config_opts @spawn_opts = exec_opts.spawn_opts @captures = {} @controller_streams = {} @join_threads = [] @child_streams = [] @parent_streams = [] @block = block @default_stream = @config_opts[:background] ? :null : :inherit @mutex = ::Mutex.new end
Public Instance Methods
execute()
click to toggle source
# File lib/toys/utils/exec.rb, line 953 def execute setup_in_stream setup_out_stream(:out) setup_out_stream(:err) log_command controller = start_with_controller return controller if @config_opts[:background] begin @block&.call(controller) controller.result ensure controller.close_streams(:both) end end
Private Instance Methods
capture_stream_thread(key)
click to toggle source
# File lib/toys/utils/exec.rb, line 1299 def capture_stream_thread(key) stream = make_out_pipe(key) @join_threads << ::Thread.new do begin data = stream.read @mutex.synchronize do @captures[key] = data end ensure stream.close end end end
copy_from_out_thread(key, io)
click to toggle source
# File lib/toys/utils/exec.rb, line 1287 def copy_from_out_thread(key, io) stream = make_out_pipe(key) @join_threads << ::Thread.new do begin ::IO.copy_stream(stream, io) ensure stream.close io.close end end end
copy_to_in_thread(io)
click to toggle source
# File lib/toys/utils/exec.rb, line 1275 def copy_to_in_thread(io) stream = make_in_pipe @join_threads << ::Thread.new do begin ::IO.copy_stream(io, stream) ensure stream.close io.close end end end
default_log_str(spawn_cmd)
click to toggle source
# File lib/toys/utils/exec.rb, line 978 def default_log_str(spawn_cmd) return nil unless spawn_cmd return spawn_cmd.first if spawn_cmd.size == 1 && spawn_cmd.first.is_a?(::String) cmd_binary = spawn_cmd.first cmd_binary = cmd_binary.first if cmd_binary.is_a?(::Array) ([cmd_binary] + spawn_cmd[1..-1]).inspect end
interpret_in_array(setting)
click to toggle source
# File lib/toys/utils/exec.rb, line 1132 def interpret_in_array(setting) case setting.first when ::Symbol setup_in_stream_of_type(setting.first, setting[1..-1]) when ::String setup_in_stream_of_type(:file, setting) else raise "Unknown value for in: #{setting.inspect}" end end
interpret_in_file(args)
click to toggle source
# File lib/toys/utils/exec.rb, line 1168 def interpret_in_file(args) raise "Expected only file name" unless args.size == 1 && args.first.is_a?(::String) @spawn_opts[:in] = args + [::File::RDONLY] end
interpret_in_io(setting)
click to toggle source
# File lib/toys/utils/exec.rb, line 1124 def interpret_in_io(setting) if setting.fileno.is_a?(::Integer) setup_in_stream_of_type(:parent, [setting.fileno]) else setup_in_stream_of_type(:copy_io, [setting]) end end
interpret_out_array(key, setting)
click to toggle source
# File lib/toys/utils/exec.rb, line 1199 def interpret_out_array(key, setting) case setting.first when ::Symbol setup_out_stream_of_type(key, setting.first, setting[1..-1]) when ::String setup_out_stream_of_type(key, :file, setting) else raise "Unknown value for #{key}: #{setting.inspect}" end end
interpret_out_array_within_fork(stream)
click to toggle source
# File lib/toys/utils/exec.rb, line 1092 def interpret_out_array_within_fork(stream) if stream.first == :child case stream[1] when :err $stderr when :out $stdout end else ::File.open(*stream) end end
interpret_out_file(key, args)
click to toggle source
# File lib/toys/utils/exec.rb, line 1235 def interpret_out_file(key, args) raise "Expected file name" if args.empty? || !args.first.is_a?(::String) raise "Too many file arguments" if args.size > 3 @spawn_opts[key] = args.size == 1 ? args.first : args end
interpret_out_io(key, setting)
click to toggle source
# File lib/toys/utils/exec.rb, line 1191 def interpret_out_io(key, setting) if setting.fileno.is_a?(::Integer) setup_out_stream_of_type(key, :parent, [setting.fileno]) else setup_out_stream_of_type(key, :copy_io, [setting]) end end
log_command()
click to toggle source
# File lib/toys/utils/exec.rb, line 970 def log_command logger = @config_opts[:logger] if logger && @config_opts[:log_level] != false cmd_str = @config_opts[:log_cmd] || default_log_str(@spawn_cmd) logger.add(@config_opts[:log_level] || ::Logger::INFO, cmd_str) if cmd_str end end
make_in_pipe()
click to toggle source
# File lib/toys/utils/exec.rb, line 1247 def make_in_pipe r, w = ::IO.pipe @spawn_opts[:in] = r @child_streams << r @parent_streams << w w.sync = true w end
make_null_stream(key, mode)
click to toggle source
# File lib/toys/utils/exec.rb, line 1241 def make_null_stream(key, mode) f = ::File.open(::File::NULL, mode) @spawn_opts[key] = f @child_streams << f end
make_out_pipe(key)
click to toggle source
# File lib/toys/utils/exec.rb, line 1256 def make_out_pipe(key) r, w = ::IO.pipe @spawn_opts[key] = w @child_streams << w @parent_streams << r r end
run_fork_func()
click to toggle source
# File lib/toys/utils/exec.rb, line 1022 def run_fork_func catch(:result) do if @spawn_opts[:chdir] ::Dir.chdir(@spawn_opts[:chdir]) { @fork_func.call(@config_opts) } else @fork_func.call(@config_opts) end 0 end end
setup_env_within_fork()
click to toggle source
# File lib/toys/utils/exec.rb, line 1033 def setup_env_within_fork if @config_opts[:unsetenv_others] ::ENV.each_key do |k| ::ENV.delete(k) unless @config_opts.key?(k) end end (@config_opts[:env] || {}).each { |k, v| ::ENV[k.to_s] = v.to_s } end
setup_in_stream()
click to toggle source
# File lib/toys/utils/exec.rb, line 1105 def setup_in_stream setting = @config_opts[:in] || @default_stream return unless setting case setting when ::Symbol setup_in_stream_of_type(setting, []) when ::Integer setup_in_stream_of_type(:parent, [setting]) when ::String setup_in_stream_of_type(:file, [setting]) when ::IO, ::StringIO interpret_in_io(setting) when ::Array interpret_in_array(setting) else raise "Unknown value for in: #{setting.inspect}" end end
setup_in_stream_of_type(type, args)
click to toggle source
# File lib/toys/utils/exec.rb, line 1143 def setup_in_stream_of_type(type, args) case type when :controller @controller_streams[:in] = make_in_pipe when :null make_null_stream(:in, "r") when :inherit @spawn_opts[:in] = :in when :close @spawn_opts[:in] = type when :parent @spawn_opts[:in] = args.first when :child @spawn_opts[:in] = [:child, args.first] when :string write_string_thread(args.first.to_s) when :copy_io copy_to_in_thread(args.first) when :file interpret_in_file(args) else raise "Unknown type for in: #{type.inspect}" end end
setup_in_stream_within_fork(stream, stdstream)
click to toggle source
# File lib/toys/utils/exec.rb, line 1049 def setup_in_stream_within_fork(stream, stdstream) in_stream = case stream when ::Integer ::IO.open(stream) when ::Array ::File.open(*stream) when ::String ::File.open(stream, "r") when :close :close else stream if stream.respond_to?(:write) end if in_stream == :close stdstream.close elsif in_stream stdstream.reopen(in_stream) end end
setup_out_stream(key)
click to toggle source
# File lib/toys/utils/exec.rb, line 1173 def setup_out_stream(key) setting = @config_opts[key] || @default_stream case setting when ::Symbol setup_out_stream_of_type(key, setting, []) when ::Integer setup_out_stream_of_type(key, :parent, [setting]) when ::String setup_out_stream_of_type(key, :file, [setting]) when ::IO, ::StringIO interpret_out_io(key, setting) when ::Array interpret_out_array(key, setting) else raise "Unknown value for #{key}: #{setting.inspect}" end end
setup_out_stream_of_type(key, type, args)
click to toggle source
# File lib/toys/utils/exec.rb, line 1210 def setup_out_stream_of_type(key, type, args) case type when :controller @controller_streams[key] = make_out_pipe(key) when :null make_null_stream(key, "w") when :inherit @spawn_opts[key] = key when :close, :out, :err @spawn_opts[key] = type when :parent @spawn_opts[key] = args.first when :child @spawn_opts[key] = [:child, args.first] when :capture capture_stream_thread(key) when :copy_io copy_from_out_thread(key, args.first) when :file interpret_out_file(key, args) else raise "Unknown type for #{key}: #{type.inspect}" end end
setup_out_stream_within_fork(stream, stdstream)
click to toggle source
# File lib/toys/utils/exec.rb, line 1070 def setup_out_stream_within_fork(stream, stdstream) out_stream = case stream when ::Integer ::IO.open(stream) when ::Array interpret_out_array_within_fork(stream) when ::String ::File.open(stream, "w") when :close :close else stream if stream.respond_to?(:write) end if out_stream == :close stdstream.close elsif out_stream stdstream.reopen(out_stream) stdstream.sync = true end end
setup_streams_within_fork()
click to toggle source
# File lib/toys/utils/exec.rb, line 1042 def setup_streams_within_fork @parent_streams.each(&:close) setup_in_stream_within_fork(@spawn_opts[:in], $stdin) setup_out_stream_within_fork(@spawn_opts[:out], $stdout) setup_out_stream_within_fork(@spawn_opts[:err], $stderr) end
start_fork()
click to toggle source
# File lib/toys/utils/exec.rb, line 1005 def start_fork pid = ::Process.fork return pid unless pid.nil? exit_code = -1 begin setup_env_within_fork setup_streams_within_fork exit_code = run_fork_func rescue ::SystemExit => e exit_code = e.status rescue ::Exception => e # rubocop:disable Lint/RescueException warn(([e.inspect] + e.backtrace).join("\n")) ensure ::Kernel.exit!(exit_code) end end
start_process()
click to toggle source
# File lib/toys/utils/exec.rb, line 998 def start_process args = [] args << @config_opts[:env] if @config_opts[:env] args.concat(@spawn_cmd) ::Process.spawn(*args, @spawn_opts) end
start_with_controller()
click to toggle source
# File lib/toys/utils/exec.rb, line 986 def start_with_controller pid = begin @fork_func ? start_fork : start_process rescue ::StandardError => e e end @child_streams.each(&:close) Controller.new(@config_opts[:name], @controller_streams, @captures, pid, @join_threads, @config_opts[:result_callback], @mutex) end
write_string_thread(string)
click to toggle source
# File lib/toys/utils/exec.rb, line 1264 def write_string_thread(string) stream = make_in_pipe @join_threads << ::Thread.new do begin stream.write string ensure stream.close end end end