From c81da3820b389c9995c55ae52570a1e913ae17c4 Mon Sep 17 00:00:00 2001 From: Leroy Hopson Date: Thu, 22 Jul 2021 12:17:23 +0700 Subject: [PATCH] Kill child process and close pty on exit - Adds kill() method to LibuvUtils. - Adds close() method to Pipe. --- .../editor_plugins/terminal/editor_terminal.gd | 1 - addons/godot_xterm/native/src/libuv_utils.cpp | 6 ++++++ addons/godot_xterm/native/src/libuv_utils.h | 6 +++++- addons/godot_xterm/native/src/pipe.cpp | 11 +++++++++-- addons/godot_xterm/native/src/pipe.h | 1 + addons/godot_xterm/nodes/pty/libuv_utils.gd | 4 ++++ addons/godot_xterm/nodes/pty/pty.gd | 7 +++++-- addons/godot_xterm/nodes/pty/unix/pty_unix.gd | 13 +++++++++++-- examples/menu/menu.tscn | 1 - test/platform/unix/unix.test.gd | 12 ++++++++++++ 10 files changed, 53 insertions(+), 9 deletions(-) diff --git a/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd b/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd index 2dd7bb1..fac0d3e 100644 --- a/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd +++ b/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd @@ -88,5 +88,4 @@ func _input(event): func _on_PTY_exited(exit_code: int, signum: int): - print("exit_code: ", exit_code, " signum: ", signum) emit_signal("exited", exit_code, signum) diff --git a/addons/godot_xterm/native/src/libuv_utils.cpp b/addons/godot_xterm/native/src/libuv_utils.cpp index 3748595..656978c 100644 --- a/addons/godot_xterm/native/src/libuv_utils.cpp +++ b/addons/godot_xterm/native/src/libuv_utils.cpp @@ -9,6 +9,8 @@ void LibuvUtils::_register_methods() { register_method("get_os_environ", &LibuvUtils::get_os_environ); register_method("get_os_release", &LibuvUtils::get_os_release); register_method("get_cwd", &LibuvUtils::get_cwd); + + register_method("kill", &LibuvUtils::kill); } LibuvUtils::LibuvUtils() {} @@ -61,6 +63,10 @@ String LibuvUtils::get_cwd() { return result; } +godot_error LibuvUtils::kill(int pid, int signum) { + RETURN_UV_ERR(uv_kill(pid, signum)); +} + godot_error LibuvUtils::translate_uv_errno(int uv_err) { if (uv_err >= 0) return GODOT_OK; diff --git a/addons/godot_xterm/native/src/libuv_utils.h b/addons/godot_xterm/native/src/libuv_utils.h index 4f6eb9b..37f91f5 100644 --- a/addons/godot_xterm/native/src/libuv_utils.h +++ b/addons/godot_xterm/native/src/libuv_utils.h @@ -9,7 +9,9 @@ String(uv_strerror(uv_err))); #define RETURN_UV_ERR(uv_err) \ - UV_ERR_PRINT(uv_err); \ + if (uv_err < 0) { \ + UV_ERR_PRINT(uv_err); \ + } \ return LibuvUtils::translate_uv_errno(uv_err); #define RETURN_IF_UV_ERR(uv_err) \ @@ -34,6 +36,8 @@ public: String get_os_release(); String get_cwd(); + godot_error kill(int pid, int signum); + public: static godot_error translate_uv_errno(int uv_err); }; diff --git a/addons/godot_xterm/native/src/pipe.cpp b/addons/godot_xterm/native/src/pipe.cpp index 361b195..89e36b4 100644 --- a/addons/godot_xterm/native/src/pipe.cpp +++ b/addons/godot_xterm/native/src/pipe.cpp @@ -31,7 +31,7 @@ void Pipe::_register_methods() { } Pipe::Pipe() {} -Pipe::~Pipe() {} +Pipe::~Pipe() { close(); } void Pipe::_init() {} @@ -39,6 +39,8 @@ void _poll_connection(); void _read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf); +void _close_cb(uv_handle_t *handle); + void _write_cb(uv_write_t *req, int status); void _alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); @@ -56,6 +58,11 @@ godot_error Pipe::open(int fd, bool ipc = false) { return GODOT_OK; } +void Pipe::close() { + uv_close((uv_handle_t *)&handle, NULL); + uv_run(uv_default_loop(), UV_RUN_NOWAIT); +} + godot_error Pipe::write(String p_data) { char *s = p_data.alloc_c_string(); ULONG len = strlen(s); @@ -88,10 +95,10 @@ void _read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) { switch (nread) { case UV_EOF: // Normal after shell exits. - return; case UV_EIO: // Can happen when the process exits. // As long as PTY has caught it, we should be fine. + uv_read_stop(handle); return; default: UV_ERR_PRINT(nread); diff --git a/addons/godot_xterm/native/src/pipe.h b/addons/godot_xterm/native/src/pipe.h index e893361..0b7c09a 100644 --- a/addons/godot_xterm/native/src/pipe.h +++ b/addons/godot_xterm/native/src/pipe.h @@ -23,6 +23,7 @@ public: void _init(); godot_error open(int fd, bool ipc); + void close(); int get_status(); godot_error write(String p_data); diff --git a/addons/godot_xterm/nodes/pty/libuv_utils.gd b/addons/godot_xterm/nodes/pty/libuv_utils.gd index 0ab58fd..d43e465 100644 --- a/addons/godot_xterm/nodes/pty/libuv_utils.gd +++ b/addons/godot_xterm/nodes/pty/libuv_utils.gd @@ -25,5 +25,9 @@ static func get_windows_build_number() -> int: assert(false, "Not implemented.") return 0 +static func kill(pid: int, signum: int): + if pid > 1: + return LibuvUtils.new().kill(pid, signum) + static func new(): assert(false, "Abstract sealed (i.e. static) class should not be instantiated.") diff --git a/addons/godot_xterm/nodes/pty/pty.gd b/addons/godot_xterm/nodes/pty/pty.gd index 8893686..e3f414e 100644 --- a/addons/godot_xterm/nodes/pty/pty.gd +++ b/addons/godot_xterm/nodes/pty/pty.gd @@ -212,8 +212,11 @@ func destroy() -> void: # Kill the pty. # sigint: The signal to send. By default this is SIGHUP. # This is not supported on Windows. -func kill(sigint: int = Signal.SIGHUP) -> void: - pass +func kill(signum: int = Signal.SIGHUP) -> void: + if _pipe: + _pipe.close() + if pid > 0: + LibuvUtils.kill(pid, signum) func fork( diff --git a/addons/godot_xterm/nodes/pty/unix/pty_unix.gd b/addons/godot_xterm/nodes/pty/unix/pty_unix.gd index 3f0fdf5..f232171 100644 --- a/addons/godot_xterm/nodes/pty/unix/pty_unix.gd +++ b/addons/godot_xterm/nodes/pty/unix/pty_unix.gd @@ -42,7 +42,6 @@ func _resize(cols: int, rows: int) -> void: func _fork_thread(args): var result = preload("./pty_unix.gdns").new().callv("fork", args) - print(result) return result @@ -88,6 +87,8 @@ func fork( status = STATUS_ERROR return FAILED + pid = result[1].pid + _pipe = Pipe.new() _pipe.open(_fd) @@ -101,12 +102,20 @@ func open(cols: int = DEFAULT_COLS, rows: int = DEFAULT_ROWS) -> Array: return PTYUnix.new().open(cols, rows) +func _exit_tree(): + _exit_cb = null + if pid > 1: + LibuvUtils.kill(pid, Signal.SIGHUP) + + func _on_pipe_data_received(data): emit_signal("data_received", data) func _on_exit(exit_code: int, signum: int) -> void: - emit_signal("exited", exit_code, signum) + if is_instance_valid(self): + pid = -1 + emit_signal("exited", exit_code, signum) func _sanitize_env(env: Dictionary) -> Dictionary: diff --git a/examples/menu/menu.tscn b/examples/menu/menu.tscn index 771deff..afcad5c 100644 --- a/examples/menu/menu.tscn +++ b/examples/menu/menu.tscn @@ -20,5 +20,4 @@ script = ExtResource( 1 ) __meta__ = { "_edit_use_anchors_": false } - [connection signal="key_pressed" from="Terminal" to="." method="_on_Terminal_key_pressed"] diff --git a/test/platform/unix/unix.test.gd b/test/platform/unix/unix.test.gd index 6bdf094..9939dea 100644 --- a/test/platform/unix/unix.test.gd +++ b/test/platform/unix/unix.test.gd @@ -1,5 +1,7 @@ extends "res://addons/gut/test.gd" +const LibuvUtils := preload("res://addons/godot_xterm/nodes/pty/libuv_utils.gd") + var pty: GDXterm.PTYUnix var helper: Helper @@ -51,6 +53,16 @@ func test_open_pty_has_correct_win_size(): assert_eq(winsize.rows, rows) +func test_closes_pty_on_exit(): + var num_pts = helper._get_pts().size() + pty.fork("sleep", ["1000"]) + remove_child(pty) + pty.free() + yield(yield_for(1), YIELD) + var new_num_pts = helper._get_pts().size() + assert_eq(new_num_pts, num_pts) + + class Helper: static func _get_pts() -> Array: assert(false, "Abstract method")