class CommandRunner attr :command attr :child_pid def initialize(*command) @command = command @child_pid = nil @waiting_thread = nil @read_pipe = nil @read_error_pipe = nil @write_pipe = nil end def closeWrite @write_pipe.close end def kill Process.kill("KILL", @child_pid) end def read return @read_pipe.read end def readError return @read_error_pipe.read end def run parent_to_child_read, parent_to_child_write = IO.pipe child_to_parent_read, child_to_parent_write = IO.pipe child_to_parent_error_read, child_to_parent_error_write = IO.pipe @child_pid = fork do parent_to_child_write.close child_to_parent_read.close child_to_parent_error_read.close # Wait until parent is ready before we start doing anything if parent_to_child_read.readchar.chr != "R" raise "Unexpected input from parent" end $stdin.reopen(parent_to_child_read) $stdout.reopen(child_to_parent_write) $stderr.reopen(child_to_parent_error_write) exec(*@command) end @waiting_thread = Thread.new(@child_pid) do |pid| return_code = -1 begin return_code = Process.waitpid2(pid) rescue SystemError raise "Process finished running already!" end return_code = return_code[1].exitstatus return_code end child_to_parent_write.close child_to_parent_error_write.close parent_to_child_read.close @read_pipe = child_to_parent_read @read_error_pipe = child_to_parent_error_read @write_pipe = parent_to_child_write @write_pipe.write("R") @write_pipe.flush end #-------------------------------------------------------------------------- # Waits for command to exit # Returns: The return code of the program when it exited #-------------------------------------------------------------------------- def wait if not @child_pid or not @waiting_thread raise "Waiting for a process that has not started" end return_value = @waiting_thread.value @waiting_thread = nil @child_pid = nil return return_value end def write(string) @write_pipe.write(string) end end