aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgbc dev <gbc@localhost>2026-09-22 14:55:33 +0200
committergbc dev <gbc@localhost>2026-09-22 14:55:33 +0200
commitacac856dcdac42b441d39708b1877ba532492ffd (patch)
treedaae0b71450347ad583bb129ddbc965fabaaa0db
parentdocs: document the SL0PBOY boot-logo patcher (tools/mk_sl0pboy_bios.py) (diff)
downloadsl0pboy-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.c25
1 files changed, 20 insertions, 5 deletions
diff --git a/src/main.c b/src/main.c
index 811e0a4..978255c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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 "