From b13e288b8a46262e2c062ddfc152f10739b1691a Mon Sep 17 00:00:00 2001
From: haozi007 <liuhao27@huawei.com>
Date: Wed, 15 Apr 2020 17:35:53 +0800
Subject: [PATCH 34/49] support timeout

Signed-off-by: haozi007 <liuhao27@huawei.com>
---
 src/lxc/attach.c           | 104 ++++++++++++++++++++++++++++++++++++++--
 src/lxc/attach.h           |   6 +++
 src/lxc/commands.c         |   5 ++
 src/lxc/execute.c          |  12 +++++
 src/lxc/lxc.h              |  13 ++++-
 src/lxc/lxccontainer.c     |  41 ++++++++++++++--
 src/lxc/lxccontainer.h     |  16 +++++++
 src/lxc/start.c            | 115 +++++++++++++++++++++++++++++++++++++++++++--
 src/lxc/start.h            |   6 +++
 src/lxc/tools/arguments.h  |   2 +
 src/lxc/tools/lxc_attach.c |  10 ++++
 src/lxc/tools/lxc_start.c  |  14 ++++++
 src/lxc/utils.c            |  11 +++++
 src/lxc/utils.h            |   2 +
 14 files changed, 346 insertions(+), 11 deletions(-)

diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index cb480ed..510c069 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -45,12 +45,27 @@
 #include "terminal.h"
 #include "utils.h"
 
+#if HAVE_SYS_PERSONALITY_H
+#include <sys/personality.h>
+#endif
+
 #ifdef HAVE_ISULAD
 #include "exec_commands.h"
-#endif
 
-#if HAVE_SYS_PERSONALITY_H
-#include <sys/personality.h>
+typedef enum {
+	ATTACH_INIT,
+	ATTACH_TIMEOUT,
+	ATTACH_MAX,
+} attach_timeout_t;
+
+static volatile attach_timeout_t g_attach_timeout_state = ATTACH_INIT;
+
+struct attach_timeout_conf {
+	int64_t timeout;
+	unsigned long long start_time;
+	pid_t pid;
+};
+
 #endif
 
 lxc_log_define(attach, lxc);
@@ -1038,9 +1053,67 @@ static inline void lxc_attach_terminal_close_log(struct lxc_terminal *terminal)
 	close_prot_errno_disarm(terminal->log_fd);
 }
 
+#ifdef HAVE_ISULAD
+/* isulad: attach timeout thread function */
+static void* wait_attach_timeout(void *arg)
+{
+	struct attach_timeout_conf *conf = (struct attach_timeout_conf *)arg;
+
+	if (!conf || conf->timeout < 1)
+		goto out;
+	sleep(conf->timeout);
+	if (lxc_process_alive(conf->pid, conf->start_time)) {
+		g_attach_timeout_state = ATTACH_TIMEOUT;
+		if (kill(conf->pid, SIGKILL) < 0) {
+			ERROR("Failed to send signal %d to pid %d", SIGKILL, conf->pid);
+		}
+	}
+
+out:
+	free(conf);
+	return ((void *)0);
+}
+
+/* isulad: create attach timeout thread */
+static int create_attach_timeout_thread(int64_t attach_timeout, pid_t pid)
+{
+	int ret = 0;
+	pthread_t ptid;
+	pthread_attr_t attr;
+	struct attach_timeout_conf *timeout_conf = NULL;
+
+	timeout_conf = malloc(sizeof(struct attach_timeout_conf));
+	if (timeout_conf == NULL) {
+		ERROR("Failed to malloc attach timeout conf");
+		ret = -1;
+		goto out;
+	}
+
+	memset(timeout_conf, 0, sizeof(struct attach_timeout_conf));
+	timeout_conf->timeout = attach_timeout;
+	timeout_conf->pid = pid;
+	timeout_conf->start_time = lxc_get_process_startat(pid);
+
+	pthread_attr_init(&attr);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+	ret = pthread_create(&ptid, &attr, wait_attach_timeout, timeout_conf);
+	if (ret != 0) {
+		ERROR("Create attach wait timeout thread failed");
+		free(timeout_conf);
+		goto out;
+	}
+
+out:
+	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)
+#else
 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
 {
 	int i, ret, status;
 	int ipc_sockets[2];
@@ -1417,6 +1490,26 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
 
 		*attached_process = attached_pid;
 
+#ifdef HAVE_ISULAD
+		if (options->timeout > 0) {
+			ret = create_attach_timeout_thread(options->timeout, *attached_process);
+			if (ret) {
+				ERROR("Failed to create attach timeout thread for container.");
+				goto close_mainloop;
+			}
+		}
+		/* isulad: read error msg from pipe */
+		ssize_t size_read;
+		char errbuf[BUFSIZ + 1] = {0};
+
+		size_read = read(conf->errpipe[0], errbuf, BUFSIZ);
+		if (size_read > 0) {
+			if (err_msg)
+				*err_msg = safe_strdup(errbuf);
+			goto close_mainloop;
+		}
+#endif
+
 		/* Now shut down communication with child, we're done. */
 		shutdown(ipc_sockets[0], SHUT_RDWR);
 		close(ipc_sockets[0]);
@@ -1433,6 +1526,11 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
 			}
 		}
 
+#ifdef HAVE_ISULAD
+		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)
 			lxc_mainloop_close(&descr);
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/commands.c b/src/lxc/commands.c
index b21c12b..c32aef1 100644
--- a/src/lxc/commands.c
+++ b/src/lxc/commands.c
@@ -119,7 +119,12 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
 	int ret;
 	struct lxc_cmd_rsp *rsp = &cmd->rsp;
 
+#ifdef HAVE_ISULAD
+	/*isulad: add timeout 1s to avoid long block due to [lxc monitor] error*/
+	ret = lxc_abstract_unix_recv_fds_timeout(sock, &fd_rsp, 1, rsp, sizeof(*rsp), 1000 * 1000);
+#else
 	ret = lxc_abstract_unix_recv_fds(sock, &fd_rsp, 1, rsp, sizeof(*rsp));
+#endif
 	if (ret < 0)
 		return log_warn_errno(-1,
 				      errno, "Failed to receive response for command \"%s\"",
diff --git a/src/lxc/execute.c b/src/lxc/execute.c
index 7dd8358..59ff604 100644
--- a/src/lxc/execute.c
+++ b/src/lxc/execute.c
@@ -88,14 +88,26 @@ static struct lxc_operations execute_start_ops = {
 	.post_start = execute_post_start
 };
 
+#ifdef HAVE_ISULAD
+int lxc_execute(const char *name, char *const argv[], int quiet,
+		struct lxc_handler *handler, const char *lxcpath,
+		bool daemonize, int *error_num, unsigned int start_timeout)
+#else
 int lxc_execute(const char *name, char *const argv[], int quiet,
 		struct lxc_handler *handler, const char *lxcpath,
 		bool daemonize, int *error_num)
+#endif
 {
+
 	struct execute_args args = {.argv = argv, .quiet = quiet};
 
 	TRACE("Doing lxc_execute");
 	handler->conf->is_execute = true;
+#ifdef HAVE_ISULAD
+	return __lxc_start(handler, &execute_start_ops, &args, lxcpath,
+			   daemonize, error_num, start_timeout);
+#else
 	return __lxc_start(handler, &execute_start_ops, &args, lxcpath,
 			   daemonize, error_num);
+#endif
 }
diff --git a/src/lxc/lxc.h b/src/lxc/lxc.h
index 99fd422..ec2feaa 100644
--- a/src/lxc/lxc.h
+++ b/src/lxc/lxc.h
@@ -32,9 +32,14 @@ struct lxc_handler;
  * @daemonize    : whether or not the container is daemonized
  * Returns 0 on success, < 0 otherwise
  */
+#ifdef HAVE_ISULAD
+extern int lxc_start(char *const argv[], struct lxc_handler *handler,
+		     const char *lxcpath, bool daemonize, int *error_num,
+		     unsigned int start_timeout);
+#else
 extern int lxc_start(char *const argv[], struct lxc_handler *handler,
 		     const char *lxcpath, bool daemonize, int *error_num);
-
+#endif
 /*
  * Start the specified command inside an application container
  * @name         : the name of the container
@@ -44,9 +49,15 @@ extern int lxc_start(char *const argv[], struct lxc_handler *handler,
  * @daemonize    : whether or not the container is daemonized
  * Returns 0 on success, < 0 otherwise
  */
+#ifdef HAVE_ISULAD
+extern int lxc_execute(const char *name, char *const argv[], int quiet,
+		       struct lxc_handler *handler, const char *lxcpath,
+		       bool daemonize, int *error_num, unsigned int start_timeout);
+#else
 extern int lxc_execute(const char *name, char *const argv[], int quiet,
 		       struct lxc_handler *handler, const char *lxcpath,
 		       bool daemonize, int *error_num);
+#endif
 
 /*
  * Close the fd associated with the monitoring
diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index ce2b2bf..f622a63 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -1234,17 +1234,25 @@ reboot:
 		goto on_error;
 	}
 
+#ifdef HAVE_ISULAD
 	if (useinit) {
 		ret = lxc_execute(c->name, argv, 1, handler, c->config_path,
-				  c->daemonize, &c->error_num);
+				  c->daemonize, &c->error_num, c->start_timeout);
 	}
 	else {
-#ifdef HAVE_ISULAD
 		handler->disable_pty = c->disable_pty;
 		handler->open_stdin = c->open_stdin;
-#endif
+		ret = lxc_start(argv, handler, c->config_path, c->daemonize,
+				&c->error_num, c->start_timeout);
+#else
+	if (useinit) {
+		ret = lxc_execute(c->name, argv, 1, handler, c->config_path,
+				  c->daemonize, &c->error_num);
+	}
+	else {
 		ret = lxc_start(argv, handler, c->config_path, c->daemonize,
 				&c->error_num);
+#endif
 	}
 
 	if (conf->reboot == REBOOT_REQ) {
@@ -4200,8 +4208,13 @@ static int lxcapi_attach(struct lxc_container *c,
 
 	current_config = c->lxc_conf;
 
+#ifdef HAVE_ISULAD
+	ret = lxc_attach(c, exec_function, exec_payload, options,
+			 attached_process, &c->lxc_conf->errmsg);
+#else
 	ret = lxc_attach(c, exec_function, exec_payload, options,
 			 attached_process);
+#endif
 	current_config = NULL;
 	return ret;
 }
@@ -4221,7 +4234,11 @@ static int do_lxcapi_attach_run_wait(struct lxc_container *c,
 	command.program = (char *)program;
 	command.argv = (char **)argv;
 
+#ifdef HAVE_ISULAD
+	ret = lxc_attach(c, lxc_attach_run_command, &command, options, &pid, NULL);
+#else
 	ret = lxc_attach(c, lxc_attach_run_command, &command, options, &pid);
+#endif
 	if (ret < 0)
 		return ret;
 
@@ -5593,6 +5610,23 @@ static bool do_lxcapi_get_container_pids(struct lxc_container *c, pid_t **pids,s
 }
 
 WRAP_API_2(bool, lxcapi_get_container_pids, pid_t **,size_t *)
+
+/* isulad add start timeout */
+static bool do_lxcapi_set_start_timeout(struct lxc_container *c,  unsigned int start_timeout)
+{
+	if (!c || !c->lxc_conf)
+		return false;
+	if (container_mem_lock(c)) {
+		ERROR("Error getting mem lock");
+		return false;
+	}
+	c->start_timeout = start_timeout;
+	container_mem_unlock(c);
+	return true;
+}
+
+WRAP_API_1(bool, lxcapi_set_start_timeout, unsigned int)
+
 #endif
 
 #ifdef HAVE_ISULAD
@@ -5764,6 +5798,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
 	c->want_open_stdin = lxcapi_want_open_stdin;
 	c->clean_container_resource = lxcapi_clean_container_resource;
 	c->get_container_pids = lxcapi_get_container_pids;
+	c->set_start_timeout = lxcapi_set_start_timeout;
 #endif
 	return c;
 
diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h
index 6ede70c..2951ac7 100644
--- a/src/lxc/lxccontainer.h
+++ b/src/lxc/lxccontainer.h
@@ -125,6 +125,12 @@ struct lxc_container {
 	 * */
 	char *ocihookfile;
 
+	/*! isulad:
+	 * \private
+	 * start_timeout.
+	 */
+	unsigned int start_timeout;
+
 	/*!
 	 * \brief Determine if \c /var/lib/lxc/$name/config exists.
 	 *
@@ -960,6 +966,16 @@ struct lxc_container {
 	 * \return \c true on success, else \c false.
 	 */
 	bool (*get_container_pids)(struct lxc_container *c,pid_t **pids,size_t *pids_len);
+
+	/*! isulad add
+	 * \brief An API call to set start timeout
+	 *
+	 * \param c Container.
+	 * \param start_timeout Value of start timeout.
+	 *
+	 * \return \c true on success, else \c false.
+	 */
+	bool (*set_start_timeout)(struct lxc_container *c, unsigned int start_timeout);
 };
 
 /*!
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 70ce1bd..0bc1143 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -880,6 +880,21 @@ out_restore_sigmask:
 }
 
 #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;
@@ -2285,6 +2300,12 @@ static int lxc_spawn(struct lxc_handler *handler)
 		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.
         */
@@ -2341,6 +2362,13 @@ static int lxc_spawn(struct lxc_handler *handler)
 		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);
@@ -2368,17 +2396,82 @@ out_sync_fini:
 }
 
 #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);
+	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)
 {
-	int ret, status;
-#ifdef HAVE_ISULAD
-    int exit_code;
 #endif
+	int ret, status;
 	const char *name = handler->name;
 	struct lxc_conf *conf = handler->conf;
 	struct cgroup_ops *cgroup_ops;
@@ -2393,6 +2486,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;
@@ -2574,14 +2677,18 @@ static struct lxc_operations start_ops = {
 };
 
 int lxc_start(char *const argv[], struct lxc_handler *handler,
-	      const char *lxcpath, bool daemonize, int *error_num)
+	      const char *lxcpath, bool daemonize, int *error_num, unsigned int start_timeout)
 {
 	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,
diff --git a/src/lxc/start.h b/src/lxc/start.h
index 4fc3ff7..cea37bc 100644
--- a/src/lxc/start.h
+++ b/src/lxc/start.h
@@ -170,8 +170,14 @@ extern void lxc_end(struct lxc_handler *handler);
  */
 extern int lxc_check_inherited(struct lxc_conf *conf, bool closeall,
 			       int *fds_to_ignore, size_t len_fds);
+#ifdef HAVE_ISULAD
+extern 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);
+#else
 extern int __lxc_start(struct lxc_handler *, struct lxc_operations *, void *,
 		       const char *, bool, int *);
+#endif
 
 extern int resolve_clone_flags(struct lxc_handler *handler);
 
diff --git a/src/lxc/tools/arguments.h b/src/lxc/tools/arguments.h
index a6d9967..41ea109 100644
--- a/src/lxc/tools/arguments.h
+++ b/src/lxc/tools/arguments.h
@@ -47,6 +47,8 @@ struct lxc_arguments {
 	const char *suffix; /* isulad add, suffix used for connect with parent of execed process*/
 	int disable_pty;
 	int open_stdin;
+	unsigned int start_timeout; /* isulad: Seconds for waiting on a container to start before it is killed*/
+	int64_t attach_timeout; /* for lxc-attach */
 #endif
 
 	/* for lxc-console */
diff --git a/src/lxc/tools/lxc_attach.c b/src/lxc/tools/lxc_attach.c
index 48e18bb..a855a8d 100644
--- a/src/lxc/tools/lxc_attach.c
+++ b/src/lxc/tools/lxc_attach.c
@@ -81,6 +81,7 @@ static const struct option my_longopts[] = {
 	{"out-fifo", required_argument, 0, OPT_OUTPUT_FIFO},
 	{"err-fifo", required_argument, 0, OPT_STDERR_FIFO},
 	{"suffix", required_argument, 0, OPT_ATTACH_SUFFIX},
+	{"timeout", required_argument, 0, OPT_ATTACH_TIMEOUT},
 #endif
 	LXC_COMMON_OPTIONS
 };
@@ -141,6 +142,7 @@ Options :\n\
 #else
 "\
   --user        User ID (format: UID[:GID])\n\
+  --timeout   Timeout in seconds (default: 0)\n\
 "
 #endif
 ,
@@ -303,6 +305,13 @@ static int my_parser(struct lxc_arguments *args, int c, char *arg)
 	case OPT_ATTACH_SUFFIX:
 		args->suffix = arg;
 		break;
+	case OPT_ATTACH_TIMEOUT:
+		if(!is_non_negative_num(arg)) {
+			ERROR("Error attach timeout parameter:%s.\n", arg);
+			return -1;
+		}
+		args->attach_timeout = (unsigned int)atoll(arg);
+		break;
 #endif
 	}
 
@@ -581,6 +590,7 @@ int main(int argc, char *argv[])
 	attach_options.env_policy = env_policy;
 	attach_options.extra_env_vars = extra_env;
 	attach_options.extra_keep_env = extra_keep;
+	attach_options.timeout = my_args.attach_timeout;
 
 	if (my_args.argc > 0) {
 		command.program = my_args.argv[0];
diff --git a/src/lxc/tools/lxc_start.c b/src/lxc/tools/lxc_start.c
index 72a4494..b430a1e 100644
--- a/src/lxc/tools/lxc_start.c
+++ b/src/lxc/tools/lxc_start.c
@@ -29,6 +29,7 @@
 #include "log.h"
 
 #ifdef HAVE_ISULAD
+#include <ctype.h>
 #include "isulad_utils.h"
 #endif
 
@@ -61,6 +62,7 @@ static const struct option my_longopts[] = {
 	{"start-timeout", required_argument, 0, OPT_START_TIMEOUT},
 	{"disable-pty", no_argument, 0, OPT_DISABLE_PTY},
 	{"open-stdin", no_argument, 0, OPT_OPEN_STDIN},
+	{"start-timeout", required_argument, 0, OPT_START_TIMEOUT},
 #endif
 	LXC_COMMON_OPTIONS
 };
@@ -155,6 +157,13 @@ static int my_parser(struct lxc_arguments *args, int c, char *arg)
 	case OPT_OPEN_STDIN:
 		args->open_stdin = 1;
 		break;
+	case OPT_START_TIMEOUT:
+		if(!is_non_negative_num(arg)) {
+			fprintf(stderr, "Error start timeout parameter:%s.\n", arg);
+			return -1;
+		}
+		args->start_timeout = (unsigned int)atoi(arg);
+		break;
 #endif
 
 	}
@@ -354,6 +363,11 @@ int main(int argc, char *argv[])
 	if (my_args.open_stdin) {
 		c->want_open_stdin(c, true);
 	}
+
+	/* isulad: add start timeout */
+	if(my_args.start_timeout) {
+		c->set_start_timeout(c, my_args.start_timeout);
+	}
 #endif
 
 	if (my_args.console)
diff --git a/src/lxc/utils.c b/src/lxc/utils.c
index ba69995..810b7fe 100644
--- a/src/lxc/utils.c
+++ b/src/lxc/utils.c
@@ -2167,4 +2167,15 @@ out:
 	return alive;
 }
 
+bool is_non_negative_num(const char *s)
+{
+	if (!s || !strcmp(s, ""))
+		return false;
+	while(*s  != '\0') {
+		if(!isdigit(*s))
+			return false;
+		++s;
+	}
+	return true;
+}
 #endif
diff --git a/src/lxc/utils.h b/src/lxc/utils.h
index a213ba7..39ef579 100644
--- a/src/lxc/utils.h
+++ b/src/lxc/utils.h
@@ -324,6 +324,8 @@ extern int unsigned long long lxc_get_process_startat(pid_t pid);
 extern int lxc_setup_env_home(uid_t uid);
 
 extern bool lxc_process_alive(pid_t pid, unsigned long long start_time);
+
+extern bool is_non_negative_num(const char *s);
 #endif
 
 #endif /* __LXC_UTILS_H */
-- 
1.8.3.1

