From 06b071d6bc7683023d3131d4e428210005333c8c Mon Sep 17 00:00:00 2001
From: chengzrz <czrzrichard@gmail.com>
Date: Wed, 20 Jul 2022 15:03:45 +0800
Subject: [PATCH] refactor patches on terminal.c, start.c and so on

Signed-off-by: chengzrz <czrzrichard@gmail.com>
---
 hooks/Makefile.am         |   3 +
 src/lxc/attach.h          |   6 +
 src/lxc/cgroups/cgroup.c  |   4 +
 src/lxc/commands_utils.h  |   6 +
 src/lxc/initutils.c       |   4 +
 src/lxc/lsm/lsm.h         |   8 +
 src/lxc/lxclock.h         |   4 +
 src/lxc/mainloop.h        |   4 +
 src/lxc/start.c           | 958 ++++++++++++++++++++++++++++++++++++
 src/lxc/storage/btrfs.c   |  11 +
 src/lxc/storage/overlay.c |   8 +
 src/lxc/sync.h            |   4 +
 src/lxc/terminal.c        | 990 ++++++++++++++++++++++++++++++++++++++
 src/tests/Makefile.am     |   4 +
 src/tests/attach.c        |  11 +
 15 files changed, 2025 insertions(+)

diff --git a/hooks/Makefile.am b/hooks/Makefile.am
index 5ae73d7..ddfd4bc 100644
--- a/hooks/Makefile.am
+++ b/hooks/Makefile.am
@@ -10,6 +10,8 @@ hooks_SCRIPTS = \
 	squid-deb-proxy-client \
 	nvidia
 
+
+if !HAVE_ISULAD
 binhooks_PROGRAMS = \
 	unmount-namespace
 
@@ -20,5 +22,6 @@ if IS_BIONIC
 unmount_namespace_SOURCES += \
 	../src/include/lxcmntent.c ../src/include/lxcmntent.h
 endif
+endif
 
 EXTRA_DIST=$(hooks_SCRIPTS)
diff --git a/src/lxc/attach.h b/src/lxc/attach.h
index ef5a6c1..8316344 100644
--- a/src/lxc/attach.h
+++ b/src/lxc/attach.h
@@ -20,9 +20,15 @@ struct lxc_proc_context_info {
 	int ns_fd[LXC_NS_MAX];
 };
 
+#ifdef HAVE_ISULAD
+extern 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);
+#else
 extern 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);
+#endif
 
 extern int lxc_attach_remount_sys_proc(void);
 
diff --git a/src/lxc/cgroups/cgroup.c b/src/lxc/cgroups/cgroup.c
index 7c94fd8..5aa2c3c 100644
--- a/src/lxc/cgroups/cgroup.c
+++ b/src/lxc/cgroups/cgroup.c
@@ -31,7 +31,11 @@ struct cgroup_ops *cgroup_init(struct lxc_conf *conf)
 	if (!cgroup_ops)
 		return log_error_errno(NULL, errno, "Failed to initialize cgroup driver");
 
+#ifdef HAVE_ISULAD
+	if (cgroup_ops->data_init(cgroup_ops, conf)) {
+#else
 	if (cgroup_ops->data_init(cgroup_ops)) {
+#endif
 		cgroup_exit(cgroup_ops);
 		return log_error_errno(NULL, errno,
 				       "Failed to initialize cgroup data");
diff --git a/src/lxc/commands_utils.h b/src/lxc/commands_utils.h
index 3ef7920..c836ead 100644
--- a/src/lxc/commands_utils.h
+++ b/src/lxc/commands_utils.h
@@ -65,4 +65,10 @@ extern int lxc_add_state_client(int state_client_fd,
 extern int lxc_cmd_connect(const char *name, const char *lxcpath,
 			   const char *hashed_sock_name, const char *suffix);
 
+#ifdef HAVE_ISULAD
+extern char *generate_named_unix_sock_dir(const char *name);
+extern int generate_named_unix_sock_path(const char *container_name,
+				const char *sock_name, char *out_path, size_t len);
+#endif
+
 #endif /* __LXC_COMMANDS_UTILS_H */
diff --git a/src/lxc/initutils.c b/src/lxc/initutils.c
index 5549c2e..76f0048 100644
--- a/src/lxc/initutils.c
+++ b/src/lxc/initutils.c
@@ -54,11 +54,15 @@ const char *lxc_global_config_value(const char *option_name)
 		{ NULL, NULL },
 	};
 
+#ifdef HAVE_ISULAD
+	static const char *values[sizeof(options) / sizeof(options[0])] = {0};
+#else
 	/* placed in the thread local storage pool for non-bionic targets */
 #ifdef HAVE_TLS
 	static thread_local const char *values[sizeof(options) / sizeof(options[0])] = {0};
 #else
 	static const char *values[sizeof(options) / sizeof(options[0])] = {0};
+#endif
 #endif
 
 	/* user_config_path is freed as soon as it is used */
diff --git a/src/lxc/lsm/lsm.h b/src/lxc/lsm/lsm.h
index ee578bb..4872f55 100644
--- a/src/lxc/lsm/lsm.h
+++ b/src/lxc/lsm/lsm.h
@@ -17,6 +17,10 @@ struct lsm_drv {
 	char *(*process_label_get)(pid_t pid);
 	int (*process_label_set)(const char *label, struct lxc_conf *conf,
 				 bool on_exec);
+#ifdef HAVE_ISULAD
+	int (*file_label_set)(const char *path, const char *label);
+	int (*relabel)(const char *path, const char *label, bool share);
+#endif
 	int (*keyring_label_set)(char* label);
 	int (*prepare)(struct lxc_conf *conf, const char *lxcpath);
 	void (*cleanup)(struct lxc_conf *conf, const char *lxcpath);
@@ -32,6 +36,10 @@ extern int lsm_process_label_set(const char *label, struct lxc_conf *conf,
 extern int lsm_process_label_fd_get(pid_t pid, bool on_exec);
 extern int lsm_process_label_set_at(int label_fd, const char *label,
 				    bool on_exec);
+#ifdef HAVE_ISULAD
+extern int lsm_file_label_set(const char *path, const char *label);
+extern int lsm_relabel(const char *path, const char *label, bool share);
+#endif
 extern void lsm_process_cleanup(struct lxc_conf *conf, const char *lxcpath);
 extern int lsm_keyring_label_set(char *label);
 
diff --git a/src/lxc/lxclock.h b/src/lxc/lxclock.h
index 9f9bc3b..6a71d7c 100644
--- a/src/lxc/lxclock.h
+++ b/src/lxc/lxclock.h
@@ -154,4 +154,8 @@ extern int container_disk_lock(struct lxc_container *c);
  */
 extern void container_disk_unlock(struct lxc_container *c);
 
+#ifdef HAVE_ISULAD
+int container_disk_removelock(struct lxc_container *c);
+#endif
+
 #endif
diff --git a/src/lxc/mainloop.h b/src/lxc/mainloop.h
index e6ab9a6..aa41a93 100644
--- a/src/lxc/mainloop.h
+++ b/src/lxc/mainloop.h
@@ -38,4 +38,8 @@ extern void lxc_mainloop_close(struct lxc_epoll_descr *descr);
 
 define_cleanup_function(struct lxc_epoll_descr *, lxc_mainloop_close);
 
+#ifdef HAVE_ISULAD
+extern int isulad_safe_mainloop(struct lxc_epoll_descr *descr, int timeout_ms);
+#endif
+
 #endif
diff --git a/src/lxc/start.c b/src/lxc/start.c
index fd969c4..f82df34 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -304,7 +304,11 @@ static int setup_signal_fd(sigset_t *oldmask)
 {
 	int ret;
 	sigset_t mask;
+#ifdef HAVE_ISULAD
+	const int signals[] = {SIGBUS, SIGILL, SIGSEGV, SIGWINCH, SIGTERM};
+#else
 	const int signals[] = {SIGBUS, SIGILL, SIGSEGV, SIGWINCH};
+#endif
 
 	/* Block everything except serious error signals. */
 	ret = sigfillset(&mask);
@@ -590,13 +594,27 @@ int lxc_poll(const char *name, struct lxc_handler *handler)
 
 	TRACE("Mainloop is ready");
 
+#ifdef HAVE_ISULAD
+	// iSulad: close stdin pipe if we do not want open_stdin with container stdin
+	if (!handler->conf->console.open_stdin) {
+		if (handler->conf->console.pipes[0][1] > 0) {
+			close(handler->conf->console.pipes[0][1]);
+			handler->conf->console.pipes[0][1] = -1;
+		}
+	}
+#endif
+
 	ret = lxc_mainloop(&descr, -1);
 	close_prot_errno_disarm(descr.epfd);
 	if (ret < 0 || !handler->init_died)
 		goto out_mainloop_console;
 
 	if (has_console)
+#ifdef HAVE_ISULAD
+		ret = isulad_safe_mainloop(&descr_console, 100);
+#else
 		ret = lxc_mainloop(&descr_console, 0);
+#endif
 
 out_mainloop_console:
 	if (has_console) {
@@ -637,7 +655,9 @@ struct lxc_handler *lxc_init_handler(struct lxc_handler *old,
 				     const char *name, struct lxc_conf *conf,
 				     const char *lxcpath, bool daemonize)
 {
+#ifndef HAVE_ISULAD
 	int nr_keep_fds = 0;
+#endif
 	int ret;
 	struct lxc_handler *handler;
 
@@ -671,6 +691,12 @@ struct lxc_handler *lxc_init_handler(struct lxc_handler *old,
 		handler->nsfd[i] = -EBADF;
 
 	handler->name = name;
+
+#ifdef HAVE_ISULAD
+	handler->exit_code = -1; /* isulad: record exit code of container */
+	handler->image_type_oci = false;
+#endif
+
 	if (daemonize)
 		handler->transient_pid = lxc_raw_getpid();
 	else
@@ -691,8 +717,10 @@ struct lxc_handler *lxc_init_handler(struct lxc_handler *old,
 		TRACE("Created anonymous pair {%d,%d} of unix sockets",
 		      handler->state_socket_pair[0],
 		      handler->state_socket_pair[1]);
+#ifndef HAVE_ISULAD
 		handler->keep_fds[nr_keep_fds++] = handler->state_socket_pair[0];
 		handler->keep_fds[nr_keep_fds++] = handler->state_socket_pair[1];
+#endif
 	}
 
 	if (handler->conf->reboot == REBOOT_NONE) {
@@ -701,7 +729,9 @@ struct lxc_handler *lxc_init_handler(struct lxc_handler *old,
 			ERROR("Failed to set up command socket");
 			goto on_error;
 		}
+#ifndef HAVE_ISULAD
 		handler->keep_fds[nr_keep_fds++] = handler->conf->maincmd_fd;
+#endif
 	}
 
 	TRACE("Unix domain socket %d for command server is ready",
@@ -721,6 +751,10 @@ int lxc_init(const char *name, struct lxc_handler *handler)
 	int ret;
 	const char *loglevel;
 	struct lxc_conf *conf = handler->conf;
+#ifdef HAVE_ISULAD
+	conf->console.disable_pty = handler->disable_pty;
+	conf->console.open_stdin = handler->open_stdin;
+#endif
 
 	handler->monitor_pid = lxc_raw_getpid();
 	status_fd = open("/proc/self/status", O_RDONLY | O_CLOEXEC);
@@ -810,6 +844,9 @@ int lxc_init(const char *name, struct lxc_handler *handler)
 	ret = lxc_terminal_setup(conf);
 	if (ret < 0) {
 		ERROR("Failed to create console");
+#ifdef HAVE_ISULAD
+		lxc_write_error_message(conf->errpipe[1], "Failed to create console for container \"%s\".", name);
+#endif
 		goto out_restore_sigmask;
 	}
 	TRACE("Created console");
@@ -853,6 +890,185 @@ out_restore_sigmask:
 	return -1;
 }
 
+#ifdef HAVE_ISULAD
+/* isulad: start timeout thread */
+typedef enum {
+	START_INIT,
+	START_TIMEOUT,
+	START_MAX,
+} start_timeout_t;
+
+static start_timeout_t global_timeout_state = START_INIT;
+static sem_t global_timeout_sem;
+
+struct start_timeout_conf {
+	unsigned int timeout;
+	int errfd;
+};
+
+void trim_line(char *s)
+{
+	size_t len;
+
+	len = strlen(s);
+	while ((len > 1) && (s[len - 1] == '\n'))
+		s[--len] = '\0';
+}
+
+static int _read_procs_file(const char *path, pid_t **pids, size_t *len)
+{
+	FILE *f;
+	char *line = NULL;
+	size_t sz = 0;
+	pid_t *tmp_pids = NULL;
+
+	f = fopen_cloexec(path, "r");
+	if (!f)
+		return -1;
+
+	while (getline(&line, &sz, f) != -1) {
+		pid_t pid;
+		trim_line(line);
+		pid = (pid_t)atoll(line);
+		if (lxc_mem_realloc((void **)&tmp_pids, sizeof(pid_t) * (*len + 1), *pids, sizeof(pid_t) * (*len)) != 0) {
+			free(*pids);
+			*pids = NULL;
+			ERROR("out of memory");
+			free(line);
+			fclose(f);
+			return -1;
+		}
+		*pids = tmp_pids;
+
+		(*pids)[*len] = pid;
+		(*len)++;
+	}
+
+	free(line);
+	fclose(f);
+	return 0;
+}
+
+static int _recursive_read_cgroup_procs(const char *dirpath, pid_t **pids, size_t *len)
+{
+	struct dirent *direntp = NULL;
+	DIR *dir = NULL;
+	int ret, failed = 0;
+	char pathname[PATH_MAX];
+
+	dir = opendir(dirpath);
+	if (dir == NULL) {
+		WARN("Failed to open \"%s\"", dirpath);
+		return 0;
+	}
+
+	while ((direntp = readdir(dir))) {
+		struct stat mystat;
+		int rc;
+
+		if (!strcmp(direntp->d_name, ".") ||
+		                !strcmp(direntp->d_name, ".."))
+			continue;
+
+		rc = snprintf(pathname, PATH_MAX, "%s/%s", dirpath, direntp->d_name);
+		if (rc < 0 || rc >= PATH_MAX) {
+			failed = 1;
+			continue;
+		}
+
+		if (strcmp(direntp->d_name, "cgroup.procs") == 0) {
+			if (_read_procs_file(pathname, pids, len)) {
+				failed = 1;
+
+			}
+			continue;
+		}
+
+		ret = lstat(pathname, &mystat);
+		if (ret) {
+			failed = 1;
+			continue;
+		}
+
+		if (S_ISDIR(mystat.st_mode)) {
+			if (_recursive_read_cgroup_procs(pathname, pids, len) < 0)
+				failed = 1;
+		}
+	}
+
+	ret = closedir(dir);
+	if (ret) {
+		WARN("Failed to close directory \"%s\"", dirpath);
+		failed = 1;
+	}
+
+	return failed ? -1 : 0;
+}
+
+int get_all_pids(struct cgroup_ops *cg_ops, pid_t **pids, size_t *len)
+{
+	const char *devices_path = NULL;
+
+	devices_path = cg_ops->get_cgroup_full_path(cg_ops, "devices");
+	if (!file_exists(devices_path)) {
+		return 0;
+	}
+
+	return _recursive_read_cgroup_procs(devices_path, pids, len);
+}
+
+static int set_cgroup_freezer(struct cgroup_ops *cg_ops, const char *value)
+{
+	char *fullpath;
+	int ret;
+
+	fullpath = must_make_path(cg_ops->get_cgroup_full_path(cg_ops, "freezer"), "freezer.state", NULL);
+	ret = lxc_write_to_file(fullpath, value, strlen(value), false, 0666);
+	free(fullpath);
+	return ret;
+}
+
+/* isulad: kill all process in container cgroup path */
+static void signal_all_processes(struct lxc_handler *handler)
+{
+	int ret;
+	struct cgroup_ops *cg_ops = handler->cgroup_ops;
+	pid_t *pids = NULL;
+	size_t len = 0, i;
+
+	ret = set_cgroup_freezer(cg_ops, "FROZEN");
+	if (ret < 0 && errno != ENOENT) {
+		WARN("cgroup_set frozen failed");
+	}
+
+	ret = get_all_pids(cg_ops, &pids, &len);
+	if (ret < 0) {
+		WARN("failed to get all pids");
+	}
+
+	for (i = 0; i < len; i++) {
+		ret = kill(pids[i], SIGKILL);
+		if (ret < 0 && errno != ESRCH) {
+			WARN("Can not kill process (pid=%d) with SIGKILL for container %s", pids[i], handler->name);
+		}
+	}
+
+	ret = set_cgroup_freezer(cg_ops, "THAWED");
+	if (ret < 0 && errno != ENOENT) {
+		WARN("cgroup_set thawed failed");
+	}
+
+	for (i = 0; i < len; i++) {
+		ret = lxc_wait_for_pid_status(pids[i]);
+		if (ret < 0 && errno != ECHILD) {
+			WARN("Failed to wait pid %d for container %s: %s", pids[i], handler->name, strerror(errno));
+		}
+	}
+
+	free(pids);
+}
+#endif
+
 void lxc_end(struct lxc_handler *handler)
 {
 	int ret;
@@ -926,6 +1142,33 @@ void lxc_end(struct lxc_handler *handler)
 
 	lsm_process_cleanup(handler->conf, handler->lxcpath);
 
+#ifdef HAVE_ISULAD
+	// close maincmd fd before destroy cgroup for isulad
+	if (handler->conf->reboot == REBOOT_NONE) {
+		/* For all new state clients simply close the command socket.
+		 * This will inform all state clients that the container is
+		 * STOPPED and also prevents a race between a open()/close() on
+		 * the command socket causing a new process to get ECONNREFUSED
+		 * because we haven't yet closed the command socket.
+		 */
+		close_prot_errno_disarm(handler->conf->maincmd_fd);
+		TRACE("Closed command socket");
+	}
+	int retry_count = 0;
+	int max_retry = 10;
+retry:
+	if (cgroup_ops != NULL && !cgroup_ops->payload_destroy(cgroup_ops, handler)) {
+	        TRACE("Trying to kill all subprocess");
+	        signal_all_processes(handler);
+	        TRACE("Finished kill all subprocess");
+	        if (retry_count < max_retry) {
+				usleep(100 * 1000); /* 100 millisecond */
+				retry_count++;
+				goto retry;
+		}
+		SYSERROR("Failed to destroy cgroup path for container: \"%s\"", handler->name);
+	}
+#else
 	if (cgroup_ops) {
 		cgroup_ops->payload_destroy(cgroup_ops, handler);
 		cgroup_ops->monitor_destroy(cgroup_ops, handler);
@@ -940,12 +1183,25 @@ void lxc_end(struct lxc_handler *handler)
 		 */
 		close_prot_errno_disarm(handler->conf->maincmd_fd);
 		TRACE("Closed command socket");
+	}
+#endif
 
+	if (handler->conf->reboot == REBOOT_NONE) {
 		/* This function will try to connect to the legacy lxc-monitord
 		 * state server and only exists for backwards compatibility.
 		 */
 		lxc_monitor_send_state(name, STOPPED, handler->lxcpath);
 
+#ifdef HAVE_ISULAD
+		/* isuald: write exit code to exit fifo */
+		if (handler->conf->exit_fd >= 0) {
+			ret = write(handler->conf->exit_fd, &handler->exit_code, sizeof(int));
+			if (ret != sizeof(int)) {
+				SYSERROR("Failed to write to exit code to exit fifo.");
+			}
+		}
+#endif
+
 		/* The command socket is closed so no one can acces the command
 		 * socket anymore so there's no need to lock it.
 		 */
@@ -1042,6 +1298,25 @@ static int do_start(void *data)
 
 	lxc_sync_fini_parent(handler);
 
+#ifdef HAVE_ISULAD
+	sigset_t mask;
+
+	/*isulad: restore default signal handlers and unblock all signals*/
+	for (int i = 1; i < NSIG; i++)
+		signal(i, SIG_DFL);
+
+	ret = sigfillset(&mask);
+	if (ret < 0) {
+		SYSERROR("Failed to fill signal mask");
+		goto out_warn_father;
+	}
+	ret = sigprocmask(SIG_UNBLOCK, &mask, NULL);
+	if (ret < 0) {
+		SYSERROR("Failed to set signal mask");
+		goto out_warn_father;
+	}
+#endif
+
 	if (lxc_abstract_unix_recv_fds(data_sock1, &status_fd, 1, NULL, 0) < 0) {
 		ERROR("Failed to receive status file descriptor to child process");
 		goto out_warn_father;
@@ -1155,7 +1430,11 @@ static int do_start(void *data)
 	 * means that migration won't work, but at least we won't spew output
 	 * where it isn't wanted.
 	 */
+#ifdef HAVE_ISULAD
+	if (!handler->disable_pty && handler->daemonize && !handler->conf->autodev) {
+#else
 	if (handler->daemonize && !handler->conf->autodev) {
+#endif
 		char path[PATH_MAX];
 
 		ret = snprintf(path, sizeof(path), "%s/dev/null",
@@ -1221,6 +1500,9 @@ static int do_start(void *data)
 	/* Setup the container, ip, names, utsname, ... */
 	ret = lxc_setup(handler);
 	if (ret < 0) {
+#ifdef HAVE_ISULAD
+		lxc_write_error_message(handler->conf->errpipe[1], "Failed to setup lxc, please check the config file.");
+#endif
 		ERROR("Failed to setup container \"%s\"", handler->name);
 		goto out_warn_father;
 	}
@@ -1243,12 +1525,70 @@ static int do_start(void *data)
 		DEBUG("Set PR_SET_NO_NEW_PRIVS to block execve() gainable privileges");
 	}
 
+#ifdef HAVE_ISULAD
+	/* isulad: dup2 pipe[0][0] to container stdin, pipe[1][1] to container stdout, pipe[2][1] to container stderr */
+	if (handler->disable_pty) {
+		if (handler->conf->console.pipes[0][1] >= 0) {
+			close(handler->conf->console.pipes[0][1]);
+			handler->conf->console.pipes[0][1] = -1;
+		}
+
+		if (handler->conf->console.pipes[0][0] >= 0) {
+			ret = dup2(handler->conf->console.pipes[0][0], STDIN_FILENO);
+			if (ret < 0)
+				goto out_warn_father;
+		}
+
+		if (handler->conf->console.pipes[1][0] >= 0) {
+			close(handler->conf->console.pipes[1][0]);
+			handler->conf->console.pipes[1][0] = -1;
+		}
+
+		if (handler->conf->console.pipes[1][1] >= 0) {
+			ret = dup2(handler->conf->console.pipes[1][1], STDOUT_FILENO);
+			if (ret < 0)
+				goto out_warn_father;
+		}
+		if (handler->conf->console.pipes[2][0] >= 0) {
+			close(handler->conf->console.pipes[2][0]);
+			handler->conf->console.pipes[2][0] = -1;
+		}
+
+		if (handler->conf->console.pipes[2][1] >= 0) {
+			ret = dup2(handler->conf->console.pipes[2][1], STDERR_FILENO);
+			if (ret < 0)
+				goto out_warn_father;
+		}
+	}
+#endif
+
 	/* Some init's such as busybox will set sane tty settings on stdin,
 	 * stdout, stderr which it thinks is the console. We already set them
 	 * the way we wanted on the real terminal, and we want init to do its
 	 * setup on its console ie. the pty allocated in lxc_terminal_setup() so
 	 * make sure that that pty is stdin,stdout,stderr.
 	 */
+#ifdef HAVE_ISULAD
+	setsid();
+	if (!handler->disable_pty && handler->conf->console.pts >= 0) {
+		/* isulad:make the given terminal as controlling terminal to avoid warning
+		* sh: cannot set terminal process group (-1): Inappropriate ioctl for device
+		* sh: no job control in this shell */
+		if (ioctl(handler->conf->console.pts, TIOCSCTTY, NULL) < 0) {
+			ERROR("Faild to make the given terminal the controlling terminal of the calling process");
+			goto out_warn_father;
+		}
+		if (handler->daemonize || !handler->conf->is_execute)
+			ret = set_stdfds(handler->conf->console.pts);
+		else
+			ret = lxc_terminal_set_stdfds(handler->conf->console.pts);
+		if (ret < 0) {
+			ERROR("Failed to redirect std{in,out,err} to pty file "
+			      "descriptor %d", handler->conf->console.pts);
+			goto out_warn_father;
+		}
+	}
+#else
 	 if (handler->conf->console.pts >= 0) {
 		 if (handler->daemonize || !handler->conf->is_execute)
 			 ret = set_stdfds(handler->conf->console.pts);
@@ -1260,6 +1600,7 @@ static int do_start(void *data)
 			goto out_warn_father;
 		 }
 	 }
+#endif
 
 	/* If we mounted a temporary proc, then unmount it now. */
 	tmp_proc_unmount(handler->conf);
@@ -1283,6 +1624,21 @@ static int do_start(void *data)
 
 	close_prot_errno_disarm(handler->sigfd);
 
+#ifdef HAVE_ISULAD
+	if (!handler->disable_pty && handler->conf->console.pts < 0 && handler->daemonize) {
+		if (devnull_fd < 0) {
+			devnull_fd = open_devnull();
+			if (devnull_fd < 0)
+				goto out_warn_father;
+		}
+
+		ret = set_stdfds(devnull_fd);
+		if (ret < 0) {
+			ERROR("Failed to redirect std{in,out,err} to \"/dev/null\"");
+			goto out_warn_father;
+		}
+	}
+#else
 	if (handler->conf->console.pts < 0 && handler->daemonize) {
 		if (devnull_fd < 0) {
 			devnull_fd = open_devnull();
@@ -1296,12 +1652,25 @@ static int do_start(void *data)
 			goto out_warn_father;
 		}
 	}
+#endif
 
 	close_prot_errno_disarm(devnull_fd);
 
+#ifndef HAVE_ISULAD
 	setsid();
 
+#endif
 	if (handler->conf->init_cwd) {
+#ifdef HAVE_ISULAD
+		/* try to craete workdir if not exist */
+		struct stat st;
+		if (stat(handler->conf->init_cwd, &st) < 0 && mkdir_p(handler->conf->init_cwd, 0755) < 0) {
+			SYSERROR("Try to create directory \"%s\" as workdir failed", handler->conf->init_cwd);
+			lxc_write_error_message(handler->conf->errpipe[1], "%s:%d: Failed to create workdir: %s.",
+			                        __FILE__, __LINE__, strerror(errno));
+			goto out_warn_father;
+		}
+#endif
 		ret = chdir(handler->conf->init_cwd);
 		if (ret < 0) {
 			SYSERROR("Could not change directory to \"%s\"",
@@ -1345,6 +1714,13 @@ static int do_start(void *data)
 		}
 	}
 
+#ifdef HAVE_ISULAD
+	if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+		SYSERROR("Failed to keep permitted capabilities");
+		goto out_warn_father;
+	}
+#endif
+
 	/* The container has been setup. We can now switch to an unprivileged
 	 * uid/gid.
 	 */
@@ -1358,6 +1734,13 @@ static int do_start(void *data)
 	if (new_gid == nsgid)
 		new_gid = LXC_INVALID_GID;
 
+#ifdef HAVE_ISULAD
+	// isulad: set env home in container
+	if (lxc_setup_env_home(new_uid) < 0) {
+		goto out_warn_father;
+	}
+#endif
+
 	/* Make sure that the processes STDIO is correctly owned by the user that we are switching to */
 	ret = fix_stdio_permissions(new_uid);
 	if (ret)
@@ -1371,8 +1754,16 @@ static int do_start(void *data)
 		#if HAVE_LIBCAP
 		if (lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE))
 		#endif
+#ifdef HAVE_ISULAD
+			/* isulad: set groups for init process, and before we set uid and gid */
+			if (!lxc_setgroups(handler->conf->init_groups_len, handler->conf->init_groups)) {
+				ERROR("Can not set groups");
+				goto out_warn_father;
+				}
+#else
 			if (!lxc_setgroups(0, NULL))
 				goto out_warn_father;
+#endif
 
 	if (!lxc_switch_uid_gid(new_uid, new_gid))
 		goto out_warn_father;
@@ -1383,6 +1774,19 @@ static int do_start(void *data)
 		goto out_warn_father;
 	}
 
+#ifdef HAVE_ISULAD
+	/* isulad: drop the cap of current process */
+	if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
+		SYSERROR("Failed to clear permitted capabilities");
+		goto out_warn_father;
+	}
+
+	if (lxc_drop_caps(handler->conf)) {
+		SYSERROR("Failed to drop caps");
+		goto out_warn_father;
+	}
+#endif
+
 	if (handler->conf->monitor_signal_pdeath != SIGKILL) {
 		ret = lxc_set_death_signal(handler->conf->monitor_signal_pdeath,
 					   handler->monitor_pid, status_fd);
@@ -1397,7 +1801,12 @@ static int do_start(void *data)
 	 * After this call, we are in error because this ops should not return
 	 * as it execs.
 	 */
+#ifdef HAVE_ISULAD
+	close_prot_errno_disarm(status_fd);
+	handler->ops->start(handler, handler->data, handler->daemonize ? handler->conf->errpipe[1] : -1);
+#else
 	handler->ops->start(handler, handler->data);
+#endif
 
 out_warn_father:
 	/*
@@ -1529,6 +1938,94 @@ static inline int do_share_ns(void *arg)
 	return 0;
 }
 
+#ifdef HAVE_ISULAD
+static int lxc_write_container_info(char *filename, pid_t pid, pid_t p_pid,
+		unsigned long long start_at, unsigned long long p_start_at)
+{
+	FILE *pid_fp = NULL;
+	int ret = 0;
+
+	pid_fp = lxc_fopen(filename, "w");
+	if (pid_fp == NULL) {
+		SYSERROR("Failed to create pidfile '%s'",filename);
+		ret = -1;
+		goto out;
+	}
+
+	if (fprintf(pid_fp, "%d %llu %d %llu\n", pid, start_at, p_pid, p_start_at) < 0) {
+		SYSERROR("Failed to write '%s'", filename);
+		ret = -1;
+		goto out;
+	}
+out:
+	if (pid_fp)
+		fclose(pid_fp);
+	pid_fp = NULL;
+	return ret;
+}
+
+static int lxc_check_container_info(char *filename, pid_t pid, pid_t p_pid,
+		unsigned long long start_at, unsigned long long p_start_at)
+{
+	int ret = 0;
+	int num;
+	char sbuf[1024] = {0}; /* bufs for stat */
+	int saved_pid;		/* process id */
+	int saved_ppid;		/* pid of parent process */
+	unsigned long long saved_start_time;	/* start time of process -- seconds since 1-1-70 */
+	unsigned long long saved_pstart_time;	/* start time of parent process -- seconds since 1-1-70 */
+
+	if ((lxc_file2str(filename, sbuf, sizeof(sbuf))) == -1) {
+		SYSERROR("Failed to read pidfile %s", filename);
+		ret = -1;
+		goto out;
+	}
+
+	num = sscanf(sbuf, "%d %Lu %d %Lu", &saved_pid, &saved_start_time, &saved_ppid, &saved_pstart_time);
+	if (num != 4) {
+		SYSERROR("Call sscanf error");
+		ret = -1;
+		goto out;
+	}
+
+	if (pid != saved_pid || p_pid != saved_ppid
+	                || start_at != saved_start_time || p_start_at != saved_pstart_time) {
+		ERROR("Check container info failed");
+		ret = -1;
+		goto out;
+	}
+
+out:
+	return ret;
+}
+
+/* isuald: save pid/ppid info */
+static int lxc_save_container_info(char *filename, pid_t pid)
+{
+	int ret = 0;
+	pid_t p_pid = 0;
+	unsigned long long start_at = 0;
+	unsigned long long p_start_at = 0;
+
+	start_at = lxc_get_process_startat(pid);
+	p_pid = getpid();
+	p_start_at = lxc_get_process_startat(p_pid);
+
+	ret = lxc_write_container_info(filename, pid, p_pid, start_at, p_start_at);
+	if (ret != 0) {
+		goto out;
+	}
+
+	ret = lxc_check_container_info(filename, pid, p_pid, start_at, p_start_at);
+	if (ret != 0) {
+		goto out;
+	}
+
+out:
+	return ret;
+}
+#endif
+
 /* lxc_spawn() performs crucial setup tasks and clone()s the new process which
  * exec()s the requested container binary.
  * Note that lxc_spawn() runs in the parent namespaces. Any operations performed
@@ -1595,7 +2092,11 @@ static int lxc_spawn(struct lxc_handler *handler)
 	 * it readonly.
 	 * If the container is unprivileged then skip rootfs pinning.
 	 */
+#ifdef HAVE_ISULAD
+	if (!wants_to_map_ids && !handler->image_type_oci) {
+#else
 	if (!wants_to_map_ids) {
+#endif
 		handler->pinfd = pin_rootfs(conf->rootfs.path);
 		if (handler->pinfd == -EBADF)
 			INFO("Failed to pin the rootfs for container \"%s\"", handler->name);
@@ -1640,6 +2141,32 @@ static int lxc_spawn(struct lxc_handler *handler)
 	}
 	TRACE("Cloned child process %d", handler->pid);
 
+#ifdef HAVE_ISULAD
+	/* isulad: close pipe after clone */
+	if (handler->conf->console.pipes[0][0] >= 0) {
+		close(handler->conf->console.pipes[0][0]);
+		handler->conf->console.pipes[0][0] = -1;
+	}
+
+	if (handler->conf->console.pipes[1][1] >= 0) {
+		close(handler->conf->console.pipes[1][1]);
+		handler->conf->console.pipes[1][1] = -1;
+	}
+
+	if (handler->conf->console.pipes[2][1] >= 0) {
+		close(handler->conf->console.pipes[2][1]);
+		handler->conf->console.pipes[2][1] = -1;
+	}
+
+	/* isulad: save pid/ppid info into file*/
+	if (handler->conf->container_info_file) {
+		if (lxc_save_container_info(handler->conf->container_info_file, handler->pid)) {
+			ERROR("Failed to save cloned container pid");
+			goto out_delete_net;
+		}
+	}
+#endif
+
 	/* Verify that we can actually make use of pidfds. */
 	if (!lxc_can_use_pidfd(handler->pidfd))
 		close_prot_errno_disarm(handler->pidfd);
@@ -1652,6 +2179,13 @@ static int lxc_spawn(struct lxc_handler *handler)
 	if (ret < 0)
 		SYSERROR("Failed to set environment variable: LXC_PID=%s", pidstr);
 
+#ifdef HAVE_ISULAD
+	if (handler->cgroup_ops->container_cgroup) {
+		if (setenv("LXC_CGROUP_PATH", handler->cgroup_ops->container_cgroup, 1))
+			SYSERROR("Failed to set environment variable: LXC_CGROUP_PATH=%s.", handler->cgroup_ops->container_cgroup);
+	}
+#endif
+
 	for (i = 0; i < LXC_NS_MAX; i++)
 		if (handler->ns_on_clone_flags & ns_info[i].clone_flag)
 			INFO("Cloned %s", ns_info[i].flag_name);
@@ -1765,7 +2299,11 @@ static int lxc_spawn(struct lxc_handler *handler)
 		goto out_delete_net;
 
 	if (!lxc_list_empty(&conf->limits)) {
+#ifdef HAVE_ISULAD
+		ret = setup_resource_limits(&conf->limits, handler->pid, conf->errpipe[1]);
+#else
 		ret = setup_resource_limits(&conf->limits, handler->pid);
+#endif
 		if (ret < 0) {
 			ERROR("Failed to setup resource limits");
 			goto out_delete_net;
@@ -1816,6 +2354,26 @@ static int lxc_spawn(struct lxc_handler *handler)
 		ERROR("Failed to run lxc.hook.start-host");
 		goto out_delete_net;
 	}
+#ifdef HAVE_ISULAD
+	/* isulad: Run oci prestart hook at here */
+	ret = run_oci_hooks(name, "oci-prestart", conf, lxcpath);
+	if (ret < 0) {
+		ERROR("Failed to run oci prestart hooks");
+		goto out_delete_net;
+	}
+
+	if (START_TIMEOUT == global_timeout_state) {
+		lxc_write_error_message(conf->errpipe[1], "Starting the container \"%s\" timeout.", name);
+		ERROR("Starting the container \"%s\" timeout.", name);
+		goto out_delete_net;
+	}
+
+       /* Tell the child to continue its initialization. We'll get
+        * LXC_SYNC_POST_OCI_PRESTART_HOOK when it is ready for us to run oci prestart hooks.
+        */
+       if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_OCI_PRESTART_HOOK))
+               goto out_delete_net;
+#endif
 
 	/* Tell the child to complete its initialization and wait for it to exec
 	 * or return an error. (The child will never return
@@ -1859,6 +2417,22 @@ static int lxc_spawn(struct lxc_handler *handler)
 	if (ret < 0)
 		goto out_abort;
 
+#ifdef HAVE_ISULAD
+	/* isulad: Run oci prestart hook at here */
+	ret = run_oci_hooks(name, "oci-poststart", conf, lxcpath);
+	if (ret < 0) {
+		ERROR("Failed to run oci poststart hooks");
+		goto out_abort;
+	}
+
+	if (START_TIMEOUT == global_timeout_state) {
+		lxc_write_error_message(conf->errpipe[1], "Starting the container \"%s\" timeout.", name);
+		ERROR("Starting the container \"%s\" timeout.", name);
+		goto out_abort;
+	}
+
+#endif
+
 	ret = lxc_set_state(name, handler, RUNNING);
 	if (ret < 0) {
 		ERROR("Failed to set state to \"%s\"", lxc_state2str(RUNNING));
@@ -1883,9 +2457,83 @@ out_sync_fini:
 	return -1;
 }
 
+#ifdef HAVE_ISULAD
+/* isulad: start timeout thread function */
+static void* wait_start_timeout(void *arg)
+{
+	struct start_timeout_conf *conf = (struct start_timeout_conf *)arg;
+
+	sem_post(&global_timeout_sem);
+
+	if (!conf || conf->timeout < 1)
+		goto out;
+
+	sleep(conf->timeout);
+
+	global_timeout_state = START_TIMEOUT;
+
+out:
+	free(conf);
+	return ((void *)0);
+}
+
+/* isulad: create start timeout thread */
+static int create_start_timeout_thread(struct lxc_conf *conf, unsigned int start_timeout)
+{
+	int ret = 0;
+	pthread_t ptid;
+	pthread_attr_t attr;
+	struct start_timeout_conf *timeout_conf = NULL;
+
+	if (sem_init(&global_timeout_sem, 0, 0)) {
+		ERROR("Failed to init start timeout semaphore");/*lint !e613*/
+		ret = -1;
+		return ret;
+	}
+
+	timeout_conf = malloc(sizeof(struct start_timeout_conf));
+	if (timeout_conf == NULL) {
+		ERROR("Failed to malloc start timeout conf");
+		ret = -1;
+		goto out;
+	}
+
+	memset(timeout_conf, 0, sizeof(struct start_timeout_conf));
+	timeout_conf->errfd = conf->errpipe[1];
+	timeout_conf->timeout = start_timeout;
+
+	pthread_attr_init(&attr);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+	ret = pthread_create(&ptid, &attr, wait_start_timeout, timeout_conf);
+	pthread_attr_destroy(&attr);
+	if (ret != 0) {
+		ERROR("Create start wait timeout thread failed");
+		free(timeout_conf);
+		goto out;
+	}
+
+	sem_wait(&global_timeout_sem);
+out:
+	sem_destroy(&global_timeout_sem);
+	return ret;
+}
+
+// isulad: send '128 + signal' if container is killed by signal.
+#define EXIT_SIGNAL_OFFSET 128
+#endif
+
+
+#ifdef HAVE_ISULAD
+int __lxc_start(struct lxc_handler *handler, struct lxc_operations *ops,
+		void *data, const char *lxcpath, bool daemonize, int *error_num,
+		unsigned int start_timeout)
+{
+	int exit_code;
+#else
 int __lxc_start(struct lxc_handler *handler, struct lxc_operations *ops,
 		void *data, const char *lxcpath, bool daemonize, int *error_num)
 {
+#endif
 	int ret, status;
 	const char *name = handler->name;
 	struct lxc_conf *conf = handler->conf;
@@ -1901,6 +2549,16 @@ int __lxc_start(struct lxc_handler *handler, struct lxc_operations *ops,
 	handler->daemonize = daemonize;
 	cgroup_ops = handler->cgroup_ops;
 
+#ifdef HAVE_ISULAD
+	/* isulad: add start timeout limit */
+	if (start_timeout > 0) {
+		ret = create_start_timeout_thread(conf, start_timeout);
+		if (ret) {
+			ERROR("Failed to create start timeout thread for container \"%s\".", name);
+			goto out_abort;
+		}
+	}
+#endif
 	if (!attach_block_device(handler->conf)) {
 		ERROR("Failed to attach block device");
 		ret = -1;
@@ -1959,11 +2617,13 @@ int __lxc_start(struct lxc_handler *handler, struct lxc_operations *ops,
 		goto out_delete_network;
 	}
 
+#ifndef HAVE_ISULAD
 	if (!handler->init_died && handler->pid > 0) {
 		ERROR("Child process is not killed");
 		ret = -1;
 		goto out_delete_network;
 	}
+#endif
 
 	status = lxc_wait_for_pid_status(handler->pid);
 	if (status < 0)
@@ -1973,6 +2633,21 @@ int __lxc_start(struct lxc_handler *handler, struct lxc_operations *ops,
 	 * reboot. This should mean it was an lxc-execute which simply exited.
 	 * In any case, treat it as a 'halt'.
 	 */
+#ifdef HAVE_ISULAD
+	// isulad: recored log for container init exit
+	if (WIFSIGNALED(status)) {
+		int signal = WTERMSIG(status);
+		signal = WTERMSIG(status);
+		exit_code = EXIT_SIGNAL_OFFSET + signal;
+		ERROR("Container \"%s\" init exited with signal %d", name, signal);
+	} else if (WIFEXITED(status)) {
+		exit_code = WEXITSTATUS(status);
+		ERROR("Container \"%s\" init exited with status %d", name, exit_code);
+	} else {
+		exit_code = -1;
+		ERROR("Container \"%s\" init exited with unknown status", name);
+	}
+#else
 	if (WIFSIGNALED(status)) {
 		switch(WTERMSIG(status)) {
 		case SIGINT: /* halt */
@@ -1990,6 +2665,7 @@ int __lxc_start(struct lxc_handler *handler, struct lxc_operations *ops,
 			break;
 		}
 	}
+#endif
 
 	ret = lxc_restore_phys_nics_to_netns(handler);
 	if (ret < 0)
@@ -1997,11 +2673,20 @@ int __lxc_start(struct lxc_handler *handler, struct lxc_operations *ops,
 
 	close_prot_errno_disarm(handler->pinfd);
 
+#ifdef HAVE_ISULAD
+	lxc_monitor_send_exit_code(name, exit_code, handler->lxcpath);
+#else
 	lxc_monitor_send_exit_code(name, status, handler->lxcpath);
+#endif
+
 	lxc_error_set_and_log(handler->pid, status);
 	if (error_num)
 		*error_num = handler->exit_status;
 
+#ifdef HAVE_ISULAD
+	handler->exit_code = exit_code; /* record exit code */
+#endif
+
 /* These are not the droids you are looking for. */
 __private_goto1:
 	lxc_delete_network(handler);
@@ -2032,7 +2717,11 @@ struct start_args {
 	char *const *argv;
 };
 
+#ifdef HAVE_ISULAD
+static int start(struct lxc_handler *handler, void* data, int fd)
+#else
 static int start(struct lxc_handler *handler, void* data)
+#endif
 {
 	struct start_args *arg = data;
 
@@ -2040,6 +2729,9 @@ static int start(struct lxc_handler *handler, void* data)
 
 	execvp(arg->argv[0], arg->argv);
 	SYSERROR("Failed to exec \"%s\"", arg->argv[0]);
+#ifdef HAVE_ISULAD
+	lxc_write_error_message(fd, "exec: \"%s\": %s.", arg->argv[0], strerror(errno));
+#endif
 	return 0;
 }
 
@@ -2057,14 +2749,22 @@ static struct lxc_operations start_ops = {
 };
 
 int lxc_start(char *const argv[], struct lxc_handler *handler,
+#ifdef HAVE_ISULAD
+	      const char *lxcpath, bool daemonize, int *error_num, unsigned int start_timeout)
+#else
 	      const char *lxcpath, bool daemonize, int *error_num)
+#endif
 {
 	struct start_args start_arg = {
 		.argv = argv,
 	};
 
 	TRACE("Doing lxc_start");
+#ifdef HAVE_ISULAD
+	return __lxc_start(handler, &start_ops, &start_arg, lxcpath, daemonize, error_num, start_timeout);
+#else
 	return __lxc_start(handler, &start_ops, &start_arg, lxcpath, daemonize, error_num);
+#endif
 }
 
 static void lxc_destroy_container_on_signal(struct lxc_handler *handler,
@@ -2136,3 +2836,261 @@ static bool do_destroy_container(struct lxc_handler *handler)
 
 	return storage_destroy(handler->conf);
 }
+
+#ifdef HAVE_ISULAD
+/*isulad: set env for clean resources */
+static int clean_resource_set_env(struct lxc_handler *handler)
+{
+	const char *name = handler->name;
+	struct lxc_conf *conf = handler->conf;
+	char bufstr[PATH_MAX + 1];
+	int i = 0;
+	int j = 0;
+	int len = 2; //set "LXC_PID" and "LXC_CGNS_AWARE"
+
+	if (conf == NULL || conf->ocihooks == NULL || conf->ocihooks->poststop_len == 0) {
+		return 0;
+	}
+
+	if (name) {
+		len++;
+	}
+	if (conf->rcfile) {
+		len++;
+	}
+	if (conf->rootfs.mount) {
+		len++;
+	}
+	if (conf->rootfs.path) {
+		len++;
+	}
+	if (conf->console.path) {
+		len++;
+	}
+	if (conf->console.log_path) {
+		len++;
+	}
+	if (handler->cgroup_ops->container_cgroup) {
+		len++;
+	}
+
+	for (; i < conf->ocihooks->poststop_len; i++) {
+		size_t cap = conf->ocihooks->poststop[i]->env_len;
+		size_t newcap = cap + len + 1;
+		if (lxc_grow_array((void ***)&(conf->ocihooks->poststop[i]->env), &cap, newcap, 1) != 0) {
+			return -1;
+		}
+		j = conf->ocihooks->poststop[i]->env_len;
+		/* Start of environment variable setup for hooks. */
+		if (name) {
+			snprintf(bufstr, PATH_MAX + 1, "LXC_NAME=%s", name);
+			conf->ocihooks->poststop[i]->env[j++] = safe_strdup(bufstr);
+		}
+		if (conf->rcfile) {
+			snprintf(bufstr, PATH_MAX + 1, "LXC_CONFIG_FILE=%s", conf->rcfile);
+			conf->ocihooks->poststop[i]->env[j++] = safe_strdup(bufstr);
+		}
+		if (conf->rootfs.mount) {
+			snprintf(bufstr, PATH_MAX + 1, "LXC_ROOTFS_MOUNT=%s", conf->rootfs.mount);
+			conf->ocihooks->poststop[i]->env[j++] = safe_strdup(bufstr);
+		}
+		if (conf->rootfs.path) {
+			snprintf(bufstr, PATH_MAX + 1, "LXC_ROOTFS_PATH=%s", conf->rootfs.path);
+			conf->ocihooks->poststop[i]->env[j++] = safe_strdup(bufstr);
+		}
+		if (conf->console.path) {
+			snprintf(bufstr, PATH_MAX + 1, "LXC_CONSOLE=%s", conf->console.path);
+			conf->ocihooks->poststop[i]->env[j++] = safe_strdup(bufstr);
+		}
+		if (conf->console.log_path) {
+			snprintf(bufstr, PATH_MAX + 1, "LXC_CONSOLE_LOGPATH=%s", conf->console.log_path);
+			conf->ocihooks->poststop[i]->env[j++] = safe_strdup(bufstr);
+		}
+		conf->ocihooks->poststop[i]->env[j++] = safe_strdup("LXC_CGNS_AWARE=1");
+
+		snprintf(bufstr, PATH_MAX + 1, "LXC_PID=%d", handler->pid);
+		conf->ocihooks->poststop[i]->env[j++] = safe_strdup(bufstr);
+		if (handler->cgroup_ops->container_cgroup) {
+			snprintf(bufstr, PATH_MAX + 1, "LXC_CGROUP_PATH=%s", handler->cgroup_ops->container_cgroup);
+			conf->ocihooks->poststop[i]->env[j++] = safe_strdup(bufstr);
+		}
+		conf->ocihooks->poststop[i]->env_len = j;
+		/* End of environment variable setup for hooks. */
+	}
+	return 0;
+}
+
+/*isulad: init handler for clean */
+static struct lxc_handler *lxc_init_clean_handler(char *name, char *lxcpath, struct lxc_conf *conf, pid_t pid)
+{
+	int i;
+	struct lxc_handler *handler;
+
+	handler = malloc(sizeof(*handler));
+	if (handler == NULL)
+		return NULL;
+
+	memset(handler, 0, sizeof(*handler));
+
+	/* Note that am_guest_unpriv() checks the effective uid. We
+	 * probably don't care if we are real root only if we are running
+	 * as root so this should be fine.
+	 */
+	handler->am_root = !am_guest_unpriv();
+	handler->data_sock[0] = handler->data_sock[1] = -1;
+	handler->conf = conf;
+	handler->lxcpath = lxcpath;
+	handler->pinfd = -1;
+	handler->sigfd = -EBADF;
+	handler->pidfd = -EBADF;
+	handler->init_died = false;
+	handler->monitor_status_fd = -EBADF;
+	handler->pid = pid;
+	handler->state_socket_pair[0] = handler->state_socket_pair[1] = -1;
+	if (handler->conf->reboot == REBOOT_NONE)
+		lxc_list_init(&handler->conf->state_clients);
+
+	for (i = 0; i < LXC_NS_MAX; i++)
+		handler->nsfd[i] = -1;
+
+	handler->name = name;
+	handler->exit_code = -1; /* isulad: record exit code of container */
+
+	handler->cgroup_ops = cgroup_init(conf);
+	if (!handler->cgroup_ops) {
+		ERROR("Failed to initialize cgroup driver");
+		goto on_error;
+	}
+
+	INFO("Container \"%s\" 's clean handler is initialized.", name);
+
+	return handler;
+
+on_error:
+	lxc_put_handler(handler);
+
+	return NULL;
+}
+
+/*isulad: init handler for clean */
+static struct lxc_handler *lxc_init_pids_handler(char *name, char *lxcpath, struct lxc_conf *conf)
+{
+	int i;
+	struct lxc_handler *handler;
+
+	handler = malloc(sizeof(*handler));
+	if (handler == NULL)
+		return NULL;
+
+	memset(handler, 0, sizeof(*handler));
+
+	/* Note that am_guest_unpriv() checks the effective uid. We
+	 * probably don't care if we are real root only if we are running
+	 * as root so this should be fine.
+	 */
+	handler->am_root = !am_guest_unpriv();
+	handler->data_sock[0] = handler->data_sock[1] = -1;
+	handler->conf = conf;
+	handler->lxcpath = lxcpath;
+	handler->pinfd = -1;
+	handler->sigfd = -EBADF;
+	handler->init_died = false;
+	handler->state_socket_pair[0] = handler->state_socket_pair[1] = -1;
+	handler->monitor_status_fd = -EBADF;
+	handler->pidfd = -EBADF;
+	if (handler->conf->reboot == REBOOT_NONE)
+		lxc_list_init(&handler->conf->state_clients);
+
+	for (i = 0; i < LXC_NS_MAX; i++)
+		handler->nsfd[i] = -1;
+
+	handler->name = name;
+	handler->exit_code = -1; /* isulad: record exit code of container */
+
+	handler->cgroup_ops = cgroup_init(conf);
+	if (!handler->cgroup_ops) {
+		ERROR("Failed to initialize cgroup driver");
+		goto on_error;
+	}
+
+	INFO("Container \"%s\" 's clean handler is initialized.", name);
+
+	return handler;
+
+on_error:
+	lxc_put_handler(handler);
+
+	return NULL;
+}
+
+/*isulad: do_lxcapi_clean_resource */
+int do_lxcapi_clean_resource(char *name, char *lxcpath, struct lxc_conf *conf, pid_t pid)
+{
+	int ret = 0;
+	struct lxc_handler *handler = NULL;
+	int retry_count = 0;
+	int max_retry = 10;
+
+	handler = lxc_init_clean_handler(name, lxcpath, conf, pid);
+	if (!handler) {
+		ERROR("Failed to init container %s clean handler", name);
+		ret = -1;
+		goto out;
+	}
+
+	if (clean_resource_set_env(handler) != 0) {
+		ERROR("Failed to set env for poststop hooks");
+		ret = -1;
+		goto out;
+	}
+
+	if (run_oci_hooks(handler->name, "oci-poststop", handler->conf, handler->lxcpath)) {
+		ERROR("Failed to run lxc.hook.post-stop for container \"%s\".", handler->name);
+		ret = -1;
+	}
+
+retry:
+	if (!handler->cgroup_ops->payload_destroy(handler->cgroup_ops, handler)) {
+		TRACE("Trying to kill all subprocess");
+		signal_all_processes(handler);
+		TRACE("Finished kill all subprocess");
+		if (retry_count < max_retry) {
+			usleep(100 * 1000); /* 100 millisecond */
+			retry_count++;
+			goto retry;
+		}
+		SYSERROR("Failed to destroy cgroup path for container: \"%s\"", handler->name);
+		ret = -1;
+	}
+
+out:
+	lxc_put_handler(handler);
+	return ret;
+}
+
+/*isulad: do_lxcapi_get_pids */
+int do_lxcapi_get_pids(char *name, char *lxcpath, struct lxc_conf *conf, pid_t **pids,size_t *pids_len)
+{
+	int ret = 0;
+	struct lxc_handler *handler = NULL;
+	struct cgroup_ops *cg_ops = NULL;
+
+	handler = lxc_init_pids_handler(name, lxcpath, conf);
+	if (!handler) {
+		ERROR("Failed to init container %s clean handler", name);
+		ret = -1;
+		goto out;
+	}
+
+	cg_ops = handler->cgroup_ops;
+	ret = get_all_pids(cg_ops, pids, pids_len);
+	if (ret < 0) {
+		WARN("failed to get all pids");
+	}
+
+out:
+	lxc_put_handler(handler);
+	return ret;
+}
+
+#endif
diff --git a/src/lxc/storage/btrfs.c b/src/lxc/storage/btrfs.c
index 92a4a6d..069a9dd 100644
--- a/src/lxc/storage/btrfs.c
+++ b/src/lxc/storage/btrfs.c
@@ -197,16 +197,27 @@ int btrfs_mount(struct lxc_storage *bdev)
 	const char *src;
 	int ret;
 
+#ifdef HAVE_ISULAD
+    unsigned long pflags = 0;
+#endif
+
 	if (strcmp(bdev->type, "btrfs"))
 		return -22;
 
 	if (!bdev->src || !bdev->dest)
 		return -22;
 
+#ifdef HAVE_ISULAD
+    if (parse_mntopts(bdev->mntopts, &mntflags, &pflags, &mntdata) < 0) {
+		free(mntdata);
+		return -22;
+	}
+#else
 	if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) {
 		free(mntdata);
 		return -22;
 	}
+#endif
 
 	src = lxc_storage_get_path(bdev->src, "btrfs");
 
diff --git a/src/lxc/storage/overlay.c b/src/lxc/storage/overlay.c
index 770785c..75a81de 100644
--- a/src/lxc/storage/overlay.c
+++ b/src/lxc/storage/overlay.c
@@ -349,6 +349,9 @@ int ovl_mount(struct lxc_storage *bdev)
 	char *work, *lastslash;
 	size_t len, len2;
 	int ret, ret2;
+#ifdef HAVE_ISULAD
+	unsigned long pflags = 0;
+#endif
 
 	if (strcmp(bdev->type, "overlay") && strcmp(bdev->type, "overlayfs"))
 		return -22;
@@ -414,7 +417,12 @@ int ovl_mount(struct lxc_storage *bdev)
 	work = must_make_path(upper, LXC_OVERLAY_WORK_DIR, NULL);
 	upper[lastslash - upper] = '/';
 
+#ifdef HAVE_ISULAD
+	ret = parse_mntopts(bdev->mntopts, &mntflags, &pflags, &mntdata);
+#else
 	ret = parse_mntopts(bdev->mntopts, &mntflags, &mntdata);
+#endif
+
 	if (ret < 0) {
 		ERROR("Failed to parse mount options");
 		free(mntdata);
diff --git a/src/lxc/sync.h b/src/lxc/sync.h
index ff7a1eb..56c1dfc 100644
--- a/src/lxc/sync.h
+++ b/src/lxc/sync.h
@@ -11,6 +11,10 @@ enum {
 	LXC_SYNC_POST_CONFIGURE,
 	LXC_SYNC_CGROUP,
 	LXC_SYNC_CGROUP_UNSHARE,
+#ifdef HAVE_ISULAD
+	LXC_SYNC_OCI_PRESTART_HOOK,
+	LXC_SYNC_POST_OCI_PRESTART_HOOK,
+#endif
 	LXC_SYNC_CGROUP_LIMITS,
 	LXC_SYNC_READY_START,
 	LXC_SYNC_RESTART,
diff --git a/src/lxc/terminal.c b/src/lxc/terminal.c
index e58db5c..0539eca 100644
--- a/src/lxc/terminal.c
+++ b/src/lxc/terminal.c
@@ -28,6 +28,10 @@
 #include "syscall_wrappers.h"
 #include "terminal.h"
 #include "utils.h"
+#ifdef HAVE_ISULAD
+#include "logger_json_file.h"
+#include "include/strlcpy.h"
+#endif
 
 #if HAVE_PTY_H
 #include <pty.h>
@@ -183,6 +187,69 @@ static int lxc_terminal_truncate_log_file(struct lxc_terminal *terminal)
 	return lxc_unpriv(ftruncate(terminal->log_fd, 0));
 }
 
+#ifdef HAVE_ISULAD
+
+int lxc_set_terminal_winsz(struct lxc_terminal *terminal, unsigned int height, unsigned int width)
+{
+	int ret = 0;
+	struct winsize wsz;
+
+	if (terminal->ptmx < 0) {
+		return 0;
+	}
+
+	ret = ioctl(terminal->ptmx, TIOCGWINSZ, &wsz);
+	if (ret < 0) {
+		WARN("Failed to get window size");
+		return -1;
+	}
+	wsz.ws_col = width;
+	wsz.ws_row = height;
+
+	ret = ioctl(terminal->ptmx, TIOCSWINSZ, &wsz);
+	if (ret < 0)
+		WARN("Failed to set window size");
+	else
+		DEBUG("Set window size to %d columns and %d rows", wsz.ws_col,
+		      wsz.ws_row);
+	return ret;
+}
+
+/*
+ * isulad: support mult-logfiles
+ * */
+static int lxc_terminal_rename_old_log_file(struct lxc_terminal *terminal)
+{
+	int ret;
+	unsigned int i;
+	char tmp[PATH_MAX] = {0};
+	char *rename_fname = NULL;
+
+	for (i = terminal->log_rotate - 1; i > 1; i--) {
+		ret = snprintf(tmp, PATH_MAX, "%s.%u", terminal->log_path, i);
+		if (ret < 0 || ret >= PATH_MAX) {
+			free(rename_fname);
+			return -EFBIG;
+		}
+		free(rename_fname);
+		rename_fname = safe_strdup(tmp);
+		ret = snprintf(tmp, PATH_MAX, "%s.%u", terminal->log_path, (i - 1));
+		if (ret < 0 || ret >= PATH_MAX) {
+			free(rename_fname);
+			return -EFBIG;
+		}
+		ret = lxc_unpriv(rename(tmp, rename_fname));
+		if (ret < 0 && errno != ENOENT) {
+			free(rename_fname);
+			return ret;
+		}
+	}
+
+	free(rename_fname);
+	return 0;
+}
+#endif
+
 static int lxc_terminal_rotate_log_file(struct lxc_terminal *terminal)
 {
 	__do_free char *tmp = NULL;
@@ -196,6 +263,15 @@ static int lxc_terminal_rotate_log_file(struct lxc_terminal *terminal)
 	if (terminal->log_fd < 0)
 		return -EBADF;
 
+#ifdef HAVE_ISULAD
+	/* isuald: rotate old log file first */
+	ret = lxc_terminal_rename_old_log_file(terminal);
+	if(ret != 0) {
+		ERROR("Rename old log file failed");
+		return ret;
+	}
+#endif
+
 	len = strlen(terminal->log_path) + sizeof(".1");
 	tmp = must_realloc(NULL, len);
 
@@ -212,6 +288,7 @@ static int lxc_terminal_rotate_log_file(struct lxc_terminal *terminal)
 	return lxc_terminal_create_log_file(terminal);
 }
 
+#ifndef HAVE_ISULAD
 static int lxc_terminal_write_log_file(struct lxc_terminal *terminal, char *buf,
 				       int bytes_read)
 {
@@ -317,7 +394,465 @@ static int lxc_terminal_write_log_file(struct lxc_terminal *terminal, char *buf,
 	bytes_read -= ret;
 	return bytes_read;
 }
+#endif
+
+#ifdef HAVE_ISULAD
+/* get time buffer */
+static bool get_time_buffer(struct timespec *timestamp, char *timebuffer,
+                            size_t maxsize)
+{
+	struct tm tm_utc = { 0 };
+	int32_t nanos = 0;
+	time_t seconds;
+	size_t len = 0;
+	int ret = 0;
+
+	if (!timebuffer || !maxsize) {
+		return false;
+	}
+
+	seconds = (time_t)timestamp->tv_sec;
+	gmtime_r(&seconds, &tm_utc);
+	strftime(timebuffer, maxsize, "%Y-%m-%dT%H:%M:%S", &tm_utc);
+
+	nanos = (int32_t)timestamp->tv_nsec;
+	len = strlen(timebuffer);
+	ret = snprintf(timebuffer + len, (maxsize - len), ".%09dZ", nanos);
+	if (ret < 0 || ret >= (maxsize - len)) {
+		return false;
+	}
+
+	return true;
+}
+
+/* get now time buffer */
+static bool get_now_time_buffer(char *timebuffer, size_t maxsize)
+{
+	int err = 0;
+	struct timespec ts;
+
+	err = clock_gettime(CLOCK_REALTIME, &ts);
+	if (err != 0) {
+		ERROR("failed to get time");
+		return false;
+	}
+
+	return get_time_buffer(&ts, timebuffer, maxsize);
+}
+
+static int isulad_lxc_terminal_rotate_write_data(struct lxc_terminal *terminal, const char *buf,
+                int bytes_read)
+{
+	int ret;
+	struct stat st;
+	int64_t space_left = -1;
+
+	if (terminal->log_fd < 0)
+		return 0;
+
+	/* A log size <= 0 means that there's no limit on the size of the log
+	 * file at which point we simply ignore whether the log is supposed to
+	 * be rotated or not.
+	 */
+	if (terminal->log_size <= 0)
+		return lxc_write_nointr(terminal->log_fd, buf, bytes_read);
+
+	/* Get current size of the log file. */
+	ret = fstat(terminal->log_fd, &st);
+	if (ret < 0) {
+		SYSERROR("Failed to stat the terminal log file descriptor");
+		return -1;
+	}
+
+	/* handle non-regular files */
+	if ((st.st_mode & S_IFMT) != S_IFREG) {
+		/* This isn't a regular file. so rotating the file seems a
+		 * dangerous thing to do, size limits are also very
+		 * questionable. Let's not risk anything and tell the user that
+		 * he's requesting us to do weird stuff.
+		 */
+		if (terminal->log_rotate > 0 || terminal->log_size > 0)
+			return -EINVAL;
+
+		/* I mean, sure log wherever you want to. */
+		return lxc_write_nointr(terminal->log_fd, buf, bytes_read);
+	}
+
+	space_left = terminal->log_size - st.st_size;
+
+	/* User doesn't want to rotate the log file and there's no more space
+	 * left so simply truncate it.
+	 */
+	if (space_left <= 0 && terminal->log_rotate <= 0) {
+		ret = lxc_terminal_truncate_log_file(terminal);
+		if (ret < 0)
+			return ret;
+
+		if (bytes_read <= terminal->log_size)
+			return lxc_write_nointr(terminal->log_fd, buf, bytes_read);
+
+		/* Write as much as we can into the buffer and loose the rest. */
+		return lxc_write_nointr(terminal->log_fd, buf, terminal->log_size);
+	}
+
+	/* There's enough space left. */
+	if (bytes_read <= space_left)
+		return lxc_write_nointr(terminal->log_fd, buf, bytes_read);
+
+	/* There'd be more to write but we aren't instructed to rotate the log
+	 * file so simply return. There's no error on our side here.
+	 */
+	if (terminal->log_rotate > 0)
+		ret = lxc_terminal_rotate_log_file(terminal);
+	else
+		ret = lxc_terminal_truncate_log_file(terminal);
+	if (ret < 0)
+		return ret;
+
+	if (terminal->log_size < bytes_read) {
+		/* Well, this is unfortunate because it means that there is more
+		 * to write than the user has granted us space. There are
+		 * multiple ways to handle this but let's use the simplest one:
+		 * write as much as we can, tell the user that there was more
+		 * stuff to write and move on.
+		 * Note that this scenario shouldn't actually happen with the
+		 * standard pty-based terminal that LXC allocates since it will
+		 * be switched into raw mode. In raw mode only 1 byte at a time
+		 * should be read and written.
+		 */
+		WARN("Size of terminal log file is smaller than the bytes to write");
+		ret = lxc_write_nointr(terminal->log_fd, buf, terminal->log_size);
+		if (ret < 0)
+			return -1;
+		bytes_read -= ret;
+		return bytes_read;
+	}
+
+	/* Yay, we made it. */
+	ret = lxc_write_nointr(terminal->log_fd, buf, bytes_read);
+	if (ret < 0)
+		return -1;
+	bytes_read -= ret;
+	return bytes_read;
+}
+
+static ssize_t isulad_logger_json_write(struct lxc_terminal *terminal, const char *type, const char *buf,
+                                int bytes_read)
+{
+	logger_json_file *msg = NULL;
+	ssize_t ret = -1;
+	size_t len;
+	char *json = NULL;
+	char timebuffer[64] = { 0 };
+	parser_error err = NULL;
+	struct parser_context ctx = { GEN_OPTIONS_SIMPLIFY | GEN_OPTIONS_NOT_VALIDATE_UTF8, stderr };
+
+	if (bytes_read < 0 || bytes_read >= INT_MAX) {
+		return -1;
+	}
+	msg = calloc(sizeof(logger_json_file), 1);
+	if (msg == NULL) {
+		return -errno;
+	}
+	msg->log = calloc(bytes_read, 1);
+	if (!msg->log) {
+		goto cleanup;
+	}
+	memcpy(msg->log, buf, bytes_read);
+	msg->log_len = bytes_read;
+	msg->stream = type ? safe_strdup(type) : safe_strdup("stdout");
+
+	get_now_time_buffer(timebuffer, sizeof(timebuffer));
+	msg->time = safe_strdup(timebuffer);
+
+	json = logger_json_file_generate_json(msg, &ctx, &err);
+	if (!json) {
+		ERROR("Failed to generate json: %s", err);
+		goto cleanup;
+	}
+	len = strlen(json);
+	json[len] = '\n';
+	ret = isulad_lxc_terminal_rotate_write_data(terminal, json, len + 1);
+cleanup:
+	free(json);
+	free_logger_json_file(msg);
+	free(err);
+	return ret;
+}
+
+static ssize_t isulad_logger_syslog_write(struct lxc_terminal *terminal, const char *buf)
+{
+        syslog(LOG_INFO, "%s", buf);
+        return 0;
+}
+
+static inline bool is_syslog(const char *driver)
+{
+        if (driver == NULL) {
+                return false;
+        }
+
+        return (strcmp("syslog", driver) == 0);
+}
+
+static inline ssize_t isulad_logger_write(struct lxc_terminal *terminal, const char *type, const char *buf,
+                                int bytes_read)
+{
+        if (is_syslog(terminal->log_driver)) {
+                return isulad_logger_syslog_write(terminal, buf);
+        }
+
+        return isulad_logger_json_write(terminal, type, buf, bytes_read);
+}
+
+static int isulad_lxc_terminal_write_log_file(struct lxc_terminal *terminal, const char *type, char *buf,
+                                       int bytes_read)
+{
+#define __BUF_CACHE_SIZE (16 * LXC_TERMINAL_BUFFER_SIZE)
+	static char cache[__BUF_CACHE_SIZE];
+	static int size = 0;
+	int upto, index;
+	int begin = 0, buf_readed = 0,  buf_left = 0;
+	int ret;
+
+	if (buf != NULL && bytes_read > 0) {
+		/* Work out how much more data we are okay with reading this time. */
+		upto = size + bytes_read;
+		if (upto > __BUF_CACHE_SIZE) {
+			upto = __BUF_CACHE_SIZE;
+		}
+
+		if (upto > size) {
+			buf_readed = upto - size;
+			memcpy(cache + size, buf, buf_readed);
+			buf_left = bytes_read - buf_readed;
+			size += buf_readed;
+		}
+	}
+
+	// If we have no data to log, and there's no more coming, we're done.
+	if (size == 0)
+		return 0;
+
+	// Break up the data that we've buffered up into lines, and log each in turn.
+	for (index = 0; index < size; index++) {
+		if (cache[index] == '\n') {
+			ret = isulad_logger_write(terminal, type, cache + begin, index - begin + 1);
+			if (ret < 0) {
+				WARN("Failed to log msg");
+			}
+			begin = index + 1;
+		}
+	}
+	/* If there's no more coming, or the buffer is full but
+	 * has no newlines, log whatever we haven't logged yet,
+	 * noting that it's a partial log line. */
+	if (buf == NULL || (begin == 0 && size == __BUF_CACHE_SIZE)) {
+		if (begin < size) {
+			ret = isulad_logger_write(terminal, type, cache + begin, size - begin);
+			if (ret < 0) {
+				WARN("Failed to log msg");
+			}
+			begin = 0;
+			size = 0;
+		}
+		if (buf == NULL) {
+			return 0;
+		}
+	}
+	/* Move any unlogged data to the front of the buffer in preparation for another read. */
+	if (begin > 0) {
+		memcpy(cache, cache + begin, size - begin);
+		size -= begin;
+	}
+	/* Move left data to cache buffer */
+	if (buf_left > 0) {
+		memcpy(cache + size, buf + buf_readed, buf_left);
+		size += buf_left;
+	}
+	return 0;
+}
+
+/* isulad: forward data to all fifos */
+static void lxc_forward_data_to_fifo(struct lxc_list *list, bool is_err, const char *buf, int r)
+{
+	struct lxc_list *it  = NULL;
+	struct lxc_list *next = NULL;
+	struct lxc_fifos_fd *elem = NULL;
+	ssize_t w = 0;
+
+	lxc_list_for_each_safe(it, list, next) {
+		elem = it->elem;
+		if (is_err) {
+			if (elem->err_fd >= 0) {
+				w = lxc_write_nointr_for_fifo(elem->err_fd, buf, r);
+				if (w != r) {
+					WARN("Failed to write to fifo fd %d with error: %s", elem->err_fd, strerror(errno));
+				}
+			}
+		} else {
+			if (elem->out_fd >= 0) {
+				w = lxc_write_nointr_for_fifo(elem->out_fd, buf, r);
+				if (w != r) {
+					WARN("Failed to write to fifo fd %d with error: %s", elem->out_fd, strerror(errno));
+				}
+			}
+		}
+	}
+
+	return;
+}
+
+/* isulad: judge the fd whether is fifo */
+static bool lxc_terminal_is_fifo(int fd, struct lxc_list *list)
+{
+	struct lxc_list *it = NULL;
+	struct lxc_list *next = NULL;
+	struct lxc_fifos_fd *elem = NULL;
+
+	lxc_list_for_each_safe(it, list, next) {
+		elem = it->elem;
+		if (elem->in_fd == fd)
+			return true;
+	}
+
+	return false;
+}
+
+/* isulad: if fd == -1, means delete all the fifos*/
+int lxc_terminal_delete_fifo(int fd, struct lxc_list *list)
+{
+	struct lxc_list *it = NULL;
+	struct lxc_list *next = NULL;
+	struct lxc_fifos_fd *elem = NULL;
+
+	lxc_list_for_each_safe(it, list, next) {
+		elem = it->elem;
+		if (elem->in_fd == fd || -1 == fd) {
+			INFO("Delete fifo fd %d", fd);
+			lxc_list_del(it);
+			if (elem->in_fifo)
+				free(elem->in_fifo);
+			if (elem->out_fifo)
+				free(elem->out_fifo);
+			if (elem->err_fifo)
+				free(elem->err_fifo);
+			if (elem->in_fd >= 0)
+				close(elem->in_fd);
+			if (elem->out_fd >= 0)
+				close(elem->out_fd);
+			if (elem->err_fd >= 0)
+				close(elem->err_fd);
+			free(elem);
+		}
+	}
+
+	return 0;
+}
+
+int lxc_terminal_io_cb(int fd, uint32_t events, void *data,
+                       struct lxc_epoll_descr *descr)
+{
+	struct lxc_terminal *terminal = data;
+	char buf[2 * LXC_TERMINAL_BUFFER_SIZE];
+	int r, w, w_log, w_rbuf;
+
+	w = r = lxc_read_nointr(fd, buf, sizeof(buf));
+	if (r <= 0) {
+		INFO("Terminal client on fd %d has exited", fd);
+		lxc_mainloop_del_handler(descr, fd);
+
+		if (fd == terminal->ptmx) {
+			terminal->ptmx = -EBADF;
+			/* write remained buffer to terminal log */
+			if (terminal->log_fd >= 0) {
+				w_log = isulad_lxc_terminal_write_log_file(terminal, "stdout", NULL, 0);
+				if (w_log < 0)
+					TRACE("Failed to write %d bytes to terminal log", r);
+			}
+			/* notes: do not close the ptmx fd due to if we close the fd, the process may
+			 * recive SIGHUP and the exit code will be 129 (128 + 1)
+			 */
+			return LXC_MAINLOOP_CLOSE;
+		} else if (fd == terminal->peer) {
+			lxc_terminal_signal_fini(terminal);
+			terminal->peer = -EBADF;
+			close(fd);
+			return LXC_MAINLOOP_CONTINUE; /* isulad: do not close mainloop when peer close*/
+		} else if (lxc_terminal_is_fifo(fd, &terminal->fifos)) {
+			/* isulad: delete fifos when the client close */
+			lxc_terminal_delete_fifo(fd, &terminal->fifos);
+			return LXC_MAINLOOP_CONTINUE;
+		} else if (fd == terminal->pipes[1][0] || fd == terminal->pipes[2][0]) {
+			if (fd == terminal->pipes[1][0]) {
+				if (terminal->log_fd >= 0) {
+					w_log = isulad_lxc_terminal_write_log_file(terminal, "stdout", NULL, 0);
+				}
+				terminal->pipes[1][0] = -EBADF;
+			} else if (fd == terminal->pipes[2][0]) {
+				if (terminal->log_fd >= 0) {
+					w_log = isulad_lxc_terminal_write_log_file(terminal, "stderr", NULL, 0);
+				}
+				terminal->pipes[2][0] = -EBADF;
+			}
+			/* notes: do not close the ptmx 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_CONTINUE;
+		} else if (fd == terminal->pipes[0][1]) {
+			TRACE("closed stdin pipe of container stdin");
+			terminal->pipes[0][1] = -EBADF;
+			return LXC_MAINLOOP_CONTINUE;
+		} else {
+			WARN("Handler received unexpected file descriptor");
+		}
+		close(fd);
+		return LXC_MAINLOOP_CLOSE;
+	}
+
+	if (fd == terminal->peer || lxc_terminal_is_fifo(fd, &terminal->fifos)) {
+		if (terminal->ptmx > 0)
+			w = lxc_write_nointr(terminal->ptmx, buf, r);
+		if (terminal->pipes[0][1] > 0)
+			w = lxc_write_nointr(terminal->pipes[0][1], buf, r);
+	}
+
+	w_rbuf = w_log = 0;
+	if (fd == terminal->ptmx || fd == terminal->pipes[1][0] || fd == terminal->pipes[2][0]) {
+		/* write to peer first */
+		if (terminal->peer >= 0)
+			w = lxc_write_nointr(terminal->peer, buf, r);
+
+		/* isulad: forward data to fifos */
+		lxc_forward_data_to_fifo(&terminal->fifos, fd == terminal->pipes[2][0], buf, r);
+
+		/* write to terminal ringbuffer */
+		if (terminal->buffer_size > 0)
+			w_rbuf = lxc_ringbuf_write(&terminal->ringbuf, buf, r);
+
+		/* write to terminal log */
+		if (terminal->log_fd >= 0) {
+			if (fd == terminal->ptmx || fd == terminal->pipes[1][0])
+				w_log = isulad_lxc_terminal_write_log_file(terminal, "stdout", buf, r);
+			else if (fd == terminal->pipes[2][0])
+				w_log = isulad_lxc_terminal_write_log_file(terminal, "stderr", buf, r);
+		}
+	}
+
+	if (w != r)
+		WARN("Short write on terminal r:%d != w:%d", r, w);
+
+	if (w_rbuf < 0) {
+		errno = -w_rbuf;
+		SYSTRACE("Failed to write %d bytes to terminal ringbuffer", r);
+	}
+
+	if (w_log < 0)
+		TRACE("Failed to write %d bytes to terminal log", r);
 
+	return LXC_MAINLOOP_CONTINUE;
+}
+#else
 int lxc_terminal_io_cb(int fd, uint32_t events, void *data,
 		       struct lxc_epoll_descr *descr)
 {
@@ -374,6 +909,7 @@ int lxc_terminal_io_cb(int fd, uint32_t events, void *data,
 
 	return LXC_MAINLOOP_CONTINUE;
 }
+#endif
 
 static int lxc_terminal_mainloop_add_peer(struct lxc_terminal *terminal)
 {
@@ -401,6 +937,110 @@ static int lxc_terminal_mainloop_add_peer(struct lxc_terminal *terminal)
 	return 0;
 }
 
+#ifdef HAVE_ISULAD
+/* isulad add pipes to mainloop */
+static int lxc_terminal_mainloop_add_pipes(struct lxc_terminal *terminal)
+{
+	int ret = 0;
+
+	// parent read data from fifo, and send to stdin of container
+	if (terminal->pipes[0][1] > 0) {
+		ret = lxc_mainloop_add_handler(terminal->descr, terminal->pipes[0][1],
+		                               lxc_terminal_io_cb, terminal);
+		if (ret) {
+			ERROR("pipe fd %d not added to mainloop", terminal->pipes[0][1]);
+			return -1;
+		}
+	}
+	// parent read data from stdout of container, and send to fifo
+	if (terminal->pipes[1][0] > 0) {
+		ret = lxc_mainloop_add_handler(terminal->descr, terminal->pipes[1][0],
+		                               lxc_terminal_io_cb, terminal);
+		if (ret) {
+			ERROR("pipe fd %d not added to mainloop", terminal->pipes[1][0]);
+			return -1;
+		}
+	}
+	// parent read data from stderr of container, and send to fifo
+	if (terminal->pipes[2][0] > 0) {
+		ret = lxc_mainloop_add_handler(terminal->descr, terminal->pipes[2][0],
+		                               lxc_terminal_io_cb, terminal);
+		if (ret) {
+			ERROR("pipe fd %d not added to mainloop", terminal->pipes[2][0]);
+			return -1;
+		}
+	}
+	return ret;
+}
+
+/* isulad add fifo to mainloop */
+static int lxc_terminal_mainloop_add_fifo(struct lxc_terminal *terminal)
+{
+	int ret = 0;
+	struct lxc_list *it = NULL;
+	struct lxc_list *next = NULL;
+	struct lxc_fifos_fd *elem = NULL;
+
+	lxc_list_for_each_safe(it, &terminal->fifos, next) {
+		elem = it->elem;
+		if (elem->in_fd >= 0) {
+			ret = lxc_mainloop_add_handler(terminal->descr, elem->in_fd,
+			                               lxc_terminal_io_cb, terminal);
+			if (ret) {
+				ERROR("console fifo %s not added to mainloop", elem->in_fifo);
+				return -1;
+			}
+		}
+	}
+	return ret;
+}
+
+int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr,
+                              struct lxc_terminal *terminal)
+{
+	int ret;
+
+	/* We cache the descr so that we can add an fd to it when someone
+	 * does attach to it in lxc_terminal_allocate().
+	 */
+	terminal->descr = descr;
+
+	ret = lxc_terminal_mainloop_add_peer(terminal);
+	if (ret < 0) {
+		ERROR("Failed to add handler for terminal peer to mainloop");
+		return -1;
+	}
+
+	/* isulad add pipes to mainloop */
+	ret = lxc_terminal_mainloop_add_pipes(terminal);
+	if (ret < 0) {
+		ERROR("Failed to add handler for terminal fifos to mainloop");
+		return -1;
+	}
+
+	/* isulad add fifo to mainloop */
+	ret = lxc_terminal_mainloop_add_fifo(terminal);
+	if (ret < 0) {
+		ERROR("Failed to add handler for terminal fifos to mainloop");
+		return -1;
+	}
+
+	if (terminal->ptmx < 0) {
+		INFO("Terminal is not initialized");
+		return 0;
+	}
+
+	ret = lxc_mainloop_add_handler(descr, terminal->ptmx,
+	                               lxc_terminal_io_cb, terminal);
+	if (ret < 0) {
+		ERROR("Failed to add handler for terminal ptmx fd %d to "
+		      "mainloop", terminal->ptmx);
+		return -1;
+	}
+
+	return 0;
+}
+#else
 int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr,
 			      struct lxc_terminal *terminal)
 {
@@ -426,6 +1066,7 @@ int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr,
 
 	return lxc_terminal_mainloop_add_peer(terminal);
 }
+#endif
 
 int lxc_setup_tios(int fd, struct termios *oldtios)
 {
@@ -639,14 +1280,39 @@ void lxc_terminal_free(struct lxc_conf *conf, int fd)
 
 static int lxc_terminal_peer_default(struct lxc_terminal *terminal)
 {
+#ifdef HAVE_ISULAD
+	struct lxc_terminal_state *ts = NULL;
+	const char *path = NULL;
+#else
 	struct lxc_terminal_state *ts;
 	const char *path;
+#endif
 	int ret = 0;
 
 	if (terminal->path)
 		path = terminal->path;
+
+#ifdef HAVE_ISULAD
+	/* 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;
+	}
+#else
 	else
 		path = "/dev/tty";
+#endif
 
 	terminal->peer = lxc_unpriv(open(path, O_RDWR | O_CLOEXEC));
 	if (terminal->peer < 0) {
@@ -760,6 +1426,35 @@ void lxc_terminal_delete(struct lxc_terminal *terminal)
 	if (terminal->log_fd >= 0)
 		close(terminal->log_fd);
 	terminal->log_fd = -1;
+
+#ifdef HAVE_ISULAD
+        if (is_syslog(terminal->log_driver)) {
+                closelog();
+                free(terminal->log_driver);
+	}
+	/* isulad: close all pipes */
+	if (terminal->pipes[0][0] >= 0)
+		close(terminal->pipes[0][0]);
+	terminal->pipes[0][0] = -1;
+	if (terminal->pipes[0][1] >= 0)
+		close(terminal->pipes[0][1]);
+	terminal->pipes[0][1] = -1;
+	if (terminal->pipes[1][0] >= 0)
+		close(terminal->pipes[1][0]);
+	terminal->pipes[1][0] = -1;
+	if (terminal->pipes[1][1] >= 0)
+		close(terminal->pipes[1][1]);
+	terminal->pipes[1][1] = -1;
+	if (terminal->pipes[2][0] >= 0)
+		close(terminal->pipes[2][0]);
+	terminal->pipes[2][0] = -1;
+	if (terminal->pipes[2][1] >= 0)
+		close(terminal->pipes[2][1]);
+	terminal->pipes[2][1] = -1;
+
+	/* isulad: delete all fifos */
+	lxc_terminal_delete_fifo(-1, &terminal->fifos);
+#endif
 }
 
 /**
@@ -828,6 +1523,251 @@ int lxc_terminal_create_log_file(struct lxc_terminal *terminal)
 	return 0;
 }
 
+#ifdef HAVE_ISULAD
+/* isulad: fd_nonblock */
+static int fd_nonblock(int fd)
+{
+	int flags;
+
+	flags = fcntl(fd, F_GETFL);
+
+	return fcntl(fd, F_SETFL, (int)((unsigned int)flags | O_NONBLOCK));
+}
+
+static int terminal_fifo_open(const char *fifo_path, int flags)
+{
+	int fd = -1;
+
+	fd = lxc_open(fifo_path, flags, 0);
+	if (fd < 0) {
+		WARN("Failed to open fifo %s to send message: %s.", fifo_path,
+		     strerror(errno));
+		return -1;
+	}
+
+	return fd;
+}
+
+bool fifo_exists(const char *path)
+{
+	struct stat sb;
+	int ret;
+
+	ret = stat(path, &sb);
+	if (ret < 0)
+		// could be something other than eexist, just say no
+		return false;
+	return S_ISFIFO(sb.st_mode);
+}
+
+/* isulad: set terminal fifos */
+static int lxc_terminal_set_fifo(struct lxc_terminal *console, const char *in, const char *out, const char *err, int *input_fd)
+{
+	int fifofd_in = -1, fifofd_out = -1, fifofd_err = -1;
+	struct lxc_fifos_fd *fifo_elem = NULL;
+
+	if ((in && !fifo_exists(in)) || (out && !fifo_exists(out)) || (err && !fifo_exists(err))) {
+		ERROR("File %s or %s or %s does not refer to a FIFO", in, out, err);
+		return -1;
+	}
+
+	if (in) {
+		fifofd_in = terminal_fifo_open(in, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+		if (fifofd_in < 0) {
+			SYSERROR("Failed to open FIFO: %s", in);
+			return -1;
+		}
+	}
+
+	if (out) {
+		fifofd_out = terminal_fifo_open(out, O_WRONLY | O_NONBLOCK | O_CLOEXEC);
+		if (fifofd_out < 0) {
+			SYSERROR("Failed to open FIFO: %s", out);
+			if (fifofd_in >= 0)
+				close(fifofd_in);
+			return -1;
+		}
+	}
+
+	if (err) {
+		fifofd_err = terminal_fifo_open(err, O_WRONLY | O_NONBLOCK | O_CLOEXEC);
+		if (fifofd_err < 0) {
+			SYSERROR("Failed to open FIFO: %s", err);
+			if (fifofd_in >= 0)
+				close(fifofd_in);
+			if (fifofd_out >= 0)
+				close(fifofd_out);
+			return -1;
+		}
+	}
+
+	fifo_elem = malloc(sizeof(*fifo_elem));
+	if (fifo_elem == NULL) {
+		if (fifofd_in >= 0)
+			close(fifofd_in);
+		if (fifofd_out >= 0)
+			close(fifofd_out);
+		if (fifofd_err >= 0)
+			close(fifofd_err);
+		return -1;
+	}
+	memset(fifo_elem, 0, sizeof(*fifo_elem));
+
+	fifo_elem->in_fifo = safe_strdup(in ? in : "");
+	fifo_elem->out_fifo = safe_strdup(out ? out : "");
+	fifo_elem->err_fifo = safe_strdup(err ? err : "");
+	fifo_elem->in_fd = fifofd_in;
+	fifo_elem->out_fd = fifofd_out;
+	fifo_elem->err_fd = fifofd_err;
+	lxc_list_add_elem(&fifo_elem->node, fifo_elem);
+	lxc_list_add_tail(&console->fifos, &fifo_elem->node);
+
+	if (input_fd)
+		*input_fd = fifofd_in;
+
+	return 0;
+}
+
+/* isulad: add default fifos */
+static int lxc_terminal_fifo_default(struct lxc_terminal *terminal)
+{
+	if (terminal->init_fifo[0] || terminal->init_fifo[1] || terminal->init_fifo[2])
+		return lxc_terminal_set_fifo(terminal, terminal->init_fifo[0], terminal->init_fifo[1], terminal->init_fifo[2], NULL);
+	return 0;
+}
+
+int lxc_terminal_create(struct lxc_terminal *terminal)
+{
+	int ret;
+
+	if (!terminal->disable_pty) {
+		ret = openpty(&terminal->ptmx, &terminal->pts, NULL, NULL, NULL);
+		if (ret < 0) {
+			SYSERROR("Failed to open terminal");
+			return -1;
+		}
+
+		ret = ttyname_r(terminal->pts, terminal->name, sizeof(terminal->name));
+		if (ret < 0) {
+			SYSERROR("Failed to retrieve name of terminal pts");
+			goto err;
+		}
+
+		ret = fd_cloexec(terminal->ptmx, true);
+		if (ret < 0) {
+			SYSERROR("Failed to set FD_CLOEXEC flag on terminal ptmx");
+			goto err;
+		}
+
+		/* isulad: make ptmx NONBLOCK */
+		ret = fd_nonblock(terminal->ptmx);
+		if (ret < 0) {
+			SYSERROR("Failed to set O_NONBLOCK flag on terminal ptmx");
+			goto err;
+		}
+
+		ret = fd_cloexec(terminal->pts, true);
+		if (ret < 0) {
+			SYSERROR("Failed to set FD_CLOEXEC flag on terminal pts");
+			goto err;
+		}
+
+		ret = lxc_terminal_peer_default(terminal);
+		if (ret < 0) {
+			ERROR("Failed to allocate proxy terminal");
+			goto err;
+		}
+	} else {
+		/* isulad: create 3 pipes */
+		/* for stdin */
+		if (pipe2(terminal->pipes[0], O_CLOEXEC)) {
+			ERROR("Failed to create stdin pipe");
+			goto err;
+		}
+
+		/* for stdout */
+		if (pipe2(terminal->pipes[1], O_CLOEXEC)) {
+			ERROR("Failed to create stdout pipe");
+			goto err;
+		}
+		/* for stderr */
+		if (pipe2(terminal->pipes[2], O_CLOEXEC)) {
+			ERROR("Failed to create stderr pipe");
+			goto err;
+		}
+	}
+
+	/* isulad: open fifos */
+	ret = lxc_terminal_fifo_default(terminal);
+	if (ret < 0) {
+		ERROR("Failed to allocate fifo terminal");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	lxc_terminal_delete(terminal);
+	return -ENODEV;
+}
+
+/* isulad: add fifos dynamic*/
+int lxc_terminal_add_fifos(struct lxc_conf *conf, const char *fifonames)
+{
+	int ret = 0;
+	struct lxc_terminal *terminal = &conf->console;
+	int fifofd_in = -1;
+	char *tmp = NULL, *saveptr = NULL, *in = NULL, *out = NULL, *err = NULL;
+	const char *none_fifo_name = "none";
+
+	tmp = safe_strdup(fifonames);
+
+	in = strtok_r(tmp, "&&&&", &saveptr);
+	if (!in) {
+		ret = -1;
+		goto free_out;
+	}
+	if (strcmp(in, none_fifo_name) == 0)
+		in = NULL;
+
+	out = strtok_r(NULL, "&&&&",  &saveptr);
+	if (!out) {
+		ret = -1;
+		goto free_out;
+	}
+	if (strcmp(out, none_fifo_name) == 0)
+		out = NULL;
+
+	err = strtok_r(NULL, "&&&&",  &saveptr);
+	if (!err) {
+		ret = -1;
+		goto free_out;
+	}
+	if (strcmp(err, none_fifo_name) == 0)
+		err = NULL;
+
+	ret = lxc_terminal_set_fifo(terminal, in, out, err, &fifofd_in);
+	if (ret < 0) {
+		ERROR("Faild to set fifos to console config");
+		ret = -1;
+		goto free_out;
+	}
+
+	if (lxc_mainloop_add_handler(terminal->descr, fifofd_in,
+	                             lxc_terminal_io_cb, terminal)) {
+		ERROR("console fifo not added to mainloop");
+		lxc_terminal_delete_fifo(fifofd_in, &terminal->fifos);
+		ret = -1;
+		goto free_out;
+	}
+
+free_out:
+	if (tmp)
+		free(tmp);
+	return ret;
+}
+
+#else
 int lxc_terminal_create(struct lxc_terminal *terminal)
 {
 	int ret;
@@ -868,6 +1808,7 @@ err:
 	lxc_terminal_delete(terminal);
 	return -ENODEV;
 }
+#endif
 
 int lxc_terminal_setup(struct lxc_conf *conf)
 {
@@ -883,6 +1824,18 @@ int lxc_terminal_setup(struct lxc_conf *conf)
 	if (ret < 0)
 		return -1;
 
+#ifdef HAVE_ISULAD
+        if (is_syslog(terminal->log_driver)) {
+                if (terminal->log_syslog_tag == NULL) {
+                        terminal->log_syslog_tag = malloc(16 * sizeof(char));
+                        (void)strlcpy(terminal->log_syslog_tag, conf->name, 16);
+                }
+                if (terminal->log_syslog_facility <= 0) {
+                        terminal->log_syslog_facility = LOG_DAEMON;
+                }
+                openlog(terminal->log_syslog_tag, LOG_PID, terminal->log_syslog_facility);
+        }
+#endif
 	ret = lxc_terminal_create_log_file(terminal);
 	if (ret < 0)
 		goto err;
@@ -1120,9 +2073,15 @@ int lxc_terminal_prepare_login(int fd)
 	if (ret < 0)
 		return -1;
 
+#ifdef HAVE_ISULAD
+	ret = set_stdfds(fd);
+	if (ret < 0)
+		return -1;
+#else
 	ret = lxc_terminal_set_stdfds(fd);
 	if (ret < 0)
 		return -1;
+#endif
 
 	if (fd > STDERR_FILENO)
 		close(fd);
@@ -1146,6 +2105,18 @@ void lxc_terminal_init(struct lxc_terminal *terminal)
 	terminal->peer = -EBADF;
 	terminal->log_fd = -EBADF;
 	lxc_terminal_info_init(&terminal->proxy);
+#ifdef HAVE_ISULAD
+	terminal->init_fifo[0] = NULL;
+	terminal->init_fifo[1] = NULL;
+	terminal->init_fifo[2] = NULL;
+	terminal->pipes[0][0] = -1;
+	terminal->pipes[0][1] = -1;
+	terminal->pipes[1][0] = -1;
+	terminal->pipes[1][1] = -1;
+	terminal->pipes[2][0] = -1;
+	terminal->pipes[2][1] = -1;
+	lxc_list_init(&terminal->fifos);
+#endif
 }
 
 void lxc_terminal_conf_free(struct lxc_terminal *terminal)
@@ -1155,6 +2126,15 @@ void lxc_terminal_conf_free(struct lxc_terminal *terminal)
 	if (terminal->buffer_size > 0 && terminal->ringbuf.addr)
 		lxc_ringbuf_release(&terminal->ringbuf);
 	lxc_terminal_signal_fini(terminal);
+#ifdef HAVE_ISULAD
+	/*isulad: free console fifos */
+	free(terminal->init_fifo[0]);
+	free(terminal->init_fifo[1]);
+	free(terminal->init_fifo[2]);
+	lxc_terminal_delete_fifo(-1, &terminal->fifos);
+	free(terminal->log_driver);
+	free(terminal->log_syslog_tag);
+#endif
 }
 
 int lxc_terminal_map_ids(struct lxc_conf *c, struct lxc_terminal *terminal)
@@ -1167,6 +2147,15 @@ int lxc_terminal_map_ids(struct lxc_conf *c, struct lxc_terminal *terminal)
 	if (strcmp(terminal->name, "") == 0)
 		return 0;
 
+#ifdef HAVE_ISULAD
+	ret = chown_mapped_root(terminal->name, c);
+	if (ret < 0) {
+		ERROR("Failed to chown terminal \"%s\"", terminal->name);
+		return -1;
+	}
+
+	TRACE("Chowned terminal \"%s\"", terminal->name);
+#else
 	ret = userns_exec_mapped_root(terminal->name, terminal->pts, c);
 	if (ret < 0) {
 		return log_error(-1, "Failed to chown terminal %d(%s)",
@@ -1174,6 +2163,7 @@ int lxc_terminal_map_ids(struct lxc_conf *c, struct lxc_terminal *terminal)
 	}
 
 	TRACE("Chowned terminal %d(%s)", terminal->pts, terminal->name);
+#endif
 
 	return 0;
 }
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index 11bba26..03bf439 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -58,6 +58,10 @@ AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
 	  -I $(top_srcdir)/src/lxc/tools \
 	  -pthread
 
+if HAVE_ISULAD
+AM_CFLAGS += -I $(top_srcdir)/src/lxc/json
+endif
+
 if ENABLE_APPARMOR
 AM_CFLAGS += -DHAVE_APPARMOR
 endif
diff --git a/src/tests/attach.c b/src/tests/attach.c
index 07e641d..f36caac 100644
--- a/src/tests/attach.c
+++ b/src/tests/attach.c
@@ -29,6 +29,9 @@
 #include "lxctest.h"
 #include "utils.h"
 #include "lsm/lsm.h"
+#ifdef HAVE_ISULAD
+#include "config.h"
+#endif
 
 #include <lxc/lxccontainer.h>
 
@@ -76,7 +79,11 @@ static void test_attach_lsm_set_config(struct lxc_container *ct)
 	ct->save_config(ct, NULL);
 }
 
+#ifdef HAVE_ISULAD
+static int test_attach_lsm_func_func(void* payload, int fd)
+#else
 static int test_attach_lsm_func_func(void* payload)
+#endif
 {
 	TSTOUT("%s", lsm_process_label_get(syscall(SYS_getpid)));
 	return 0;
@@ -187,7 +194,11 @@ static int  test_attach_lsm_func(struct lxc_container *ct) { return 0; }
 static int  test_attach_lsm_cmd(struct lxc_container *ct) { return 0; }
 #endif /* HAVE_APPARMOR || HAVE_SELINUX */
 
+#ifdef HAVE_ISULAD
+static int test_attach_func_func(void* payload, int fd)
+#else
 static int test_attach_func_func(void* payload)
+#endif
 {
 	TSTOUT("%d", (int)syscall(SYS_getpid));
 	return 0;
-- 
2.25.1

