diff --git a/kitty/prewarm.py b/kitty/prewarm.py index 5c0ef51c9..cebb87d39 100644 --- a/kitty/prewarm.py +++ b/kitty/prewarm.py @@ -8,6 +8,7 @@ import os import select import signal import socket +import struct import sys import time import warnings @@ -525,15 +526,8 @@ class SocketChild: def handle_death(self, status: int) -> None: if self.closed: return - if hasattr(os, 'waitstatus_to_exitcode'): - status = os.waitstatus_to_exitcode(status) - # negative numbers are signals usually and shells report these as - # 128 + signal number, so do the same. There is no API to exit a - # process with full 32bit status information. - if -128 < status < 0: - status = 128 - status try: - self.conn.sendall(f'{status}'.encode('ascii')) + self.conn.sendall(struct.pack('q', status)) except OSError as e: print_error(f'Failed to send exit status of socket child with error: {e}') @@ -541,7 +535,7 @@ class SocketChild: if self.closed: return False try: - self.conn.sendall(f'{self.pid}:'.encode('ascii')) + self.conn.sendall(struct.pack('q', self.pid)) except OSError as e: print_error(f'Failed to send pid of socket child with error: {e}') return False diff --git a/kitty_tests/prewarm.py b/kitty_tests/prewarm.py index 57d533aa9..64ae061ac 100644 --- a/kitty_tests/prewarm.py +++ b/kitty_tests/prewarm.py @@ -69,8 +69,12 @@ def socket_child_main(exit_code=0, initial_print=''): if status is None: os.kill(pty.child_pid, signal.SIGKILL) self.assertIsNotNone(status, f'prewarm wrapper process did not exit. Screen contents: {pty.screen_contents()}') - with suppress(AttributeError): - self.assertEqual(os.waitstatus_to_exitcode(status), exit_code, pty.screen_contents()) + if isinstance(exit_code, signal.Signals): + self.assertTrue(os.WIFSIGNALED(status), 'prewarm wrapper did not die with a signal') + self.assertEqual(os.WTERMSIG(status), exit_code.value) + else: + with suppress(AttributeError): + self.assertEqual(os.waitstatus_to_exitcode(status), exit_code, pty.screen_contents()) # test SIGINT via signal to wrapper, unfortunately as best as I can # tell SIGINT is delivered reliably to the wrapper process @@ -89,7 +93,7 @@ def socket_child_main(exit_code=0, initial_print=''): pty.wait_till(lambda: f'Screen size changed: {cols + 3}' in pty.screen_contents()) pty.write_to_child('\x03') pty.wait_till(lambda: 'KeyboardInterrupt' in pty.screen_contents()) - wait_for_death(128 + signal.SIGINT) + wait_for_death(signal.SIGINT) # test passing of data via cwd, env vars and stdin/stdout redirection stdin_r, stdin_w = os.pipe() diff --git a/prewarm-launcher.c b/prewarm-launcher.c index 3fd839b89..8e7325025 100644 --- a/prewarm-launcher.c +++ b/prewarm-launcher.c @@ -83,14 +83,6 @@ parse_long(const char *str, long *val) { return rc; } -static bool -parse_int(const char *str, int *val) { - long lval = 0; - if (!parse_long(str, &lval)) return false; - *val = lval; - return true; -} - static inline int safe_open(const char *path, int flags, mode_t mode) { while (true) { @@ -355,26 +347,20 @@ static enum ChildState child_state = CHILD_NOT_STARTED; static bool read_child_data(void) { ssize_t n; - if (from_child_buf_pos >= sizeof(from_child_buf) - 2) { print_error("Too much data from prewarm socket", 0); return false; } - n = safe_read(socket_fd, from_child_buf, sizeof(from_child_buf) - 2 - from_child_buf_pos); + if (from_child_buf_pos >= sizeof(from_child_buf)) { print_error("Too much data from prewarm socket", 0); return false; } + n = safe_read(socket_fd, from_child_buf + from_child_buf_pos, sizeof(from_child_buf) - from_child_buf_pos); if (n < 0) { if (errno == EIO || errno == EPIPE) { socket_fd = -1; return true; } return false; } if (n) { from_child_buf_pos += n; - char *p = memchr(from_child_buf, ':', from_child_buf_pos); - if (p && child_pid == 0) { - *p = 0; - long cp = 0; - if (!parse_long(from_child_buf, &cp)) { print_error("Could not parse child pid from prewarm socket", 0); return false; } + if (from_child_buf_pos >= sizeof(long long)) { + pid_t cp = *((long long*)from_child_buf); if (cp == 0) { print_error("Got zero child pid from prewarm socket", 0); return false; } child_pid = cp; child_state = CHILD_STARTED; if (child_slave_fd > -1) { safe_close(child_slave_fd); child_slave_fd = -1; } - memset(from_child_buf, 0, (p - from_child_buf) + 1); - from_child_buf_pos -= (p - from_child_buf) + 1; - if (from_child_buf_pos) memmove(from_child_buf, p + 1, from_child_buf_pos); for (size_t i = 0; i < arraysz(pending_signals) && pending_signals[i]; i++) { kill(child_pid, pending_signals[i]); } @@ -612,7 +598,18 @@ loop(void) { if (FD_ISSET(socket_fd, &readable)) { if (!read_child_data()) fail("reading information about child failed"); if (socket_fd < 0) { // hangup - if (from_child_buf[0]) { parse_int(from_child_buf, &exit_status); } + if (from_child_buf_pos >= 2 * sizeof(long long)) { + int child_exit_status = *((long long*)(from_child_buf + sizeof(long long))); + if (WIFEXITED(child_exit_status)) { + exit_status = WEXITSTATUS(child_exit_status); + } else if (WIFSIGNALED(child_exit_status)) { + int signum = WTERMSIG(child_exit_status); + if (signum > 0) { + signal(signum, SIG_DFL); + kill(getpid(), signum); + } + } + } child_pid = 0; child_state = CHILD_EXITED; }