testsuite: catch i3 SIGSEGV by installing SIGCHLD handler (#5123)

The testsuite already contains quite a number of SIGCHLD handler
installation/un-installations. Here is my attempt at an inventary.

1. complete-run.pl installs a SIGCHLD handler in the `start_xserver()` function
   call, which prints an error and exits when all x server processes have exited.

2. In the TestWorker child process, a SIGCHLD handler is installed to reap dead
   test child processes.

3. The TestWorker child process forks another child process for running the test
   file, where the previously installed SIGCHLD handler (point 2) is unset.

   This is where this commit comes in: it installs a SIGCHLD handler in the test
   file child process, which will trigger when the i3 subprocess dies.

4. (For completeness: i3test.pm defines an END block where it unsets the
    previous SIGCHLD handler before it kills the subprocesses.)

With this commit, when i3 segfaults, the output will look like this:

    Writing logfile to 'testsuite-2022-09-10-21-14-46-4.20-103-gb242bceb/complete-run.log'...
    [:100] /home/michael/i3/testcases/t/167-workspace_layout.t: BAILOUT
    completed 0 of 1 tests
    test /home/michael/i3/testcases/t/167-workspace_layout.t bailed out:
    could not kill i3: No such process

fixes https://github.com/i3/i3/issues/4437
This commit is contained in:
Michael Stapelberg 2022-09-10 21:29:04 +02:00 committed by GitHub
parent 4b5ead023e
commit f795c2c8da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -798,6 +798,8 @@ sub exit_gracefully {
my ($pid, $socketpath) = @_; my ($pid, $socketpath) = @_;
$socketpath ||= get_socket_path(); $socketpath ||= get_socket_path();
$SIG{CHLD} = undef;
my $exited = 0; my $exited = 0;
eval { eval {
say "Exiting i3 cleanly..."; say "Exiting i3 cleanly...";
@ -836,6 +838,8 @@ sub exit_forcefully {
my ($pid, $signal) = @_; my ($pid, $signal) = @_;
$signal ||= 'TERM'; $signal ||= 'TERM';
$SIG{CHLD} = undef;
# Send the given signal to the i3 instance and wait for up to 10s # Send the given signal to the i3 instance and wait for up to 10s
# for it to terminate. # for it to terminate.
kill($signal, $pid) kill($signal, $pid)
@ -959,6 +963,18 @@ sub launch_with_config {
return ${^CHILD_ERROR_NATIVE}; return ${^CHILD_ERROR_NATIVE};
} }
$SIG{CHLD} = sub {
# don't change $! and $? outside handler
local ($!, $?);
my $child = waitpid -1, POSIX::WNOHANG;
warn "SIGCHLD, waitpid() = $child";
if ($child == $i3_pid) {
warn "i3 died, exiting!";
exit 1;
}
};
# force update of the cached socket path in lib/i3test # force update of the cached socket path in lib/i3test
# as soon as i3 has started # as soon as i3 has started
$cv->cb(sub { get_socket_path(0) }); $cv->cb(sub { get_socket_path(0) });