fix: server shutdown on SIGTERM — use sigaction without SA_RESTART
CI / lint (pull_request) Successful in 7s
CI / sanitizers (address) (pull_request) Successful in 14s
CI / sanitizers (undefined) (pull_request) Successful in 14s
CI / fuzz-build (pull_request) Successful in 12s
CI / coverage (pull_request) Successful in 9s
CI / build-and-test (pull_request) Failing after 58s
CI / valgrind (pull_request) Successful in 11s

The previous code used signal() to register the cleanup handler, but on glibc
(and many other modern libc implementations), signal() implicitly sets
SA_RESTART, which causes blocking syscalls like accept() to be transparently
restarted after the handler runs. As a result, accept() never returns EINTR,
the accept loop never breaks, and the server never exits on SIGTERM/SIGINT.

Fix by using sigaction() with sa_flags = 0 (no SA_RESTART), guaranteeing that
accept() is interrupted and returns EINTR, allowing the loop to exit and the
server to shut down cleanly.
This commit is contained in:
2026-07-20 20:27:59 +02:00
parent ef267c94e2
commit d9ab4775fb
+6 -2
View File
@@ -180,8 +180,12 @@ int main(int argc, char* argv[]) {
log_message(LOG_LEVEL_WARNING, "--ca has no effect without --tls"); log_message(LOG_LEVEL_WARNING, "--ca has no effect without --tls");
} }
signal(SIGINT, cleanup); struct sigaction sa;
signal(SIGTERM, cleanup); sigemptyset(&sa.sa_mask);
sa.sa_handler = cleanup;
sa.sa_flags = 0; /* Do NOT set SA_RESTART — we need accept() to return EINTR */
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
g_server = server_create(port); g_server = server_create(port);
if (g_server == NULL) { if (g_server == NULL) {
log_message(LOG_LEVEL_ERROR, "Failed to create server"); log_message(LOG_LEVEL_ERROR, "Failed to create server");