public inbox for util-linux@vger.kernel.org
 help / color / mirror / Atom feed
* [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