* [PATCH 0/5] unshare: fix signal forwarding to child processes
@ 2026-01-08 18:31 Kiran Rangoon
2026-01-08 18:31 ` [PATCH 1/5] unshare: add global child_pid variable for signal forwarding Kiran Rangoon
` (5 more replies)
0 siblings, 6 replies; 18+ messages in thread
From: Kiran Rangoon @ 2026-01-08 18:31 UTC (permalink / raw)
To: util-linux; +Cc: Kiran Rangoon
This series fixes a bug where unshare with --fork was not properly
forwarding SIGTERM and SIGINT to child processes, causing premature
termination during system shutdown.
The problem occurred because the parent process was blocking signals
instead of handling and forwarding them. When systemd sent SIGTERM
during shutdown, the parent would receive it but the child would
continue running until the kernel forcibly killed it. This resulted
in scripts being unable to handle SIGTERM traps properly and perform
cleanup operations.
The fix replaces signal blocking with signal handlers that forward
SIGTERM/SIGINT to the child process, allowing proper signal handling
and graceful shutdown. The waitpid loop is also updated to handle
EINTR interruptions from the signal forwarding mechanism.
Kiran Rangoon (5):
unshare: add global child_pid variable for signal forwarding
unshare: add signal forwarding handler
unshare: replace signal blocking with signal handlers
unshare: store child PID in global variable
unshare: handle EINTR in waitpid loop
sys-utils/unshare.c | 47 ++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 42 insertions(+), 5 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH 1/5] unshare: add global child_pid variable for signal forwarding 2026-01-08 18:31 [PATCH 0/5] unshare: fix signal forwarding to child processes Kiran Rangoon @ 2026-01-08 18:31 ` Kiran Rangoon 2026-01-08 18:31 ` [PATCH 2/5] unshare: add signal forwarding handler Kiran Rangoon ` (4 subsequent siblings) 5 siblings, 0 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-08 18:31 UTC (permalink / raw) To: util-linux; +Cc: Kiran Rangoon Add a global variable to store the child process PID, which will be used by the signal handler to forward SIGTERM/SIGINT to the child. Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com> --- sys-utils/unshare.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 5370ab981..6df53666a 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -74,6 +74,9 @@ static struct namespace_file { static int npersists; /* number of persistent namespaces */ +/* Global PID of child process for signal forwarding */ +static volatile pid_t child_pid = 0; + enum { SETGROUPS_NONE = -1, SETGROUPS_DENY = 0, -- 2.47.3 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 2/5] unshare: add signal forwarding handler 2026-01-08 18:31 [PATCH 0/5] unshare: fix signal forwarding to child processes Kiran Rangoon 2026-01-08 18:31 ` [PATCH 1/5] unshare: add global child_pid variable for signal forwarding Kiran Rangoon @ 2026-01-08 18:31 ` Kiran Rangoon 2026-01-08 18:31 ` [PATCH 3/5] unshare: replace signal blocking with signal handlers Kiran Rangoon ` (3 subsequent siblings) 5 siblings, 0 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-08 18:31 UTC (permalink / raw) To: util-linux; +Cc: Kiran Rangoon Add forward_signal() function that forwards SIGTERM/SIGINT from the parent process to the child process. This will be installed as a signal handler in the next step. Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com> --- sys-utils/unshare.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 6df53666a..3850e5f4a 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -122,6 +122,19 @@ static void setgroups_control(int action) close(fd); } +/** + * forward_signal() - Forward signal to child process + * @sig: Signal number to forward + * + * Signal handler that forwards SIGTERM/SIGINT from parent to child. + * This allows the child to handle signals properly when using --fork. + */ +static void forward_signal(int sig) +{ + if (child_pid > 0) + kill(child_pid, sig); +} + static void map_id(const char *file, uint32_t from, uint32_t to) { char *buf; -- 2.47.3 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 3/5] unshare: replace signal blocking with signal handlers 2026-01-08 18:31 [PATCH 0/5] unshare: fix signal forwarding to child processes Kiran Rangoon 2026-01-08 18:31 ` [PATCH 1/5] unshare: add global child_pid variable for signal forwarding Kiran Rangoon 2026-01-08 18:31 ` [PATCH 2/5] unshare: add signal forwarding handler Kiran Rangoon @ 2026-01-08 18:31 ` Kiran Rangoon 2026-01-08 18:31 ` [PATCH 4/5] unshare: store child PID in global variable Kiran Rangoon ` (2 subsequent siblings) 5 siblings, 0 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-08 18:31 UTC (permalink / raw) To: util-linux; +Cc: Kiran Rangoon Replace sigprocmask(SIG_BLOCK) with sigaction() to install signal handlers for SIGTERM and SIGINT. This allows the parent to catch signals and forward them to the child, instead of just blocking them. The forward_signal() handler installed in the previous commit will now be called when SIGTERM/SIGINT is received. Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com> --- sys-utils/unshare.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 3850e5f4a..9255ff4f8 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -1116,11 +1116,23 @@ int main(int argc, char *argv[]) settime(monotonic, CLOCK_MONOTONIC); if (forkit) { + struct sigaction sa; + + /* Set up signal handler to forward signals to child */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = forward_signal; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction(SIGTERM, &sa, NULL) == -1) + err(EXIT_FAILURE, _("sigaction SIGTERM failed")); + if (sigaction(SIGINT, &sa, NULL) == -1) + err(EXIT_FAILURE, _("sigaction SIGINT failed")); + + /* Save old mask for child to restore */ if (sigemptyset(&sigset) != 0 || - sigaddset(&sigset, SIGINT) != 0 || - sigaddset(&sigset, SIGTERM) != 0 || - sigprocmask(SIG_BLOCK, &sigset, &oldsigset) != 0) - err(EXIT_FAILURE, _("sigprocmask block failed")); + sigprocmask(SIG_SETMASK, NULL, &oldsigset) != 0) + err(EXIT_FAILURE, _("sigprocmask failed")); #ifdef HAVE_PIDFD_OPEN if (kill_child_signo != 0) { /* make a connection to the original process (parent) */ -- 2.47.3 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 4/5] unshare: store child PID in global variable 2026-01-08 18:31 [PATCH 0/5] unshare: fix signal forwarding to child processes Kiran Rangoon ` (2 preceding siblings ...) 2026-01-08 18:31 ` [PATCH 3/5] unshare: replace signal blocking with signal handlers Kiran Rangoon @ 2026-01-08 18:31 ` Kiran Rangoon 2026-01-08 18:31 ` [PATCH 5/5] unshare: handle EINTR in waitpid loop Kiran Rangoon 2026-01-12 14:05 ` [PATCH 0/5] unshare: fix signal forwarding to child processes Karel Zak 5 siblings, 0 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-08 18:31 UTC (permalink / raw) To: util-linux; +Cc: Kiran Rangoon After fork(), store the child PID in the global child_pid variable so the signal handler can forward signals to it. Clear child_pid in the child process to prevent recursion. Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com> --- sys-utils/unshare.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 9255ff4f8..8bc805e05 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -1149,6 +1149,7 @@ int main(int argc, char *argv[]) case -1: err(EXIT_FAILURE, _("fork failed")); case 0: /* child */ + child_pid = 0; /* Clear in child process */ if (sigprocmask(SIG_SETMASK, &oldsigset, NULL)) err(EXIT_FAILURE, _("sigprocmask restore failed")); @@ -1156,6 +1157,7 @@ int main(int argc, char *argv[]) close(fd_bind); break; default: /* parent */ + child_pid = pid; /* Store child PID for signal handler */ break; } } -- 2.47.3 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 5/5] unshare: handle EINTR in waitpid loop 2026-01-08 18:31 [PATCH 0/5] unshare: fix signal forwarding to child processes Kiran Rangoon ` (3 preceding siblings ...) 2026-01-08 18:31 ` [PATCH 4/5] unshare: store child PID in global variable Kiran Rangoon @ 2026-01-08 18:31 ` Kiran Rangoon 2026-01-12 14:05 ` [PATCH 0/5] unshare: fix signal forwarding to child processes Karel Zak 5 siblings, 0 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-08 18:31 UTC (permalink / raw) To: util-linux; +Cc: Kiran Rangoon Modify waitpid() to retry on EINTR. When the signal handler forwards a signal to the child, waitpid() can be interrupted. We need to retry in this case instead of failing. Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com> --- sys-utils/unshare.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 8bc805e05..2ced8b7a8 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -1172,7 +1172,14 @@ int main(int argc, char *argv[]) } if (pid) { - if (waitpid(pid, &status, 0) == -1) + int rc; + + /* Wait for child, handling EINTR from signal forwarding */ + do { + rc = waitpid(pid, &status, 0); + } while (rc == -1 && errno == EINTR); + + if (rc == -1) err(EXIT_FAILURE, _("waitpid failed")); if (WIFEXITED(status)) -- 2.47.3 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH 0/5] unshare: fix signal forwarding to child processes 2026-01-08 18:31 [PATCH 0/5] unshare: fix signal forwarding to child processes Kiran Rangoon ` (4 preceding siblings ...) 2026-01-08 18:31 ` [PATCH 5/5] unshare: handle EINTR in waitpid loop Kiran Rangoon @ 2026-01-12 14:05 ` Karel Zak 2026-01-13 17:29 ` [Patch V2 0/4] " Kiran Rangoon 5 siblings, 1 reply; 18+ messages in thread From: Karel Zak @ 2026-01-12 14:05 UTC (permalink / raw) To: Kiran Rangoon; +Cc: util-linux On Thu, Jan 08, 2026 at 01:31:29PM -0500, Kiran Rangoon wrote: > This series fixes a bug where unshare with --fork was not properly > forwarding SIGTERM and SIGINT to child processes, causing premature > termination during system shutdown. Thanks. Created PR https://github.com/util-linux/util-linux/pull/3961 to test by CI on github. Karel -- Karel Zak <kzak@redhat.com> http://karelzak.blogspot.com ^ permalink raw reply [flat|nested] 18+ messages in thread
* [Patch V2 0/4] unshare: fix signal forwarding to child processes 2026-01-12 14:05 ` [PATCH 0/5] unshare: fix signal forwarding to child processes Karel Zak @ 2026-01-13 17:29 ` Kiran Rangoon 2026-01-13 17:29 ` [V2 1/4] unshare: add --forward-signals option to argument parser Kiran Rangoon ` (3 more replies) 0 siblings, 4 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-13 17:29 UTC (permalink / raw) To: util-linux; +Cc: kzak, Kiran Rangoon Changes in V2 (responding to feedback from karelzak): - Made signal forwarding opt-in via new --forward-signals flag - Preserves default behavior (signals blocked) for backward compatibility - Addresses concerns from PR #1087 about "impatient parent" problem This adds --forward-signals to enable graceful shutdown of child processes during system reboot. Since util-linux 2.36, unshare blocks SIGTERM/SIGINT to prevent premature parent exit, but this prevents children from receiving shutdown signals for cleanup. The new flag installs signal handlers instead of blocking, forwards signals to the child, and properly handles EINTR in waitpid(). Includes tests for basic forwarding and --kill-child compatibility. Kiran Rangoon (4): unshare: add --forward-signals option to argument parser unshare: implement signal forwarding when --forward-signals is used unshare: document --forward-signals in man page tests: add tests for unshare --forward-signals sys-utils/unshare.1.adoc | 13 ++++ sys-utils/unshare.c | 67 +++++++++++++++++-- tests/expected/unshare/forward-signals | 1 + .../unshare/forward-signals-kill-child | 1 + tests/ts/unshare/forward-signals | 55 +++++++++++++++ tests/ts/unshare/forward-signals-kill-child | 56 ++++++++++++++++ 6 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 tests/expected/unshare/forward-signals create mode 100644 tests/expected/unshare/forward-signals-kill-child create mode 100755 tests/ts/unshare/forward-signals create mode 100755 tests/ts/unshare/forward-signals-kill-child -- 2.47.3 ^ permalink raw reply [flat|nested] 18+ messages in thread
* [V2 1/4] unshare: add --forward-signals option to argument parser 2026-01-13 17:29 ` [Patch V2 0/4] " Kiran Rangoon @ 2026-01-13 17:29 ` Kiran Rangoon 2026-01-13 17:29 ` [V2 2/4] unshare: implement signal forwarding when --forward-signals is used Kiran Rangoon ` (2 subsequent siblings) 3 siblings, 0 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-13 17:29 UTC (permalink / raw) To: util-linux; +Cc: kzak, Kiran Rangoon Add a new --forward-signals command-line option that will allow unshare to forward SIGTERM and SIGINT signals from the parent process to the forked child process. This commit adds the option parsing infrastructure but does not implement the signal forwarding logic yet. The flag defaults to 0 (disabled) to maintain backward compatibility. Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com> --- sys-utils/unshare.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 5370ab981..7e94b9148 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -801,6 +801,7 @@ int main(int argc, char *argv[]) OPT_MAPAUTO, OPT_MAPSUBIDS, OPT_OWNER, + OPT_FORWARD_SIGNALS, }; static const struct option longopts[] = { { "help", no_argument, NULL, 'h' }, @@ -817,6 +818,7 @@ int main(int argc, char *argv[]) { "fork", no_argument, NULL, 'f' }, { "kill-child", optional_argument, NULL, OPT_KILLCHILD }, + { "forward-signals", no_argument, NULL, OPT_FORWARD_SIGNALS }, { "mount-proc", optional_argument, NULL, OPT_MOUNTPROC }, { "mount-binfmt", optional_argument, NULL, OPT_MOUNTBINFMT }, { "map-user", required_argument, NULL, OPT_MAPUSER }, @@ -843,7 +845,7 @@ int main(int argc, char *argv[]) int setgrpcmd = SETGROUPS_NONE; int unshare_flags = 0; - int c, forkit = 0; + int c, forkit = 0, forward_signals = 0; uid_t mapuser = -1, owneruser = -1; gid_t mapgroup = -1, ownergroup = -1; struct map_range *usermap = NULL; @@ -1015,6 +1017,10 @@ int main(int argc, char *argv[]) keepcaps = 1; cap_last_cap(); /* Force last cap to be cached before we fork. */ break; + case OPT_FORWARD_SIGNALS: + forkit = 1; + forward_signals = 1; + break; case 'S': uid = strtoul_or_err(optarg, _("failed to parse uid")); force_uid = 1; -- 2.47.3 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [V2 2/4] unshare: implement signal forwarding when --forward-signals is used 2026-01-13 17:29 ` [Patch V2 0/4] " Kiran Rangoon 2026-01-13 17:29 ` [V2 1/4] unshare: add --forward-signals option to argument parser Kiran Rangoon @ 2026-01-13 17:29 ` Kiran Rangoon 2026-01-13 17:29 ` [V2 3/4] unshare: document --forward-signals in man page Kiran Rangoon 2026-01-13 17:29 ` [V2 4/4] tests: add tests for unshare --forward-signals Kiran Rangoon 3 siblings, 0 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-13 17:29 UTC (permalink / raw) To: util-linux; +Cc: kzak, Kiran Rangoon When --forward-signals is specified, install signal handlers for SIGTERM and SIGINT that forward these signals to the child process. This allows child processes with cleanup handlers to execute gracefully during shutdown scenarios like system reboot. Also fix waitpid() to handle EINTR properly when signals are being forwarded, ensuring the parent waits for the child to complete signal handling before exiting. Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com> --- sys-utils/unshare.c | 59 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 7e94b9148..efcb57cba 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -74,6 +74,9 @@ static struct namespace_file { static int npersists; /* number of persistent namespaces */ +/* Global PID of child process for signal forwarding */ +static volatile pid_t child_pid = 0; + enum { SETGROUPS_NONE = -1, SETGROUPS_DENY = 0, @@ -135,6 +138,19 @@ static void map_id(const char *file, uint32_t from, uint32_t to) close(fd); } +/** + * forward_signal() - Forward signal to child process + * @sig: Signal number to forward + * + * Signal handler that forwards SIGTERM/SIGINT from parent to child. + * Only used when --forward-signals flag is enabled. + */ +static void forward_signal(int sig) +{ + if (child_pid > 0) + kill(child_pid, sig); +} + static unsigned long parse_propagation(const char *str) { size_t i; @@ -1106,11 +1122,32 @@ int main(int argc, char *argv[]) settime(monotonic, CLOCK_MONOTONIC); if (forkit) { - if (sigemptyset(&sigset) != 0 || - sigaddset(&sigset, SIGINT) != 0 || - sigaddset(&sigset, SIGTERM) != 0 || - sigprocmask(SIG_BLOCK, &sigset, &oldsigset) != 0) - err(EXIT_FAILURE, _("sigprocmask block failed")); + if (forward_signals) { + /* Install signal handlers to forward signals to child */ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = forward_signal; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction(SIGTERM, &sa, NULL) == -1) + err(EXIT_FAILURE, _("sigaction SIGTERM failed")); + if (sigaction(SIGINT, &sa, NULL) == -1) + err(EXIT_FAILURE, _("sigaction SIGINT failed")); + + /* Save old mask for child to restore */ + if (sigemptyset(&sigset) != 0 || + sigprocmask(SIG_SETMASK, NULL, &oldsigset) != 0) + err(EXIT_FAILURE, _("sigprocmask failed")); + } else { + /* Block signals to prevent "impatient parent" problem */ + if (sigemptyset(&sigset) != 0 || + sigaddset(&sigset, SIGINT) != 0 || + sigaddset(&sigset, SIGTERM) != 0 || + sigprocmask(SIG_BLOCK, &sigset, &oldsigset) != 0) + err(EXIT_FAILURE, _("sigprocmask block failed")); + } #ifdef HAVE_PIDFD_OPEN if (kill_child_signo != 0) { /* make a connection to the original process (parent) */ @@ -1127,6 +1164,7 @@ int main(int argc, char *argv[]) case -1: err(EXIT_FAILURE, _("fork failed")); case 0: /* child */ + child_pid = 0; /* Clear in child process */ if (sigprocmask(SIG_SETMASK, &oldsigset, NULL)) err(EXIT_FAILURE, _("sigprocmask restore failed")); @@ -1134,6 +1172,8 @@ int main(int argc, char *argv[]) close(fd_bind); break; default: /* parent */ + if (forward_signals) + child_pid = pid; /* Store for signal handler */ break; } } @@ -1148,7 +1188,14 @@ int main(int argc, char *argv[]) } if (pid) { - if (waitpid(pid, &status, 0) == -1) + int rc; + + /* Wait for child, handling EINTR from signal forwarding */ + do { + rc = waitpid(pid, &status, 0); + } while (rc == -1 && errno == EINTR); + + if (rc == -1) err(EXIT_FAILURE, _("waitpid failed")); if (WIFEXITED(status)) -- 2.47.3 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [V2 3/4] unshare: document --forward-signals in man page 2026-01-13 17:29 ` [Patch V2 0/4] " Kiran Rangoon 2026-01-13 17:29 ` [V2 1/4] unshare: add --forward-signals option to argument parser Kiran Rangoon 2026-01-13 17:29 ` [V2 2/4] unshare: implement signal forwarding when --forward-signals is used Kiran Rangoon @ 2026-01-13 17:29 ` Kiran Rangoon 2026-01-13 17:29 ` [V2 4/4] tests: add tests for unshare --forward-signals Kiran Rangoon 3 siblings, 0 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-13 17:29 UTC (permalink / raw) To: util-linux; +Cc: kzak, Kiran Rangoon Add documentation for the new --forward-signals option, explaining its use case (graceful shutdown during reboot/system management) and interaction with the --fork option. Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com> --- sys-utils/unshare.1.adoc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc index 85d00af3c..fa0b61df1 100644 --- a/sys-utils/unshare.1.adoc +++ b/sys-utils/unshare.1.adoc @@ -81,6 +81,19 @@ Create a new time namespace. If _file_ is specified, then the namespace is made *-f*, *--fork*:: Fork the specified _program_ as a child process of *unshare* rather than running it directly. This is useful when creating a new PID namespace. Note that when *unshare* is waiting for the child process, then it ignores *SIGINT* and *SIGTERM* and does not forward any signals to the child. It is necessary to send signals to the child process. +*--forward-signals*:: +Forward *SIGTERM* and *SIGINT* signals received by the parent *unshare* process to the child process. +When this option is not specified, *unshare* ignores these signals while waiting for the child process +to exit (the default behavior since util-linux 2.36). This allows the parent to remain alive while +the child process handles the signals. ++ +This option is useful when the parent *unshare* process will receive *SIGTERM* or *SIGINT* signals +(for example, during system reboot or from a process manager), and you want the child process to +be notified of graceful shutdown requests so it can perform cleanup operations. If your child +process has signal handlers (such as shell trap handlers), enabling this option allows them to execute. ++ +This option implies *--fork*. + *--keep-caps*:: When the *--user* option is given, ensure that capabilities granted in the user namespace are preserved in the child process. -- 2.47.3 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [V2 4/4] tests: add tests for unshare --forward-signals 2026-01-13 17:29 ` [Patch V2 0/4] " Kiran Rangoon ` (2 preceding siblings ...) 2026-01-13 17:29 ` [V2 3/4] unshare: document --forward-signals in man page Kiran Rangoon @ 2026-01-13 17:29 ` Kiran Rangoon 2026-01-16 17:06 ` [PATCH V3 0/4] unshare: add --forward-signals option Kiran Rangoon 3 siblings, 1 reply; 18+ messages in thread From: Kiran Rangoon @ 2026-01-13 17:29 UTC (permalink / raw) To: util-linux; +Cc: kzak, Kiran Rangoon Add two test cases for the new --forward-signals option: - forward-signals: verifies SIGTERM is forwarded to child - forward-signals-kill-child: verifies compatibility with --kill-child Both tests use test_sigreceive which exits with the signal number received, confirming proper signal forwarding. Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com> --- tests/expected/unshare/forward-signals | 1 + .../unshare/forward-signals-kill-child | 1 + tests/ts/unshare/forward-signals | 55 ++++++++++++++++++ tests/ts/unshare/forward-signals-kill-child | 56 +++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 tests/expected/unshare/forward-signals create mode 100644 tests/expected/unshare/forward-signals-kill-child create mode 100755 tests/ts/unshare/forward-signals create mode 100755 tests/ts/unshare/forward-signals-kill-child diff --git a/tests/expected/unshare/forward-signals b/tests/expected/unshare/forward-signals new file mode 100644 index 000000000..868dab6e1 --- /dev/null +++ b/tests/expected/unshare/forward-signals @@ -0,0 +1 @@ +SIGTERM forwarded successfully diff --git a/tests/expected/unshare/forward-signals-kill-child b/tests/expected/unshare/forward-signals-kill-child new file mode 100644 index 000000000..d4e1224da --- /dev/null +++ b/tests/expected/unshare/forward-signals-kill-child @@ -0,0 +1 @@ +SIGTERM forwarded successfully with kill-child diff --git a/tests/ts/unshare/forward-signals b/tests/ts/unshare/forward-signals new file mode 100755 index 000000000..24aea8563 --- /dev/null +++ b/tests/ts/unshare/forward-signals @@ -0,0 +1,55 @@ +#!/bin/bash + +# +# Copyright (C) 2026 util-linux contributors +# +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +TS_TOPDIR="${0%/*}/../.." +TS_DESC="forward signals to child" + +. "$TS_TOPDIR/functions.sh" +ts_init "$*" + +ts_check_test_command "$TS_CMD_UNSHARE" +ts_check_test_command "$TS_HELPER_SIGRECEIVE" + +ts_skip_nonroot + +# Start unshare with --forward-signals and a child process that exits +# with the signal number it receives +"$TS_CMD_UNSHARE" --user --map-root-user \ + --pid --mount-proc \ + --fork --forward-signals \ + "$TS_HELPER_SIGRECEIVE" < /dev/null >> $TS_OUTPUT 2>> $TS_ERRLOG & + +UNSHARE_PID=$! + +# Give child time to set up signal handlers +sleep 2 + +# Send SIGTERM to parent unshare process +kill -TERM $UNSHARE_PID + +# Wait for completion +wait $UNSHARE_PID +EXIT_CODE=$? + +# test_sigreceive exits with the signal number it receives (15 = SIGTERM) +if [ $EXIT_CODE -eq 15 ]; then + echo "SIGTERM forwarded successfully" >> $TS_OUTPUT +else + echo "UNEXPECTED EXIT CODE: $EXIT_CODE" >> $TS_OUTPUT +fi + +ts_finalize diff --git a/tests/ts/unshare/forward-signals-kill-child b/tests/ts/unshare/forward-signals-kill-child new file mode 100755 index 000000000..3065f1052 --- /dev/null +++ b/tests/ts/unshare/forward-signals-kill-child @@ -0,0 +1,56 @@ +#!/bin/bash + +# +# Copyright (C) 2026 util-linux contributors +# +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +TS_TOPDIR="${0%/*}/../.." +TS_DESC="forward signals with kill-child" + +. "$TS_TOPDIR/functions.sh" +ts_init "$*" + +ts_check_test_command "$TS_CMD_UNSHARE" +ts_check_test_command "$TS_HELPER_SIGRECEIVE" + +ts_skip_nonroot + +# Start unshare with both --forward-signals and --kill-child +# The child should receive SIGTERM from forwarding first +"$TS_CMD_UNSHARE" --user --map-root-user \ + --pid --mount-proc \ + --fork --forward-signals --kill-child \ + "$TS_HELPER_SIGRECEIVE" < /dev/null >> $TS_OUTPUT 2>> $TS_ERRLOG & + +UNSHARE_PID=$! + +# Give child time to set up signal handlers +sleep 2 + +# Send SIGTERM to parent unshare process +kill -TERM $UNSHARE_PID + +# Wait for completion +wait $UNSHARE_PID +EXIT_CODE=$? + +# test_sigreceive exits with the signal number it receives (15 = SIGTERM) +# With --kill-child, it should still receive SIGTERM first via forwarding +if [ $EXIT_CODE -eq 15 ]; then + echo "SIGTERM forwarded successfully with kill-child" >> $TS_OUTPUT +else + echo "UNEXPECTED EXIT CODE: $EXIT_CODE" >> $TS_OUTPUT +fi + +ts_finalize -- 2.47.3 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH V3 0/4] unshare: add --forward-signals option 2026-01-13 17:29 ` [V2 4/4] tests: add tests for unshare --forward-signals Kiran Rangoon @ 2026-01-16 17:06 ` Kiran Rangoon 2026-01-16 17:06 ` [Patch V3 1/4] unshare: add --forward-signals option to argument parser Kiran Rangoon ` (4 more replies) 0 siblings, 5 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-16 17:06 UTC (permalink / raw) To: util-linux; +Cc: kzak, Kiran Rangoon This series adds a new --forward-signals option to unshare that forwards SIGTERM and SIGINT from the parent process to the child. This allows child processes to handle graceful shutdown during system reboot or service manager termination. Changes in V3: - Add bash completion for --forward-signals - Add usage text in help output - Fix code indentation to match project style - Improve test synchronization (replace fixed sleep with adaptive polling) - Add namespace capability checks in tests to prevent CI failures Kiran Rangoon (4): unshare: add --forward-signals option to argument parser unshare: implement signal forwarding when --forward-signals is used unshare: document --forward-signals in man page tests: add tests for unshare --forward-signals bash-completion/unshare | 1 + sys-utils/unshare.1.adoc | 13 ++++ sys-utils/unshare.c | 64 ++++++++++++++-- tests/expected/unshare/forward-signals | 1 + .../unshare/forward-signals-kill-child | 1 + tests/ts/unshare/forward-signals | 73 ++++++++++++++++++ tests/ts/unshare/forward-signals-kill-child | 74 +++++++++++++++++++ 7 files changed, 222 insertions(+), 5 deletions(-) create mode 100644 tests/expected/unshare/forward-signals create mode 100644 tests/expected/unshare/forward-signals-kill-child create mode 100755 tests/ts/unshare/forward-signals create mode 100755 tests/ts/unshare/forward-signals-kill-child -- 2.47.3 ^ permalink raw reply [flat|nested] 18+ messages in thread
* [Patch V3 1/4] unshare: add --forward-signals option to argument parser 2026-01-16 17:06 ` [PATCH V3 0/4] unshare: add --forward-signals option Kiran Rangoon @ 2026-01-16 17:06 ` Kiran Rangoon 2026-01-16 17:06 ` [Patch V3 2/4] unshare: implement signal forwarding when --forward-signals is used Kiran Rangoon ` (3 subsequent siblings) 4 siblings, 0 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-16 17:06 UTC (permalink / raw) To: util-linux; +Cc: kzak, Kiran Rangoon Add a new --forward-signals command-line option that will allow unshare to forward SIGTERM and SIGINT signals from the parent process to the forked child process. This commit adds the option parsing infrastructure but does not implement the signal forwarding logic yet. The flag defaults to 0 (disabled) to maintain backward compatibility. Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com> --- bash-completion/unshare | 1 + sys-utils/unshare.c | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/bash-completion/unshare b/bash-completion/unshare index 19eeb8f08..fdd40e5cb 100644 --- a/bash-completion/unshare +++ b/bash-completion/unshare @@ -28,6 +28,7 @@ _unshare_module() --cgroup --time --fork + --forward-signals --kill-child --keep-caps --mount-proc diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 5370ab981..8a7a26df2 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -769,6 +769,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -f, --fork fork before launching <program>\n"), out); fputs(_(" --kill-child[=<signame>] when dying, kill the forked child (implies --fork)\n" " defaults to SIGKILL\n"), out); + fputs(_(" --forward-signals forward SIGTERM and SIGINT to child (implies --fork)\n"), out); fputs(USAGE_SEPARATOR, out); fputs(_(" --setgroups allow|deny control the setgroups syscall in user namespaces\n"), out); fputs(_(" --keep-caps retain capabilities granted in user namespaces\n"), out); @@ -801,6 +802,7 @@ int main(int argc, char *argv[]) OPT_MAPAUTO, OPT_MAPSUBIDS, OPT_OWNER, + OPT_FORWARD_SIGNALS, }; static const struct option longopts[] = { { "help", no_argument, NULL, 'h' }, @@ -817,6 +819,7 @@ int main(int argc, char *argv[]) { "fork", no_argument, NULL, 'f' }, { "kill-child", optional_argument, NULL, OPT_KILLCHILD }, + { "forward-signals", no_argument, NULL, OPT_FORWARD_SIGNALS }, { "mount-proc", optional_argument, NULL, OPT_MOUNTPROC }, { "mount-binfmt", optional_argument, NULL, OPT_MOUNTBINFMT }, { "map-user", required_argument, NULL, OPT_MAPUSER }, @@ -843,7 +846,7 @@ int main(int argc, char *argv[]) int setgrpcmd = SETGROUPS_NONE; int unshare_flags = 0; - int c, forkit = 0; + int c, forkit = 0, forward_signals = 0; uid_t mapuser = -1, owneruser = -1; gid_t mapgroup = -1, ownergroup = -1; struct map_range *usermap = NULL; @@ -1015,6 +1018,10 @@ int main(int argc, char *argv[]) keepcaps = 1; cap_last_cap(); /* Force last cap to be cached before we fork. */ break; + case OPT_FORWARD_SIGNALS: + forkit = 1; + forward_signals = 1; + break; case 'S': uid = strtoul_or_err(optarg, _("failed to parse uid")); force_uid = 1; -- 2.47.3 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Patch V3 2/4] unshare: implement signal forwarding when --forward-signals is used 2026-01-16 17:06 ` [PATCH V3 0/4] unshare: add --forward-signals option Kiran Rangoon 2026-01-16 17:06 ` [Patch V3 1/4] unshare: add --forward-signals option to argument parser Kiran Rangoon @ 2026-01-16 17:06 ` Kiran Rangoon 2026-01-16 17:06 ` [Patch V3 3/4] unshare: document --forward-signals in man page Kiran Rangoon ` (2 subsequent siblings) 4 siblings, 0 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-16 17:06 UTC (permalink / raw) To: util-linux; +Cc: kzak, Kiran Rangoon When --forward-signals is specified, install signal handlers for SIGTERM and SIGINT that forward these signals to the child process. This allows child processes with cleanup handlers to execute gracefully during shutdown scenarios like system reboot. Also fix waitpid() to handle EINTR properly when signals are being forwarded, ensuring the parent waits for the child to complete signal handling before exiting. Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com> --- sys-utils/unshare.c | 55 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 8a7a26df2..a4d28892d 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -74,6 +74,9 @@ static struct namespace_file { static int npersists; /* number of persistent namespaces */ +/* Global PID of child process for signal forwarding */ +static volatile pid_t child_pid = 0; + enum { SETGROUPS_NONE = -1, SETGROUPS_DENY = 0, @@ -135,6 +138,19 @@ static void map_id(const char *file, uint32_t from, uint32_t to) close(fd); } +/** + * forward_signal() - Forward signal to child process + * @sig: Signal number to forward + * + * Signal handler that forwards SIGTERM/SIGINT from parent to child. + * Only used when --forward-signals flag is enabled. + */ +static void forward_signal(int sig) +{ + if (child_pid > 0) + kill(child_pid, sig); +} + static unsigned long parse_propagation(const char *str) { size_t i; @@ -1107,11 +1123,32 @@ int main(int argc, char *argv[]) settime(monotonic, CLOCK_MONOTONIC); if (forkit) { + if (forward_signals) { + /* Install signal handlers to forward signals to child */ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = forward_signal; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction(SIGTERM, &sa, NULL) == -1) + err(EXIT_FAILURE, _("sigaction SIGTERM failed")); + if (sigaction(SIGINT, &sa, NULL) == -1) + err(EXIT_FAILURE, _("sigaction SIGINT failed")); + + /* Save old mask for child to restore */ + if (sigemptyset(&sigset) != 0 || + sigprocmask(SIG_SETMASK, NULL, &oldsigset) != 0) + err(EXIT_FAILURE, _("sigprocmask failed")); + } else { + /* Block signals to prevent "impatient parent" problem */ if (sigemptyset(&sigset) != 0 || - sigaddset(&sigset, SIGINT) != 0 || - sigaddset(&sigset, SIGTERM) != 0 || - sigprocmask(SIG_BLOCK, &sigset, &oldsigset) != 0) + sigaddset(&sigset, SIGINT) != 0 || + sigaddset(&sigset, SIGTERM) != 0 || + sigprocmask(SIG_BLOCK, &sigset, &oldsigset) != 0) err(EXIT_FAILURE, _("sigprocmask block failed")); + } #ifdef HAVE_PIDFD_OPEN if (kill_child_signo != 0) { /* make a connection to the original process (parent) */ @@ -1128,6 +1165,7 @@ int main(int argc, char *argv[]) case -1: err(EXIT_FAILURE, _("fork failed")); case 0: /* child */ + child_pid = 0; /* Clear in child process */ if (sigprocmask(SIG_SETMASK, &oldsigset, NULL)) err(EXIT_FAILURE, _("sigprocmask restore failed")); @@ -1135,6 +1173,8 @@ int main(int argc, char *argv[]) close(fd_bind); break; default: /* parent */ + if (forward_signals) + child_pid = pid; /* Store for signal handler */ break; } } @@ -1149,7 +1189,14 @@ int main(int argc, char *argv[]) } if (pid) { - if (waitpid(pid, &status, 0) == -1) + int rc; + + /* Wait for child, handling EINTR from signal forwarding */ + do { + rc = waitpid(pid, &status, 0); + } while (rc == -1 && errno == EINTR); + + if (rc == -1) err(EXIT_FAILURE, _("waitpid failed")); if (WIFEXITED(status)) -- 2.47.3 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Patch V3 3/4] unshare: document --forward-signals in man page 2026-01-16 17:06 ` [PATCH V3 0/4] unshare: add --forward-signals option Kiran Rangoon 2026-01-16 17:06 ` [Patch V3 1/4] unshare: add --forward-signals option to argument parser Kiran Rangoon 2026-01-16 17:06 ` [Patch V3 2/4] unshare: implement signal forwarding when --forward-signals is used Kiran Rangoon @ 2026-01-16 17:06 ` Kiran Rangoon 2026-01-16 17:06 ` [Patch V3 4/4] tests: add tests for unshare --forward-signals Kiran Rangoon 2026-01-21 8:37 ` [PATCH V3 0/4] unshare: add --forward-signals option Karel Zak 4 siblings, 0 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-16 17:06 UTC (permalink / raw) To: util-linux; +Cc: kzak, Kiran Rangoon Add documentation for the new --forward-signals option, explaining its use case (graceful shutdown during reboot/system management) and interaction with the --fork option. Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com> --- sys-utils/unshare.1.adoc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc index 1d1fd68f8..888928147 100644 --- a/sys-utils/unshare.1.adoc +++ b/sys-utils/unshare.1.adoc @@ -81,6 +81,19 @@ Create a new time namespace. If _file_ is specified, then the namespace is made *-f*, *--fork*:: Fork the specified _program_ as a child process of *unshare* rather than running it directly. This is useful when creating a new PID namespace. Note that when *unshare* is waiting for the child process, then it ignores *SIGINT* and *SIGTERM* and does not forward any signals to the child. It is necessary to send signals to the child process. +*--forward-signals*:: +Forward *SIGTERM* and *SIGINT* signals received by the parent *unshare* process to the child process. +When this option is not specified, *unshare* ignores these signals while waiting for the child process +to exit (the default behavior since util-linux 2.36). This allows the parent to remain alive while +the child process handles the signals. ++ +This option is useful when the parent *unshare* process will receive *SIGTERM* or *SIGINT* signals +(for example, during system reboot or from a process manager), and you want the child process to +be notified of graceful shutdown requests so it can perform cleanup operations. If your child +process has signal handlers (such as shell trap handlers), enabling this option allows them to execute. ++ +This option implies *--fork*. + *--keep-caps*:: When the *--user* option is given, ensure that capabilities granted in the user namespace are preserved in the child process. -- 2.47.3 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Patch V3 4/4] tests: add tests for unshare --forward-signals 2026-01-16 17:06 ` [PATCH V3 0/4] unshare: add --forward-signals option Kiran Rangoon ` (2 preceding siblings ...) 2026-01-16 17:06 ` [Patch V3 3/4] unshare: document --forward-signals in man page Kiran Rangoon @ 2026-01-16 17:06 ` Kiran Rangoon 2026-01-21 8:37 ` [PATCH V3 0/4] unshare: add --forward-signals option Karel Zak 4 siblings, 0 replies; 18+ messages in thread From: Kiran Rangoon @ 2026-01-16 17:06 UTC (permalink / raw) To: util-linux; +Cc: kzak, Kiran Rangoon Add two test cases for the new --forward-signals option: - forward-signals: verifies SIGTERM is forwarded to child - forward-signals-kill-child: verifies compatibility with --kill-child Both tests use test_sigreceive which exits with the signal number received, confirming proper signal forwarding. Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com> --- tests/expected/unshare/forward-signals | 1 + .../unshare/forward-signals-kill-child | 1 + tests/ts/unshare/forward-signals | 73 ++++++++++++++++++ tests/ts/unshare/forward-signals-kill-child | 74 +++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 tests/expected/unshare/forward-signals create mode 100644 tests/expected/unshare/forward-signals-kill-child create mode 100755 tests/ts/unshare/forward-signals create mode 100755 tests/ts/unshare/forward-signals-kill-child diff --git a/tests/expected/unshare/forward-signals b/tests/expected/unshare/forward-signals new file mode 100644 index 000000000..868dab6e1 --- /dev/null +++ b/tests/expected/unshare/forward-signals @@ -0,0 +1 @@ +SIGTERM forwarded successfully diff --git a/tests/expected/unshare/forward-signals-kill-child b/tests/expected/unshare/forward-signals-kill-child new file mode 100644 index 000000000..d4e1224da --- /dev/null +++ b/tests/expected/unshare/forward-signals-kill-child @@ -0,0 +1 @@ +SIGTERM forwarded successfully with kill-child diff --git a/tests/ts/unshare/forward-signals b/tests/ts/unshare/forward-signals new file mode 100755 index 000000000..042ee0a58 --- /dev/null +++ b/tests/ts/unshare/forward-signals @@ -0,0 +1,73 @@ +#!/bin/bash + +# +# Copyright (C) 2026 util-linux contributors +# +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +TS_TOPDIR="${0%/*}/../.." +TS_DESC="forward signals to child" + +. "$TS_TOPDIR/functions.sh" +ts_init "$*" + +ts_check_test_command "$TS_CMD_UNSHARE" +ts_check_test_command "$TS_HELPER_SIGRECEIVE" + +ts_skip_nonroot + +# Verify that user namespaces work +"$TS_CMD_UNSHARE" --user --map-root-user /bin/true &> /dev/null || \ + ts_skip "user namespaces not supported" + +# Start unshare with --forward-signals and a child process that exits +# with the signal number it receives +"$TS_CMD_UNSHARE" --user --map-root-user \ + --pid --mount-proc \ + --fork --forward-signals \ + "$TS_HELPER_SIGRECEIVE" < /dev/null >> $TS_OUTPUT 2>> $TS_ERRLOG & + +UNSHARE_PID=$! + +# Wait for child to be ready - poll for up to 10 seconds +# The child process tree should exist +TIMEOUT=10 +ELAPSED=0 +while [ $ELAPSED -lt $TIMEOUT ]; do + # Check if the process still exists + if ! kill -0 $UNSHARE_PID 2>/dev/null; then + ts_skip "unshare process died prematurely" + fi + # Give it a bit more time to set up namespaces and signal handlers + if [ $ELAPSED -ge 2 ]; then + break + fi + sleep 0.5 + ELAPSED=$((ELAPSED + 1)) +done + +# Send SIGTERM to parent unshare process +kill -TERM $UNSHARE_PID 2>/dev/null + +# Wait for completion with timeout +wait $UNSHARE_PID +EXIT_CODE=$? + +# test_sigreceive exits with the signal number it receives (15 = SIGTERM) +if [ $EXIT_CODE -eq 15 ]; then + echo "SIGTERM forwarded successfully" >> $TS_OUTPUT +else + echo "UNEXPECTED EXIT CODE: $EXIT_CODE" >> $TS_OUTPUT +fi + +ts_finalize diff --git a/tests/ts/unshare/forward-signals-kill-child b/tests/ts/unshare/forward-signals-kill-child new file mode 100755 index 000000000..766f9fe56 --- /dev/null +++ b/tests/ts/unshare/forward-signals-kill-child @@ -0,0 +1,74 @@ +#!/bin/bash + +# +# Copyright (C) 2026 util-linux contributors +# +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +TS_TOPDIR="${0%/*}/../.." +TS_DESC="forward signals with kill-child" + +. "$TS_TOPDIR/functions.sh" +ts_init "$*" + +ts_check_test_command "$TS_CMD_UNSHARE" +ts_check_test_command "$TS_HELPER_SIGRECEIVE" + +ts_skip_nonroot + +# Verify that user namespaces work +"$TS_CMD_UNSHARE" --user --map-root-user /bin/true &> /dev/null || \ + ts_skip "user namespaces not supported" + +# Start unshare with both --forward-signals and --kill-child +# The child should receive SIGTERM from forwarding first +"$TS_CMD_UNSHARE" --user --map-root-user \ + --pid --mount-proc \ + --fork --forward-signals --kill-child \ + "$TS_HELPER_SIGRECEIVE" < /dev/null >> $TS_OUTPUT 2>> $TS_ERRLOG & + +UNSHARE_PID=$! + +# Wait for child to be ready - poll for up to 10 seconds +# The child process tree should exist +TIMEOUT=10 +ELAPSED=0 +while [ $ELAPSED -lt $TIMEOUT ]; do + # Check if the process still exists + if ! kill -0 $UNSHARE_PID 2>/dev/null; then + ts_skip "unshare process died prematurely" + fi + # Give it a bit more time to set up namespaces and signal handlers + if [ $ELAPSED -ge 2 ]; then + break + fi + sleep 0.5 + ELAPSED=$((ELAPSED + 1)) +done + +# Send SIGTERM to parent unshare process +kill -TERM $UNSHARE_PID 2>/dev/null + +# Wait for completion with timeout +wait $UNSHARE_PID +EXIT_CODE=$? + +# test_sigreceive exits with the signal number it receives (15 = SIGTERM) +# With --kill-child, it should still receive SIGTERM first via forwarding +if [ $EXIT_CODE -eq 15 ]; then + echo "SIGTERM forwarded successfully with kill-child" >> $TS_OUTPUT +else + echo "UNEXPECTED EXIT CODE: $EXIT_CODE" >> $TS_OUTPUT +fi + +ts_finalize -- 2.47.3 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH V3 0/4] unshare: add --forward-signals option 2026-01-16 17:06 ` [PATCH V3 0/4] unshare: add --forward-signals option Kiran Rangoon ` (3 preceding siblings ...) 2026-01-16 17:06 ` [Patch V3 4/4] tests: add tests for unshare --forward-signals Kiran Rangoon @ 2026-01-21 8:37 ` Karel Zak 4 siblings, 0 replies; 18+ messages in thread From: Karel Zak @ 2026-01-21 8:37 UTC (permalink / raw) To: Kiran Rangoon; +Cc: util-linux On Fri, Jan 16, 2026 at 12:06:44PM -0500, Kiran Rangoon wrote: > bash-completion/unshare | 1 + > sys-utils/unshare.1.adoc | 13 ++++ > sys-utils/unshare.c | 64 ++++++++++++++-- > tests/expected/unshare/forward-signals | 1 + > .../unshare/forward-signals-kill-child | 1 + > tests/ts/unshare/forward-signals | 73 ++++++++++++++++++ > tests/ts/unshare/forward-signals-kill-child | 74 +++++++++++++++++++ > 7 files changed, 222 insertions(+), 5 deletions(-) > create mode 100644 tests/expected/unshare/forward-signals > create mode 100644 tests/expected/unshare/forward-signals-kill-child > create mode 100755 tests/ts/unshare/forward-signals > create mode 100755 tests/ts/unshare/forward-signals-kill-child Applied, thanks. Karel -- Karel Zak <kzak@redhat.com> http://karelzak.blogspot.com ^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-01-21 8:37 UTC | newest] Thread overview: 18+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-01-08 18:31 [PATCH 0/5] unshare: fix signal forwarding to child processes Kiran Rangoon 2026-01-08 18:31 ` [PATCH 1/5] unshare: add global child_pid variable for signal forwarding Kiran Rangoon 2026-01-08 18:31 ` [PATCH 2/5] unshare: add signal forwarding handler Kiran Rangoon 2026-01-08 18:31 ` [PATCH 3/5] unshare: replace signal blocking with signal handlers Kiran Rangoon 2026-01-08 18:31 ` [PATCH 4/5] unshare: store child PID in global variable Kiran Rangoon 2026-01-08 18:31 ` [PATCH 5/5] unshare: handle EINTR in waitpid loop Kiran Rangoon 2026-01-12 14:05 ` [PATCH 0/5] unshare: fix signal forwarding to child processes Karel Zak 2026-01-13 17:29 ` [Patch V2 0/4] " Kiran Rangoon 2026-01-13 17:29 ` [V2 1/4] unshare: add --forward-signals option to argument parser Kiran Rangoon 2026-01-13 17:29 ` [V2 2/4] unshare: implement signal forwarding when --forward-signals is used Kiran Rangoon 2026-01-13 17:29 ` [V2 3/4] unshare: document --forward-signals in man page Kiran Rangoon 2026-01-13 17:29 ` [V2 4/4] tests: add tests for unshare --forward-signals Kiran Rangoon 2026-01-16 17:06 ` [PATCH V3 0/4] unshare: add --forward-signals option Kiran Rangoon 2026-01-16 17:06 ` [Patch V3 1/4] unshare: add --forward-signals option to argument parser Kiran Rangoon 2026-01-16 17:06 ` [Patch V3 2/4] unshare: implement signal forwarding when --forward-signals is used Kiran Rangoon 2026-01-16 17:06 ` [Patch V3 3/4] unshare: document --forward-signals in man page Kiran Rangoon 2026-01-16 17:06 ` [Patch V3 4/4] tests: add tests for unshare --forward-signals Kiran Rangoon 2026-01-21 8:37 ` [PATCH V3 0/4] unshare: add --forward-signals option Karel Zak
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox