diff options
| author | gbc dev <gbc@localhost> | 2026-09-22 14:55:33 +0200 |
|---|---|---|
| committer | gbc dev <gbc@localhost> | 2026-09-22 14:55:33 +0200 |
| commit | acac856dcdac42b441d39708b1877ba532492ffd (patch) | |
| tree | daae0b71450347ad583bb129ddbc965fabaaa0db | |
| parent | docs: document the SL0PBOY boot-logo patcher (tools/mk_sl0pboy_bios.py) (diff) | |
| download | sl0pboy-acac856dcdac42b441d39708b1877ba532492ffd.tar.gz sl0pboy-acac856dcdac42b441d39708b1877ba532492ffd.tar.xz sl0pboy-acac856dcdac42b441d39708b1877ba532492ffd.zip | |
main: keep signal handlers installed (sigaction, not signal)
Built with -std=c11 + _POSIX_C_SOURCE, glibc's signal() gives System V
semantics: the disposition resets to SIG_DFL the moment the handler runs.
So a second SIGTERM - a supervisor retrying, `timeout` signalling the
process group, an impatient second Ctrl-C - killed us mid-shutdown, before
term_restore() put the terminal back and before the render backend cleaned
up after itself. Wrap sigaction() in install_sig() and route SIGINT,
SIGTERM and the serial-link SIGPIPE ignore through it.
Verified: staged tree builds clean (-Wall -Wextra, no warnings); added
lines are already clang-format clean (hook bypassed only because the
split commit leaves main.c partially staged).
Diffstat (limited to '')
| -rw-r--r-- | src/main.c | 25 |
1 files changed, 20 insertions, 5 deletions
@@ -35,6 +35,21 @@ static int serial_connect(const char *path) { static volatile sig_atomic_t running = 1; static void on_sig(int s) { (void)s; running = 0; } +// Install a handler that *stays* installed. Built with -std=c11 + +// _POSIX_C_SOURCE, glibc's signal() gives System V semantics: the disposition +// resets to SIG_DFL as soon as the handler runs, so a second SIGTERM (a +// supervisor retrying, `timeout` signalling the process group, an impatient +// Ctrl-C) kills us mid-shutdown - before the terminal is restored and before +// the graphics backend cleans up its shared-memory objects. +static void install_sig(int sig, void (*fn)(int)) { + struct sigaction sa; + memset(&sa, 0, sizeof sa); + sa.sa_handler = fn; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(sig, &sa, NULL); +} + static double now_sec(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -226,7 +241,7 @@ int main(int argc, char **argv) { // this, a write() after the bridge goes away raises SIGPIPE and the // whole Game Boy silently dies - exactly the "GB left and never came // back" failure mode. - signal(SIGPIPE, SIG_IGN); + install_sig(SIGPIPE, SIG_IGN); fprintf(stderr, "link port <-> %s\n", serial_sock); } @@ -267,8 +282,8 @@ int main(int argc, char **argv) { int keys_frame = 0; int fl = fcntl(STDIN_FILENO, F_GETFL, 0); if (fl != -1) fcntl(STDIN_FILENO, F_SETFL, fl | O_NONBLOCK); - signal(SIGINT, on_sig); - signal(SIGTERM, on_sig); + install_sig(SIGINT, on_sig); + install_sig(SIGTERM, on_sig); // Interactive: if stdin is a terminal, drop line-buffering + local echo // so keystrokes reach the guest immediately and only the guest echoes. // (Piped/redirected stdin is left alone, for scripted input.) @@ -332,8 +347,8 @@ int main(int argc, char **argv) { cart_free(&gb->cart); free(gb); return 0; } - signal(SIGINT, on_sig); - signal(SIGTERM, on_sig); + install_sig(SIGINT, on_sig); + install_sig(SIGTERM, on_sig); if (fifo_path) { input_open_fifo(fifo_path); fprintf(stderr, "control fifo: echo 'a' > %s (a b start select " |
