From b13e288b8a46262e2c062ddfc152f10739b1691a Mon Sep 17 00:00:00 2001 From: haozi007 Date: Wed, 15 Apr 2020 17:35:53 +0800 Subject: [PATCH 34/49] support timeout Signed-off-by: haozi007 --- 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 +#endif + #ifdef HAVE_ISULAD #include "exec_commands.h" -#endif -#if HAVE_SYS_PERSONALITY_H -#include +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 #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