|
|
|
@ -23,6 +23,10 @@ class ClientScriptError < StandardError
|
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
$original_stdin = $stdin.dup |
|
|
|
|
$original_stdout = $stdout.dup |
|
|
|
|
$original_stderr = $stderr.dup |
|
|
|
|
|
|
|
|
|
control_socket_path = "/tmp/github-fast-envd.sock" |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
#end |
|
|
|
|
|
|
|
|
|
$stderr.puts "serving control socket" |
|
|
|
|
$original_stderr.puts "serving control socket" |
|
|
|
|
|
|
|
|
|
connection_id = 0 |
|
|
|
|
|
|
|
|
@ -101,7 +105,7 @@ def set_up_named_pipes(control_socket, connection_id)
|
|
|
|
|
stderr = File::open(stderr, "w") |
|
|
|
|
stderr.sync = true |
|
|
|
|
|
|
|
|
|
$stderr.puts " set up named pipes" |
|
|
|
|
$original_stderr.puts " set up named pipes" |
|
|
|
|
|
|
|
|
|
control_socket.puts "ready" |
|
|
|
|
|
|
|
|
@ -117,38 +121,23 @@ def set_up_named_pipes(control_socket, connection_id)
|
|
|
|
|
$stderr.reopen(stderr) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def set_up_pseudoterminal(control_socket) |
|
|
|
|
require "pty" |
|
|
|
|
|
|
|
|
|
master, client = PTY.open |
|
|
|
|
master.raw! |
|
|
|
|
def set_up_pseudoterminal(control_socket, pseudoterminal_path) |
|
|
|
|
pseudoterminal_io = File::open(pseudoterminal_path, File::RDWR | File::NOCTTY) |
|
|
|
|
|
|
|
|
|
File.chmod 0600, client.path |
|
|
|
|
$original_stderr.puts " connecting to pseudoterminal #{pseudoterminal_path}" |
|
|
|
|
|
|
|
|
|
client_path = Base64.encode64(client.path).delete("\n") |
|
|
|
|
client.close |
|
|
|
|
$stdin.reopen(pseudoterminal_io) |
|
|
|
|
$stdout.reopen(pseudoterminal_io) |
|
|
|
|
$stderr.reopen(pseudoterminal_io) |
|
|
|
|
|
|
|
|
|
control_socket.puts "pseudoterminal #{client_path}" |
|
|
|
|
|
|
|
|
|
$stderr.puts " set up pseudoterminal" |
|
|
|
|
$original_stderr.puts " connected to pseudoterminal #{pseudoterminal_path}" |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
while true |
|
|
|
|
control_socket = control_server.accept |
|
|
|
|
$stderr.puts "- new connection" |
|
|
|
|
$original_stderr.puts "- new connection" |
|
|
|
|
|
|
|
|
|
begin |
|
|
|
|
command, arguments = read_command(control_socket) |
|
|
|
@ -157,7 +146,7 @@ while true
|
|
|
|
|
raise ClientError.new "unexpected command" |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
if arguments.length != 3 |
|
|
|
|
if arguments.empty? |
|
|
|
|
raise ClientError.new "malformed command" |
|
|
|
|
end |
|
|
|
|
|
|
|
|
@ -169,59 +158,65 @@ while true
|
|
|
|
|
|
|
|
|
|
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})" |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
script_path = Base64.decode64(arguments[2]) |
|
|
|
|
script_path = Base64.decode64(arguments.last) |
|
|
|
|
|
|
|
|
|
connection_id += 1 |
|
|
|
|
|
|
|
|
|
child_process = fork { |
|
|
|
|
original_stdin = $stdin.dup |
|
|
|
|
original_stdout = $stdout.dup |
|
|
|
|
original_stderr = $stderr.dup |
|
|
|
|
|
|
|
|
|
exit_code = "unknown" |
|
|
|
|
|
|
|
|
|
if mode == "named-pipes" |
|
|
|
|
set_up_named_pipes(control_socket, connection_id) |
|
|
|
|
else |
|
|
|
|
set_up_pseudoterminal(control_socket) |
|
|
|
|
set_up_pseudoterminal(control_socket, pseudoterminal_path) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
original_stderr.puts " executing script " + script_path |
|
|
|
|
$original_stderr.puts " executing script " + script_path |
|
|
|
|
|
|
|
|
|
begin |
|
|
|
|
begin |
|
|
|
|
load script_path, true |
|
|
|
|
rescue SystemExit => error |
|
|
|
|
original_stderr.puts " exit code: #{error.status}" |
|
|
|
|
$original_stderr.puts " exit code: #{error.status}" |
|
|
|
|
exit_code = error.status |
|
|
|
|
rescue StandardError => error |
|
|
|
|
$stdin = original_stdin |
|
|
|
|
$stdout = original_stdout |
|
|
|
|
$stderr = original_stderr |
|
|
|
|
$stdin = $original_stdin |
|
|
|
|
$stdout = $original_stdout |
|
|
|
|
$stderr = $original_stderr |
|
|
|
|
|
|
|
|
|
raise ClientScriptError.new error |
|
|
|
|
end |
|
|
|
|
rescue ClientScriptError => error |
|
|
|
|
# TODO: Restore pipes to make sure that syntax errors are caught |
|
|
|
|
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 |
|
|
|
|
begin |
|
|
|
|
control_socket.puts "script_error #{encoded_error_output}" |
|
|
|
|
rescue |
|
|
|
|
end |
|
|
|
|
rescue ClientError => error |
|
|
|
|
original_stderr.puts " error communicating with client, ignoring request (#{error})" |
|
|
|
|
$original_stderr.puts " error communicating with client, ignoring request (#{error})" |
|
|
|
|
begin |
|
|
|
|
control_socket.puts "error #{error}" |
|
|
|
|
rescue |
|
|
|
|
end |
|
|
|
|
rescue StandardError => error |
|
|
|
|
original_stderr.puts " error, ignoring request (#{error})" |
|
|
|
|
$original_stderr.puts " error, ignoring request (#{error})" |
|
|
|
|
begin |
|
|
|
|
control_socket.puts "error internal server error" |
|
|
|
|
rescue |
|
|
|
@ -229,25 +224,26 @@ while true
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
begin |
|
|
|
|
# TODO: which exit code to return when interrupted? |
|
|
|
|
control_socket.puts "done #{exit_code}" |
|
|
|
|
rescue |
|
|
|
|
end |
|
|
|
|
control_socket.close |
|
|
|
|
|
|
|
|
|
original_stderr.puts " finished handling request" |
|
|
|
|
$original_stderr.puts " finished handling request" |
|
|
|
|
|
|
|
|
|
Kernel.exit! |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Process.detach(child_process) |
|
|
|
|
rescue ClientError => error |
|
|
|
|
$stderr.puts " error communicating with client, ignoring request (#{error})" |
|
|
|
|
$original_stderr.puts " error communicating with client, ignoring request (#{error})" |
|
|
|
|
begin |
|
|
|
|
control_socket.puts "error #{error}" |
|
|
|
|
rescue |
|
|
|
|
end |
|
|
|
|
rescue StandardError => error |
|
|
|
|
$stderr.puts " error, ignoring request (#{error})" |
|
|
|
|
$original_stderr.puts " error, ignoring request (#{error})" |
|
|
|
|
begin |
|
|
|
|
control_socket.puts "error internal server error" |
|
|
|
|
rescue |
|
|
|
|