wrapper process should exit with signal if prewarmed worker exits with signal

This commit is contained in:
Kovid Goyal 2022-07-10 14:10:22 +05:30
parent a3f1a44d83
commit 624e96df9b
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 26 additions and 31 deletions

View File

@ -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

View File

@ -69,6 +69,10 @@ 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()}')
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())
@ -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()

View File

@ -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;
}