Attach to existing pseudoterminal
This commit is contained in:
parent
bbdda36e4c
commit
2741f97050
@ -48,15 +48,15 @@ def log(level, message)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_command(control_socket)
|
def read_command
|
||||||
ready_ios = IO.select([control_socket], [], [], 10)
|
ready_ios = IO.select([$control_socket], [], [], 10)
|
||||||
|
|
||||||
if not ready_ios
|
if not ready_ios
|
||||||
log "error", "timeout while communicating with github-fast-envd"
|
log "error", "timeout while communicating with github-fast-envd"
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
response = control_socket.readline.strip.split(" ", 2)
|
response = $control_socket.readline.strip.split(" ", 2)
|
||||||
|
|
||||||
if response.empty?
|
if response.empty?
|
||||||
log "error", "malformed response from github-fast-envd"
|
log "error", "malformed response from github-fast-envd"
|
||||||
@ -98,8 +98,13 @@ log "info", "connected to control socket"
|
|||||||
|
|
||||||
encoded_script_path = Base64.encode64(script_path).delete("\n")
|
encoded_script_path = Base64.encode64(script_path).delete("\n")
|
||||||
|
|
||||||
|
read_ios = [$control_socket]
|
||||||
|
|
||||||
if $options[:interactive]
|
if $options[:interactive]
|
||||||
$control_socket.puts "new v1 pseudoterminal #{encoded_script_path}"
|
pseudoterminal_path = File.readlink("/proc/self/fd/0")
|
||||||
|
encoded_pseudoterminal_path = Base64.encode64(pseudoterminal_path).delete("\n")
|
||||||
|
|
||||||
|
$control_socket.puts "new v1 pseudoterminal #{encoded_pseudoterminal_path} #{encoded_script_path}"
|
||||||
else
|
else
|
||||||
$control_socket.puts "new v1 named-pipes #{encoded_script_path}"
|
$control_socket.puts "new v1 named-pipes #{encoded_script_path}"
|
||||||
end
|
end
|
||||||
@ -107,7 +112,7 @@ end
|
|||||||
pipes = {"stdin" => nil, "stdout" => nil, "stderr" => nil}
|
pipes = {"stdin" => nil, "stdout" => nil, "stderr" => nil}
|
||||||
|
|
||||||
while true
|
while true
|
||||||
command, arguments = read_command($control_socket)
|
command, arguments = read_command
|
||||||
|
|
||||||
if command == "ready"
|
if command == "ready"
|
||||||
break
|
break
|
||||||
@ -128,36 +133,18 @@ while true
|
|||||||
pipes["stderr"] = File::open("#{pipe_base_path}.stderr", "r")
|
pipes["stderr"] = File::open("#{pipe_base_path}.stderr", "r")
|
||||||
pipes["stderr"].sync = true
|
pipes["stderr"].sync = true
|
||||||
|
|
||||||
read_ios = [$control_socket, $stdin, pipes["stdout"], pipes["stderr"]]
|
read_ios += [$stdin, pipes["stdout"], pipes["stderr"]]
|
||||||
|
|
||||||
log "info", "connected via named pipes"
|
log "info", "connected via named pipes"
|
||||||
elsif command == "pseudoterminal"
|
|
||||||
if arguments.empty?
|
|
||||||
log "error", "malformed response"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
pseudoterminal_path = Base64.decode64(arguments[0])
|
log "info", "ready"
|
||||||
|
$control_socket.puts "ready"
|
||||||
log "info", "connecting to pseudoterminal at #{pseudoterminal_path}"
|
|
||||||
|
|
||||||
pseudoterminal_io = File::open(pseudoterminal_path, File::RDWR | File::NOCTTY)
|
|
||||||
|
|
||||||
pipes["stdin"] = pseudoterminal_io
|
|
||||||
pipes["stdout"] = pseudoterminal_io
|
|
||||||
|
|
||||||
read_ios = [$control_socket, $stdin, pipes["stdout"]]
|
|
||||||
|
|
||||||
log "info", "connected via pseudoterminal"
|
|
||||||
else
|
else
|
||||||
log "error", "malformed response"
|
log "error", "malformed response"
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
log "info", "ready"
|
|
||||||
$control_socket.puts "ready"
|
|
||||||
|
|
||||||
exit_code = "unknown"
|
exit_code = "unknown"
|
||||||
|
|
||||||
while read_ios.include?($control_socket) or read_ios.include?(pipes["stdout"]) or read_ios.include?(pipes["stderr"])
|
while read_ios.include?($control_socket) or read_ios.include?(pipes["stdout"]) or read_ios.include?(pipes["stderr"])
|
||||||
@ -180,7 +167,7 @@ while read_ios.include?($control_socket) or read_ios.include?(pipes["stdout"]) o
|
|||||||
$stderr.write ready_read_io.readpartial(4096)
|
$stderr.write ready_read_io.readpartial(4096)
|
||||||
elsif ready_read_io.equal? $control_socket
|
elsif ready_read_io.equal? $control_socket
|
||||||
log "trace", "reading from control socket"
|
log "trace", "reading from control socket"
|
||||||
command, arguments = read_command($control_socket)
|
command, arguments = read_command
|
||||||
|
|
||||||
if command != "done"
|
if command != "done"
|
||||||
log "error", "malformed response from github-fast-envd"
|
log "error", "malformed response from github-fast-envd"
|
||||||
|
@ -23,6 +23,10 @@ class ClientScriptError < StandardError
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
$original_stdin = $stdin.dup
|
||||||
|
$original_stdout = $stdout.dup
|
||||||
|
$original_stderr = $stderr.dup
|
||||||
|
|
||||||
control_socket_path = "/tmp/github-fast-envd.sock"
|
control_socket_path = "/tmp/github-fast-envd.sock"
|
||||||
|
|
||||||
if File.exist?(control_socket_path) and File.socket?(control_socket_path)
|
if File.exist?(control_socket_path) and File.socket?(control_socket_path)
|
||||||
@ -36,7 +40,7 @@ File.chmod 0700, control_socket_path
|
|||||||
# control_server.close
|
# control_server.close
|
||||||
#end
|
#end
|
||||||
|
|
||||||
$stderr.puts "serving control socket"
|
$original_stderr.puts "serving control socket"
|
||||||
|
|
||||||
connection_id = 0
|
connection_id = 0
|
||||||
|
|
||||||
@ -101,7 +105,7 @@ def set_up_named_pipes(control_socket, connection_id)
|
|||||||
stderr = File::open(stderr, "w")
|
stderr = File::open(stderr, "w")
|
||||||
stderr.sync = true
|
stderr.sync = true
|
||||||
|
|
||||||
$stderr.puts " set up named pipes"
|
$original_stderr.puts " set up named pipes"
|
||||||
|
|
||||||
control_socket.puts "ready"
|
control_socket.puts "ready"
|
||||||
|
|
||||||
@ -117,38 +121,23 @@ def set_up_named_pipes(control_socket, connection_id)
|
|||||||
$stderr.reopen(stderr)
|
$stderr.reopen(stderr)
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_up_pseudoterminal(control_socket)
|
def set_up_pseudoterminal(control_socket, pseudoterminal_path)
|
||||||
require "pty"
|
pseudoterminal_io = File::open(pseudoterminal_path, File::RDWR | File::NOCTTY)
|
||||||
|
|
||||||
master, client = PTY.open
|
$original_stderr.puts " connecting to pseudoterminal #{pseudoterminal_path}"
|
||||||
master.raw!
|
|
||||||
|
|
||||||
File.chmod 0600, client.path
|
$stdin.reopen(pseudoterminal_io)
|
||||||
|
$stdout.reopen(pseudoterminal_io)
|
||||||
|
$stderr.reopen(pseudoterminal_io)
|
||||||
|
|
||||||
client_path = Base64.encode64(client.path).delete("\n")
|
$original_stderr.puts " connected to pseudoterminal #{pseudoterminal_path}"
|
||||||
client.close
|
|
||||||
|
|
||||||
control_socket.puts "pseudoterminal #{client_path}"
|
|
||||||
|
|
||||||
$stderr.puts " set up pseudoterminal"
|
|
||||||
|
|
||||||
control_socket.puts "ready"
|
control_socket.puts "ready"
|
||||||
|
|
||||||
response = control_socket.readline.strip
|
|
||||||
|
|
||||||
if response != "ready"
|
|
||||||
raise ClientError.new "invalid command"
|
|
||||||
Kernel.exit!
|
|
||||||
end
|
|
||||||
|
|
||||||
$stdin.reopen(master)
|
|
||||||
$stdout.reopen(master)
|
|
||||||
$stderr.reopen(master)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
while true
|
while true
|
||||||
control_socket = control_server.accept
|
control_socket = control_server.accept
|
||||||
$stderr.puts "- new connection"
|
$original_stderr.puts "- new connection"
|
||||||
|
|
||||||
begin
|
begin
|
||||||
command, arguments = read_command(control_socket)
|
command, arguments = read_command(control_socket)
|
||||||
@ -157,7 +146,7 @@ while true
|
|||||||
raise ClientError.new "unexpected command"
|
raise ClientError.new "unexpected command"
|
||||||
end
|
end
|
||||||
|
|
||||||
if arguments.length != 3
|
if arguments.empty?
|
||||||
raise ClientError.new "malformed command"
|
raise ClientError.new "malformed command"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -169,59 +158,65 @@ while true
|
|||||||
|
|
||||||
mode = arguments[1]
|
mode = arguments[1]
|
||||||
|
|
||||||
if not ["named-pipes", "pseudoterminal"].include?(mode)
|
if mode == "named-pipes"
|
||||||
|
if arguments.length < 3
|
||||||
|
raise ClientError.new "malformed command"
|
||||||
|
end
|
||||||
|
elsif mode == "pseudoterminal"
|
||||||
|
if arguments.length < 4
|
||||||
|
raise ClientError.new "malformed command"
|
||||||
|
end
|
||||||
|
|
||||||
|
pseudoterminal_path = Base64.decode64(arguments[2])
|
||||||
|
else
|
||||||
raise ClientError.new "unknown mode (#{mode})"
|
raise ClientError.new "unknown mode (#{mode})"
|
||||||
end
|
end
|
||||||
|
|
||||||
script_path = Base64.decode64(arguments[2])
|
script_path = Base64.decode64(arguments.last)
|
||||||
|
|
||||||
connection_id += 1
|
connection_id += 1
|
||||||
|
|
||||||
child_process = fork {
|
child_process = fork {
|
||||||
original_stdin = $stdin.dup
|
|
||||||
original_stdout = $stdout.dup
|
|
||||||
original_stderr = $stderr.dup
|
|
||||||
|
|
||||||
exit_code = "unknown"
|
exit_code = "unknown"
|
||||||
|
|
||||||
if mode == "named-pipes"
|
if mode == "named-pipes"
|
||||||
set_up_named_pipes(control_socket, connection_id)
|
set_up_named_pipes(control_socket, connection_id)
|
||||||
else
|
else
|
||||||
set_up_pseudoterminal(control_socket)
|
set_up_pseudoterminal(control_socket, pseudoterminal_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
original_stderr.puts " executing script " + script_path
|
$original_stderr.puts " executing script " + script_path
|
||||||
|
|
||||||
begin
|
begin
|
||||||
begin
|
begin
|
||||||
load script_path, true
|
load script_path, true
|
||||||
rescue SystemExit => error
|
rescue SystemExit => error
|
||||||
original_stderr.puts " exit code: #{error.status}"
|
$original_stderr.puts " exit code: #{error.status}"
|
||||||
exit_code = error.status
|
exit_code = error.status
|
||||||
rescue StandardError => error
|
rescue StandardError => error
|
||||||
$stdin = original_stdin
|
$stdin = $original_stdin
|
||||||
$stdout = original_stdout
|
$stdout = $original_stdout
|
||||||
$stderr = original_stderr
|
$stderr = $original_stderr
|
||||||
|
|
||||||
raise ClientScriptError.new error
|
raise ClientScriptError.new error
|
||||||
end
|
end
|
||||||
rescue ClientScriptError => error
|
rescue ClientScriptError => error
|
||||||
# TODO: Restore pipes to make sure that syntax errors are caught
|
# TODO: Restore pipes to make sure that syntax errors are caught
|
||||||
encoded_error_output = Base64.encode64(error.source.full_message).delete("\n")
|
encoded_error_output = Base64.encode64(error.source.full_message).delete("\n")
|
||||||
original_stderr.puts " error executing script, ignoring request"
|
$original_stderr.puts " error executing script, ignoring request"
|
||||||
# TODO: if the begin/rescue blog has syntax errors, these go unnoticed
|
# TODO: if the begin/rescue blog has syntax errors, these go unnoticed
|
||||||
begin
|
begin
|
||||||
control_socket.puts "script_error #{encoded_error_output}"
|
control_socket.puts "script_error #{encoded_error_output}"
|
||||||
rescue
|
rescue
|
||||||
end
|
end
|
||||||
rescue ClientError => error
|
rescue ClientError => error
|
||||||
original_stderr.puts " error communicating with client, ignoring request (#{error})"
|
$original_stderr.puts " error communicating with client, ignoring request (#{error})"
|
||||||
begin
|
begin
|
||||||
control_socket.puts "error #{error}"
|
control_socket.puts "error #{error}"
|
||||||
rescue
|
rescue
|
||||||
end
|
end
|
||||||
rescue StandardError => error
|
rescue StandardError => error
|
||||||
original_stderr.puts " error, ignoring request (#{error})"
|
$original_stderr.puts " error, ignoring request (#{error})"
|
||||||
begin
|
begin
|
||||||
control_socket.puts "error internal server error"
|
control_socket.puts "error internal server error"
|
||||||
rescue
|
rescue
|
||||||
@ -229,25 +224,26 @@ while true
|
|||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
# TODO: which exit code to return when interrupted?
|
||||||
control_socket.puts "done #{exit_code}"
|
control_socket.puts "done #{exit_code}"
|
||||||
rescue
|
rescue
|
||||||
end
|
end
|
||||||
control_socket.close
|
control_socket.close
|
||||||
|
|
||||||
original_stderr.puts " finished handling request"
|
$original_stderr.puts " finished handling request"
|
||||||
|
|
||||||
Kernel.exit!
|
Kernel.exit!
|
||||||
}
|
}
|
||||||
|
|
||||||
Process.detach(child_process)
|
Process.detach(child_process)
|
||||||
rescue ClientError => error
|
rescue ClientError => error
|
||||||
$stderr.puts " error communicating with client, ignoring request (#{error})"
|
$original_stderr.puts " error communicating with client, ignoring request (#{error})"
|
||||||
begin
|
begin
|
||||||
control_socket.puts "error #{error}"
|
control_socket.puts "error #{error}"
|
||||||
rescue
|
rescue
|
||||||
end
|
end
|
||||||
rescue StandardError => error
|
rescue StandardError => error
|
||||||
$stderr.puts " error, ignoring request (#{error})"
|
$original_stderr.puts " error, ignoring request (#{error})"
|
||||||
begin
|
begin
|
||||||
control_socket.puts "error internal server error"
|
control_socket.puts "error internal server error"
|
||||||
rescue
|
rescue
|
||||||
|
Loading…
Reference in New Issue
Block a user