From 1f5b601dcc5ccd9f2699495134d1adfc0473cb18 Mon Sep 17 00:00:00 2001
From: LiFeng <lifeng68@huawei.com>
Date: Mon, 14 Jan 2019 02:18:26 -0500
Subject: [PATCH 018/131] lxc-attach: add support terminal fifos

1. support terminal fifos to redirect terminal
2. support lxc-attach run in background

Signed-off-by: LiFeng <lifeng68@huawei.com>
---
 src/lxc/attach.c           |  18 +++-
 src/lxc/attach_options.h   |   3 +
 src/lxc/terminal.c         |  27 ++++--
 src/lxc/tools/arguments.h  |   2 +-
 src/lxc/tools/lxc_attach.c | 181 +++++++++++++++++++++++++++++++++----
 5 files changed, 204 insertions(+), 27 deletions(-)

diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index 2bbf1eb5..1886bdef 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -988,12 +988,23 @@ on_error:
 }
 
 static int lxc_attach_terminal(struct lxc_conf *conf,
-			       struct lxc_terminal *terminal)
+			       struct lxc_terminal *terminal, lxc_attach_options_t *options)
 {
 	int ret;
 
 	lxc_terminal_init(terminal);
 
+	/* isulad: if we pass fifo in option, use them as init fifos */
+	if (options->init_fifo[0] && options->init_fifo[1]) {
+		if (terminal->init_fifo[0])
+			free(terminal->init_fifo[0]);
+		terminal->init_fifo[0] = strdup(options->init_fifo[0]);
+
+		if (terminal->init_fifo[1])
+			free(terminal->init_fifo[1]);
+		terminal->init_fifo[1] = strdup(options->init_fifo[1]);
+	}
+
 	ret = lxc_terminal_create(terminal);
 	if (ret < 0) {
 		ERROR("Failed to create terminal");
@@ -1203,7 +1214,7 @@ int lxc_attach(const char *name, const char *lxcpath,
 	}
 
 	if (options->attach_flags & LXC_ATTACH_TERMINAL) {
-		ret = lxc_attach_terminal(conf, &terminal);
+		ret = lxc_attach_terminal(conf, &terminal, options);
 		if (ret < 0) {
 			ERROR("Failed to setup new terminal");
 			free(cwd);
@@ -1489,7 +1500,7 @@ int lxc_attach(const char *name, const char *lxcpath,
 	}
 
 	if (pid == 0) {
-		if (options->attach_flags & LXC_ATTACH_TERMINAL) {
+		if (options->attach_flags & LXC_ATTACH_TERMINAL && terminal.tty_state) {
 			ret = pthread_sigmask(SIG_SETMASK,
 					      &terminal.tty_state->oldmask, NULL);
 			if (ret < 0) {
@@ -1497,7 +1508,6 @@ int lxc_attach(const char *name, const char *lxcpath,
 				_exit(EXIT_FAILURE);
 			}
 		}
-
 		ret = attach_child_main(&payload);
 		if (ret < 0)
 			ERROR("Failed to exec");
diff --git a/src/lxc/attach_options.h b/src/lxc/attach_options.h
index 193fd7e7..081618cd 100644
--- a/src/lxc/attach_options.h
+++ b/src/lxc/attach_options.h
@@ -135,6 +135,8 @@ typedef struct lxc_attach_options_t {
 
 	/*! File descriptor to log output. */
 	int log_fd;
+
+	char *init_fifo[2]; /* isulad: default fifos for the start */
 } lxc_attach_options_t;
 
 /*! Default attach options to use */
@@ -153,6 +155,7 @@ typedef struct lxc_attach_options_t {
 		/* .stdout_fd = */      1,                                     \
 		/* .stderr_fd = */      2,                                     \
 		/* .log_fd    = */      -EBADF,                                \
+		/* .init_fifo = */	{NULL, NULL},                          \
 	}
 
 /*!
diff --git a/src/lxc/terminal.c b/src/lxc/terminal.c
index 7aa47309..ee3aef21 100644
--- a/src/lxc/terminal.c
+++ b/src/lxc/terminal.c
@@ -514,7 +514,7 @@ static int lxc_terminal_mainloop_add_peer(struct lxc_terminal *terminal)
 }
 
 /* isulad add fifo to mainloop */
-static int lxc_console_mainloop_add_fifo(struct lxc_terminal *terminal)
+static int lxc_terminal_mainloop_add_fifo(struct lxc_terminal *terminal)
 {
 	int ret = 0;
 	struct lxc_list *it,*next;
@@ -564,7 +564,7 @@ int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr,
 	}
 
 	/* isulad add fifo to mainloop */
-	ret = lxc_console_mainloop_add_fifo(terminal);
+	ret = lxc_terminal_mainloop_add_fifo(terminal);
 	if (ret < 0) {
 		ERROR("Failed to add handler for terminal fifos to mainloop");
 		return -1;
@@ -789,13 +789,28 @@ void lxc_terminal_free(struct lxc_conf *conf, int fd)
 static int lxc_terminal_peer_default(struct lxc_terminal *terminal)
 {
 	struct lxc_terminal_state *ts;
-	const char *path;
+	const char *path = NULL;
 	int ret = 0;
 
 	if (terminal->path)
 		path = terminal->path;
-	else
-		path = "/dev/tty";
+
+	/* isulad: if no console was given, try current controlling terminal, there
+	 * won't be one if we were started as a daemon (-d)
+	 */
+	if (!path && !access("/dev/tty", F_OK)) {
+		int fd;
+		fd = open("/dev/tty", O_RDWR);
+		if (fd >= 0) {
+			close(fd);
+			path = "/dev/tty";
+		}
+	}
+
+	if (!path) {
+		DEBUG("Not have a controlling terminal");
+		return 0;
+	}
 
 	terminal->peer = lxc_unpriv(open(path, O_RDWR | O_CLOEXEC));
 	if (terminal->peer < 0) {
@@ -1355,7 +1370,7 @@ int lxc_terminal_prepare_login(int fd)
 	if (ret < 0)
 		return -1;
 
-	ret = lxc_terminal_set_stdfds(fd);
+	ret = set_stdfds(fd);
 	if (ret < 0)
 		return -1;
 
diff --git a/src/lxc/tools/arguments.h b/src/lxc/tools/arguments.h
index 61f4a0a7..047e9f16 100644
--- a/src/lxc/tools/arguments.h
+++ b/src/lxc/tools/arguments.h
@@ -62,7 +62,7 @@ struct lxc_arguments {
 
 	/* for lxc-start */
 	const char *share_ns[32]; /* size must be greater than LXC_NS_MAX */
-	const char *terminal_fifos[2]; /* isulad add, fifos used to redirct stdin/out/err */
+	char *terminal_fifos[2]; /* isulad add, fifos used to redirct stdin/out/err */
 	const char *container_info; /* isulad: file used to store pid and ppid info of container */
 	const char *exit_monitor_fifo; /* isulad: fifo used to monitor state of monitor process */
 
diff --git a/src/lxc/tools/lxc_attach.c b/src/lxc/tools/lxc_attach.c
index 8c8e7d39..6d0ffe57 100644
--- a/src/lxc/tools/lxc_attach.c
+++ b/src/lxc/tools/lxc_attach.c
@@ -75,6 +75,8 @@ static const struct option my_longopts[] = {
 	{"set-var", required_argument, 0, 'v'},
 	{"pty-log", required_argument, 0, 'L'},
 	{"rcfile", required_argument, 0, 'f'},
+	{"in-fifo", required_argument, 0, OPT_INPUT_FIFO}, /* isulad add terminal fifos*/
+	{"out-fifo", required_argument, 0, OPT_OUTPUT_FIFO},
 	LXC_COMMON_OPTIONS
 };
 
@@ -133,6 +135,9 @@ Options :\n\
 	.log_file     = "none",
 };
 
+// isulad: send '128 + signal' if container is killed by signal.
+#define ExitSignalOffset 128
+
 static int my_parser(struct lxc_arguments *args, int c, char *arg)
 {
 	int ret;
@@ -190,6 +195,12 @@ static int my_parser(struct lxc_arguments *args, int c, char *arg)
 	case 'f':
 		args->rcfile = arg;
 		break;
+	case OPT_INPUT_FIFO:
+		args->terminal_fifos[0] = arg;
+		break;
+	case OPT_OUTPUT_FIFO:
+		args->terminal_fifos[1] = arg;
+		break;
 	}
 
 	return 0;
@@ -253,10 +264,143 @@ static int lxc_attach_create_log_file(const char *log_file)
 	return fd;
 }
 
+/*isulad: attach with terminal*/
+static int do_attach_foreground(struct lxc_container *c, lxc_attach_command_t *command,
+				lxc_attach_options_t *attach_options,
+				char **errmsg)
+{
+	int ret = 0;
+	pid_t pid;
+	int wexit = -1;
+	int signal;
+
+	if (command->program)
+		ret = c->attach(c, lxc_attach_run_command, command, attach_options, &pid);
+	else
+		ret = c->attach(c, lxc_attach_run_shell, NULL, attach_options, &pid);
+	if (ret < 0)
+		goto out;
+
+	ret = lxc_wait_for_pid_status(pid);
+	if (ret < 0)
+		goto out;
+
+	if (WIFEXITED(ret))
+		wexit = WEXITSTATUS(ret);
+	else
+		wexit = -1;
+
+	if (WIFSIGNALED(ret)) {
+		signal = WTERMSIG(ret);
+		wexit = ExitSignalOffset + signal;
+	}
+out:
+	//if (c->lxc_conf->errmsg)
+	//	*errmsg = strdup(c->lxc_conf->errmsg);
+	return wexit;
+}
+
+static void close_msg_pipe(int *errpipe)
+{
+	if (errpipe[0] >= 0) {
+		close(errpipe[0]);
+		errpipe[0] = -1;
+	}
+	if (errpipe[1] >= 0) {
+		close(errpipe[1]);
+		errpipe[1] = -1;
+	}
+}
+
+/*isulad: attach without terminal in background */
+static int do_attach_background(struct lxc_container *c, lxc_attach_command_t *command,
+				lxc_attach_options_t *attach_options,
+				char **errmsg)
+{
+	int ret = 0;
+	int msgpipe[2];
+	pid_t pid = 0;
+	ssize_t size_read;
+	char msgbuf[BUFSIZ + 1] = {0};
+
+	//pipdfd for get error message of child or grandchild process.
+	if (pipe2(msgpipe, O_CLOEXEC) != 0) {
+		SYSERROR("Failed to init msgpipe");
+		return -1;
+	}
+
+	pid = fork();
+	if (pid < 0) {
+		close_msg_pipe(msgpipe);
+		return -1;
+	}
+
+	if (pid != 0) {
+		close(msgpipe[1]);
+		msgpipe[1] = -1;
+		size_read = read(msgpipe[0], msgbuf, BUFSIZ);
+		if (size_read > 0) {
+			*errmsg = strdup(msgbuf);
+			ret = -1;
+		}
+
+		close(msgpipe[0]);
+		msgpipe[0] = -1;
+
+		return ret;
+	}
+
+	/* second fork to be reparented by init */
+	pid = fork();
+	if (pid < 0) {
+		SYSERROR("Error doing dual-fork");
+		close_msg_pipe(msgpipe);
+		exit(1);
+	}
+	if (pid != 0) {
+		close_msg_pipe(msgpipe);
+		exit(0);
+	}
+
+	close(msgpipe[0]);
+	msgpipe[0] = -1;
+
+	if (null_stdfds() < 0) {
+		ERROR("failed to close fds");
+		exit(1);
+	}
+	setsid();
+
+	if (command->program)
+		ret = c->attach(c, lxc_attach_run_command, command, attach_options, &pid);
+	else
+		ret = c->attach(c, lxc_attach_run_shell, NULL, attach_options, &pid);
+	if (ret < 0) {
+		//if (c->lxc_conf->errmsg)
+		//	lxc_write_error_message(msgpipe[1], "%s", c->lxc_conf->errmsg);
+		close(msgpipe[1]);
+		msgpipe[1] = -1;
+		ret = -1;
+		goto out;
+	}
+
+	close(msgpipe[1]);
+	msgpipe[1] = -1;
+
+	ret = wait_for_pid(pid);
+out:
+	lxc_container_put(c);
+	if (ret)
+		exit(EXIT_FAILURE);
+	else
+		exit(0);
+}
+
 int main(int argc, char *argv[])
 {
 	int ret = -1;
 	int wexit = 0;
+	char *errmsg = NULL;
 	struct lxc_log log;
 	pid_t pid;
 	lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
@@ -316,8 +460,13 @@ int main(int argc, char *argv[])
 	if (elevated_privileges)
 		attach_options.attach_flags &= ~(elevated_privileges);
 
-	if (stdfd_is_pty())
+	if (my_args.terminal_fifos[0] && my_args.terminal_fifos[1]) {
+		attach_options.init_fifo[0] = my_args.terminal_fifos[0];
+		attach_options.init_fifo[1] = my_args.terminal_fifos[1];
+		attach_options.attach_flags |= LXC_ATTACH_TERMINAL;
+	} else if (stdfd_is_pty()) {
 		attach_options.attach_flags |= LXC_ATTACH_TERMINAL;
+	}
 
 	attach_options.namespaces = namespace_flags;
 	attach_options.personality = new_personality;
@@ -332,27 +481,27 @@ int main(int argc, char *argv[])
 
 	if (my_args.console_log) {
 		attach_options.log_fd = lxc_attach_create_log_file(my_args.console_log);
-		if (attach_options.log_fd < 0)
-			goto out;
+		if (attach_options.log_fd < 0) {
+			ERROR("Failed to create log file for %s", c->name);
+			lxc_container_put(c);
+			exit(EXIT_FAILURE);
+		}
 	}
 
-	if (command.program)
-		ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, &pid);
+	/* isulad: add do attach background */
+	if (attach_options.attach_flags & LXC_ATTACH_TERMINAL)
+		wexit = do_attach_foreground(c, &command, &attach_options, &errmsg);
 	else
-		ret = c->attach(c, lxc_attach_run_shell, NULL, &attach_options, &pid);
-	if (ret < 0)
-		goto out;
+		wexit = do_attach_background(c, &command, &attach_options, &errmsg);
 
-	ret = lxc_wait_for_pid_status(pid);
-	if (ret < 0)
-		goto out;
-
-	if (WIFEXITED(ret))
-		wexit = WEXITSTATUS(ret);
+	if (errmsg) {
+		fprintf(stderr, "%s:%s:%s:%d starting container process caused \"%s\"", c->name,
+		__FILE__, __func__, __LINE__, errmsg);
+		free(errmsg);
+	}
 
-out:
 	lxc_container_put(c);
-	if (ret >= 0)
+	if (wexit >= 0)
 		exit(wexit);
 
 	exit(EXIT_FAILURE);
-- 
2.23.0

