Add optional signal forwarder and '-z' option to cli

Add a 'forward_signal' option to the minijail configuration struct that
defaults to false, but when set installs a signal handler in the parent
process that forwards any signal received (except SIGCHLD) to the jailed
child process.

The minijail0 cli defaults to having this setting enabled, but called
with the `-z` option this is disabled.

This is used to make daemon management (e.g. with `sv`) easier, since
otherwise SIGTERM on minijail0 just orphans the true service.

Change-Id: Ide0471511a4242839e3c8a34795e5a297b6ef57a
diff --git a/libminijail.c b/libminijail.c
index 2e3e8b6..98ad5fa 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -127,6 +127,7 @@
 		int reset_signal_mask : 1;
 		int close_open_fds : 1;
 		int new_session_keyring : 1;
+		int forward_signals : 1;
 	} flags;
 	uid_t uid;
 	gid_t gid;
@@ -169,6 +170,7 @@
 	j->flags.do_init = 0;
 	j->flags.pid_file = 0;
 	j->flags.cgroups = 0;
+	j->flags.forward_signals = 0;
 }
 
 /*
@@ -619,6 +621,12 @@
 	return 0;
 }
 
+int API minijail_forward_signals(struct minijail *j)
+{
+	j->flags.forward_signals = 1;
+	return 0;
+}
+
 int API minijail_mount_with_data(struct minijail *j, const char *src,
 				 const char *dest, const char *type,
 				 unsigned long flags, const char *data)
@@ -1504,6 +1512,43 @@
 	}
 }
 
+static pid_t forward_pid = -1;
+
+static void forward_signal(__attribute__((unused)) int nr,
+			   __attribute__((unused)) siginfo_t *siginfo,
+			   __attribute__((unused)) void *void_context)
+{
+	if (forward_pid != -1) {
+		kill(forward_pid, nr);
+	}
+}
+
+static void install_signal_handlers(void)
+{
+	struct sigaction act;
+
+	memset(&act, 0, sizeof(act));
+	act.sa_sigaction = &forward_signal;
+	act.sa_flags = SA_SIGINFO|SA_RESTART;
+
+	/* Handle all signals, except SIGCHLD. */
+	for (int nr = 1; nr <= SIGUNUSED; nr++) {
+		/*
+		 * We don't care if we get EINVAL: that just means that we
+		 * can't handle this signal, so let's skip it and continue.
+		 */
+		sigaction(nr, &act, NULL);
+	}
+	/* Reset SIGCHLD's handler. */
+	signal(SIGCHLD, SIG_DFL);
+
+	/* Handle real-time signals. */
+	for (int nr = SIGRTMIN; nr <= SIGRTMAX; nr++) {
+		sigaction(nr, &act, NULL);
+	}
+}
+
+
 void API minijail_enter(const struct minijail *j)
 {
 	/*
@@ -2022,6 +2067,11 @@
 
 		j->initpid = child_pid;
 
+		if (j->flags.forward_signals) {
+			forward_pid = child_pid;
+			install_signal_handlers();
+		}
+
 		if (j->flags.pid_file)
 			write_pid_file_or_die(j);
 
diff --git a/libminijail.h b/libminijail.h
index 9177ca8..a1f2f74 100644
--- a/libminijail.h
+++ b/libminijail.h
@@ -104,6 +104,12 @@
 int minijail_add_to_cgroup(struct minijail *j, const char *path);
 
 /*
+ * Install signal handlers in the minijail process that forward received
+ * signals to the jailed child process.
+ */
+int minijail_forward_signals(struct minijail *j);
+
+/*
  * minijail_enter_chroot: enables chroot() restriction for @j
  * @j   minijail to apply restriction to
  * @dir directory to chroot() to. Owned by caller.
diff --git a/minijail0.c b/minijail0.c
index 470a8df..b9f4e5a 100644
--- a/minijail0.c
+++ b/minijail0.c
@@ -111,7 +111,7 @@
 {
 	size_t i;
 	/* clang-format off */
-	printf("Usage: %s [-GhHiIKlLnNprstUvyY]\n"
+	printf("Usage: %s [-GhHiIKlLnNprstUvyYz]\n"
 	       "  [-a <table>]\n"
 	       "  [-b <src>,<dest>[,<writeable>]] [-k <src>,<dest>,<type>[,<flags>][,<data>]]\n"
 	       "  [-c <caps>] [-C <dir>] [-P <dir>] [-e[file]] [-f <file>] [-g <group>]\n"
@@ -177,6 +177,7 @@
 	       "  -V <file>:  Enter specified mount namespace.\n"
 	       "  -w:         Create and join a new anonymous session keyring.\n"
 	       "  -Y:         Synchronize seccomp filters across thread group.\n"
+	       "  -z:         Don't forward signals to jailed process.\n"
 	       "  --ambient:  Raise ambient capabilities. Requires -c.\n");
 	/* clang-format on */
 }
@@ -197,6 +198,7 @@
 {
 	int opt;
 	int use_seccomp_filter = 0;
+	int forward = 1;
 	int binding = 0;
 	int chroot = 0, pivot_root = 0;
 	int mount_ns = 0, skip_remount = 0;
@@ -210,7 +212,7 @@
 		return 1;
 
 	const char *optstring =
-	    "u:g:sS:c:C:P:b:V:f:m::M::k:a:e::T:vrGhHinNplLt::IUKwyY";
+	    "u:g:sS:c:C:P:b:V:f:m::M::k:a:e::T:vrGhHinNplLt::IUKwyYz";
 	int longoption_index = 0;
 	/* clang-format off */
 	const struct option long_options[] = {
@@ -434,6 +436,9 @@
 		case 'Y':
 			minijail_set_seccomp_filter_tsync(j);
 			break;
+		case 'z':
+			forward = 0;
+			break;
 		/* Long options. */
 		case 128: /* Ambient caps. */
 			ambient_caps = 1;
@@ -454,6 +459,10 @@
 		exit(1);
 	}
 
+	/* Set up signal handlers in minijail unless asked not to. */
+	if (forward)
+		minijail_forward_signals(j);
+
 	/* Only allow bind mounts when entering a chroot or using pivot_root. */
 	if (binding && !(chroot || pivot_root)) {
 		fprintf(stderr, "Can't add bind mounts without chroot or"