From cb3a7926948c4d9668d54521cb7907436723b67a Mon Sep 17 00:00:00 2001
From: LiFeng <lifeng68@huawei.com>
Date: Wed, 22 Apr 2020 23:30:23 +0800
Subject: [PATCH] attach: add sigfd to monitor the exit of pid

Signed-off-by: LiFeng <lifeng68@huawei.com>
---
 src/lxc/attach.c   | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/lxc/terminal.c |  4 +--
 2 files changed, 87 insertions(+), 3 deletions(-)

diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index 51f5c08..b25222e 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -1197,6 +1197,56 @@ static int create_attach_timeout_thread(int64_t attach_timeout, pid_t pid)
 out:
 	return ret;
 }
+
+static int attach_signal_handler(int fd, uint32_t events, void *data,
+			  struct lxc_epoll_descr *descr)
+{
+	int ret;
+	siginfo_t info;
+	pid_t *pid = data;
+
+	/* Check whether init is running. */
+	info.si_pid = 0;
+	ret = waitid(P_PID, *pid, &info, WEXITED | WNOWAIT | WNOHANG);
+	if (ret == 0 && info.si_pid == *pid) {
+		return log_error(LXC_MAINLOOP_CLOSE, "Container attach init process %d exited", *pid);
+	}
+
+	return LXC_MAINLOOP_CONTINUE;
+}
+
+static int isulad_setup_signal_fd(sigset_t *oldmask)
+{
+	int ret;
+	sigset_t mask;
+	const int signals[] = {SIGBUS, SIGILL, SIGSEGV, SIGWINCH};
+
+	/* Block everything except serious error signals. */
+	ret = sigfillset(&mask);
+	if (ret < 0)
+		return -EBADF;
+
+	for (int sig = 0; sig < (sizeof(signals) / sizeof(signals[0])); sig++) {
+		ret = sigdelset(&mask, signals[sig]);
+		if (ret < 0)
+			return -EBADF;
+	}
+
+	ret = pthread_sigmask(SIG_BLOCK, &mask, oldmask);
+	if (ret < 0)
+		return log_error_errno(-EBADF, errno,
+				       "Failed to set signal mask");
+
+	ret = signalfd(-1, &mask, SFD_CLOEXEC);
+	if (ret < 0)
+		return log_error_errno(-EBADF,
+				       errno, "Failed to create signal file descriptor");
+
+	TRACE("Created signal file descriptor %d", ret);
+
+	return ret;
+}
+
 int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
 	       void *exec_payload, lxc_attach_options_t *options,
 	       pid_t *attached_process, char **err_msg)
@@ -1471,6 +1521,11 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
 		int ret_parent = -1;
 		pid_t to_cleanup_pid = pid;
 		struct lxc_epoll_descr descr = {0};
+#ifdef HAVE_ISULAD
+		int isulad_sigfd;
+		sigset_t isulad_oldmask;
+		struct lxc_epoll_descr isulad_descr = {0};
+#endif
 
 		/* close unneeded file descriptors */
 		close(ipc_sockets[1]);
@@ -1541,7 +1596,12 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
 			ret = lxc_attach_terminal_mainloop_init(&terminal, &descr);
 			if (ret < 0)
 				goto on_error;
+
 #ifdef HAVE_ISULAD
+			ret = lxc_attach_terminal_mainloop_init(&terminal, &isulad_descr);
+			if (ret < 0)
+				goto on_error;
+
 			if (suffix != NULL) {
 				(void)lxc_exec_cmd_mainloop_add(&descr, &exec_command);
 			}
@@ -1549,6 +1609,16 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
 			TRACE("Initialized terminal mainloop");
 		}
 
+#ifdef HAVE_ISULAD
+		/* The signal fd has to be created before forking otherwise if the child
+		* process exits before we setup the signal fd, the event will be lost
+		* and the command will be stuck.
+		*/
+		isulad_sigfd = isulad_setup_signal_fd(&isulad_oldmask);
+		if (isulad_sigfd < 0)
+			goto close_mainloop;
+#endif
+
 		/* Let the child process know to go ahead. */
 		status = 0;
 		ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status));
@@ -1637,6 +1707,7 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
 		/* isulad: read error msg from pipe */
 		ssize_t size_read;
 		char errbuf[BUFSIZ + 1] = {0};
+		pid_t tmp_pid = *attached_process;
 
 		size_read = read(conf->errpipe[0], errbuf, BUFSIZ);
 		if (size_read > 0) {
@@ -1644,6 +1715,13 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
 				*err_msg = safe_strdup(errbuf);
 			goto close_mainloop;
 		}
+		if (options->attach_flags & LXC_ATTACH_TERMINAL) {
+			ret = lxc_mainloop_add_handler(&descr, isulad_sigfd, attach_signal_handler, &tmp_pid);
+			if (ret < 0) {
+				ERROR("Failed to add signal handler for %d to mainloop", tmp_pid);
+				goto close_mainloop;
+			}
+		}
 #endif
 
 		/* Now shut down communication with child, we're done. */
@@ -1672,13 +1750,19 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
 		}
 
 #ifdef HAVE_ISULAD
+		// do lxc_mainloop to make sure we do not lose any output
+		(void)lxc_mainloop(&isulad_descr, 100);
 		if (g_attach_timeout_state == ATTACH_TIMEOUT && err_msg != NULL && *err_msg == NULL) {
 			*err_msg = safe_strdup("Attach exceeded timeout");
 		}
 #endif
 	close_mainloop:
-		if (options->attach_flags & LXC_ATTACH_TERMINAL)
+		if (options->attach_flags & LXC_ATTACH_TERMINAL) {
+#ifdef HAVE_ISULAD
+			lxc_mainloop_close(&isulad_descr);
+#endif
 			lxc_mainloop_close(&descr);
+		}
 
 	on_error:
 		if (ipc_sockets[0] >= 0) {
diff --git a/src/lxc/terminal.c b/src/lxc/terminal.c
index 0197503..e696b4e 100644
--- a/src/lxc/terminal.c
+++ b/src/lxc/terminal.c
@@ -763,11 +763,11 @@ int lxc_terminal_io_cb(int fd, uint32_t events, void *data,
 			/* notes: do not close the master fd due to if we close the fd, the process may
 			 * recive SIGHUP and the exit code will be 141 (128 + 13)
 			 */
-			return LXC_MAINLOOP_CLOSE;
+			return LXC_MAINLOOP_CONTINUE;
 		} else if (fd == terminal->pipes[0][1]) {
 			TRACE("closed stdin pipe of container stdin");
 			terminal->pipes[0][1] = -EBADF;
-			return LXC_MAINLOOP_CLOSE;
+			return LXC_MAINLOOP_CONTINUE;
 		} else {
 			ERROR("Handler received unexpected file descriptor");
 		}
-- 
1.8.3.1

