diff --git a/CLAUDE.md b/CLAUDE.md index 9d2358c..3e2809e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,7 +38,12 @@ Source layout (all under `src/`): - `process.{h,cpp}` — argv-based subprocess helpers (fork/execvp, no shell): `run_process()` captures stdout (used for `containers-storage` calls), `run_process_foreground()` inherits all of stdio (used for the interactive `bwrap` - run). Also `find_in_path()`, a shared `$PATH` lookup. + run). Also `find_in_path()`, a shared `$PATH` lookup. `run_process_foreground()` + installs a SIGINT/SIGTERM handler around its `waitpid()` that forwards the signal + to the running child and keeps waiting instead of letting the default disposition + kill `slocker_lite` itself — without this, Ctrl-C (or `kill`) during `-r`'s `bwrap` + run would skip `run_container()`'s unmount/cleanup entirely, leaving the layer + imported and/or mounted. Errors are logged via `spdlog::error`; every external command is also traced at debug level in `run_process()`/`run_process_foreground()` (`src/process.cpp`) — visible via diff --git a/src/process.cpp b/src/process.cpp index 9cbf29a..3146ef0 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include @@ -27,6 +29,21 @@ namespace { +// Set by run_process_foreground() before it blocks in waitpid(), so the signal +// handler below has a pid to forward SIGINT/SIGTERM to (a handler can't take extra +// arguments, and this is the conventional way to pass it state). +volatile sig_atomic_t g_foreground_child_pid = 0; + +// Forwards the signal to the running foreground child instead of letting the +// default disposition kill this process outright -- without this, Ctrl-C or a +// `kill` during `-r`'s bwrap run skips run_container()'s unmount/cleanup entirely, +// leaving the layer imported and/or mounted. kill() is async-signal-safe. +void forward_signal_to_foreground_child(int sig) { + if (g_foreground_child_pid > 0) { + kill(g_foreground_child_pid, sig); + } +} + std::vector to_c_argv(const std::vector& argv) { std::vector c_argv; c_argv.reserve(argv.size() + 1); @@ -107,8 +124,25 @@ int run_process_foreground(const std::vector& argv) { _exit(127); } + g_foreground_child_pid = pid; + + struct sigaction action = {}; + action.sa_handler = forward_signal_to_foreground_child; + sigemptyset(&action.sa_mask); + struct sigaction old_sigint; + struct sigaction old_sigterm; + sigaction(SIGINT, &action, &old_sigint); + sigaction(SIGTERM, &action, &old_sigterm); + int status = 0; - waitpid(pid, &status, 0); + pid_t wait_result; + do { + wait_result = waitpid(pid, &status, 0); + } while (wait_result == -1 && errno == EINTR); + + sigaction(SIGINT, &old_sigint, nullptr); + sigaction(SIGTERM, &old_sigterm, nullptr); + g_foreground_child_pid = 0; int exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : -1; if (exit_code != 0) {