From: Christian Brauner Date: Tue, 21 Nov 2017 20:29:57 +0000 (+0100) Subject: lxccontainer: add reboot2() API extension X-Git-Tag: lxc-3.0.0.beta1~130^2~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d39b10eba1837e3638925e653eb7121d1563cbe3;p=thirdparty%2Flxc.git lxccontainer: add reboot2() API extension This adds reboot2() as a new API extension. This function properly wait until a reboot succeeded. It takes a timeout argument. When set to > 0 reboot2() will block until the timeout is reached, if timeout is set to zero reboot2() will not block, if set to -1 reboot2() will block indefinitly. The struct state_client gets rename to lxc_state_client since it's more in line with other declarations. It also gets moved from the lxc_handler to the lxc_conf struct so that the state clients waiting for reboots don't get deallocated on reboot since the handler is deallocated on reboot. Signed-off-by: Christian Brauner --- diff --git a/src/lxc/commands.c b/src/lxc/commands.c index 4056fb7ac..d7e41e5a7 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -909,8 +909,8 @@ int lxc_cmd_add_state_client(const char *name, const char *lxcpath, if (state < 0) { TRACE("%s - Failed to retrieve state of container", strerror(errno)); return -1; - } else if (states[state]) { - TRACE("Container is %s state", lxc_state2str(state)); + } else if (states[state] == 1) { + TRACE("Container is in %s state", lxc_state2str(state)); return state; } @@ -1125,7 +1125,7 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler, struct lxc_epoll_descr *descr, const lxc_cmd_t cmd) { - struct state_client *client; + struct lxc_state_client *client; struct lxc_list *cur, *next; lxc_console_free(handler->conf, fd); @@ -1136,7 +1136,7 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler, } process_lock(); - lxc_list_for_each_safe(cur, &handler->state_clients, next) { + lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) { client = cur->elem; if (client->clientfd != fd) continue; diff --git a/src/lxc/commands_utils.c b/src/lxc/commands_utils.c index e03022f28..394f0a1d7 100644 --- a/src/lxc/commands_utils.c +++ b/src/lxc/commands_utils.c @@ -193,7 +193,7 @@ int lxc_cmd_connect(const char *name, const char *lxcpath, int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler, lxc_state_t states[MAX_STATE]) { - struct state_client *newclient; + struct lxc_state_client *newclient; struct lxc_list *tmplist; newclient = malloc(sizeof(*newclient)); @@ -212,7 +212,7 @@ int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler, process_lock(); lxc_list_add_elem(tmplist, newclient); - lxc_list_add_tail(&handler->state_clients, tmplist); + lxc_list_add_tail(&handler->conf->state_clients, tmplist); process_unlock(); TRACE("added state client %d to state client list", state_client_fd); diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 359482580..2aa057b1e 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2419,6 +2419,7 @@ struct lxc_conf *lxc_conf_init(void) for (i = 0; i < NUM_LXC_HOOKS; i++) lxc_list_init(&new->hooks[i]); lxc_list_init(&new->groups); + lxc_list_init(&new->state_clients); new->lsm_aa_profile = NULL; new->lsm_se_context = NULL; new->tmp_umount_proc = 0; diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 43fc2b450..ce259c42b 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -248,6 +248,11 @@ enum lxchooks { extern char *lxchook_names[NUM_LXC_HOOKS]; +struct lxc_state_client { + int clientfd; + lxc_state_t states[MAX_STATE]; +}; + struct lxc_conf { int is_execute; char *fstab; @@ -363,6 +368,8 @@ struct lxc_conf { /* init working directory */ char* init_cwd; + /* A list of clients registered to be informed about a container state. */ + struct lxc_list state_clients; }; int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 413dd375b..46ca47f0f 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -1793,6 +1793,73 @@ static bool do_lxcapi_reboot(struct lxc_container *c) WRAP_API(bool, lxcapi_reboot) +static bool do_lxcapi_reboot2(struct lxc_container *c, int timeout) +{ + int killret, ret; + pid_t pid; + int rebootsignal = SIGINT, state_client_fd = -1; + lxc_state_t states[MAX_STATE] = {0}; + + if (!c) + return false; + + if (!do_lxcapi_is_running(c)) + return true; + + pid = do_lxcapi_init_pid(c); + if (pid <= 0) + return true; + + if (c->lxc_conf && c->lxc_conf->rebootsignal) + rebootsignal = c->lxc_conf->rebootsignal; + + /* Add a new state client before sending the shutdown signal so that we + * don't miss a state. + */ + if (timeout != 0) { + states[RUNNING] = 2; + ret = lxc_cmd_add_state_client(c->name, c->config_path, states, + &state_client_fd); + if (ret < 0) + return false; + + if (state_client_fd < 0) + return false; + + if (ret == RUNNING) + return true; + + if (ret < MAX_STATE) + return false; + } + + /* Send reboot signal to container. */ + killret = kill(pid, rebootsignal); + if (killret < 0) { + WARN("Could not send signal %d to pid %d", rebootsignal, pid); + if (state_client_fd >= 0) + close(state_client_fd); + return false; + } + TRACE("Sent signal %d to pid %d", rebootsignal, pid); + + if (timeout == 0) + return true; + + ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout); + close(state_client_fd); + if (ret < 0) + return false; + + TRACE("Received state \"%s\"", lxc_state2str(ret)); + if (ret != RUNNING) + return false; + + return true; +} + +WRAP_API_1(bool, lxcapi_reboot2, int) + static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout) { int ret, state_client_fd = -1; @@ -4595,6 +4662,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath c->createl = lxcapi_createl; c->shutdown = lxcapi_shutdown; c->reboot = lxcapi_reboot; + c->reboot2 = lxcapi_reboot2; c->clear_config = lxcapi_clear_config; c->clear_config_item = lxcapi_clear_config_item; c->get_config_item = lxcapi_get_config_item; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 9ea67df7f..5938fa3d1 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -846,6 +846,17 @@ struct lxc_container { * \return \c 0 on success, nonzero on failure. */ int (*console_log)(struct lxc_container *c, struct lxc_console_log *log); + + /*! + * \brief Request the container reboot by sending it \c SIGINT. + * + * \param c Container. + * \param timeout Seconds to wait before returning false. + * (-1 to wait forever, 0 to avoid waiting). + * + * \return \c true if the container was rebooted successfully, else \c false. + */ + bool (*reboot2)(struct lxc_container *c, int timeout); }; /*! diff --git a/src/lxc/start.c b/src/lxc/start.c index a7bb6a665..ce13a3c50 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -200,6 +200,9 @@ restart: fddir = dirfd(dir); while ((direntp = readdir(dir))) { + struct lxc_list *cur; + bool matched = false; + if (!direntp) break; @@ -222,6 +225,20 @@ restart: (i < len_fds && fd == fds_to_ignore[i])) continue; + /* Keep state clients that wait on reboots. */ + lxc_list_for_each(cur, &conf->state_clients) { + struct lxc_state_client *client = cur->elem; + + if (client->clientfd != fd) + continue; + + matched = true; + break; + } + + if (matched) + continue; + if (current_config && fd == current_config->logfd) continue; @@ -338,14 +355,14 @@ static int lxc_serve_state_clients(const char *name, { ssize_t ret; struct lxc_list *cur, *next; - struct state_client *client; + struct lxc_state_client *client; struct lxc_msg msg = {.type = lxc_msg_state, .value = state}; handler->state = state; TRACE("Set container state to %s", lxc_state2str(state)); process_lock(); - if (lxc_list_empty(&handler->state_clients)) { + if (lxc_list_empty(&handler->conf->state_clients)) { TRACE("No state clients registered"); process_unlock(); lxc_monitor_send_state(name, state, handler->lxcpath); @@ -355,10 +372,10 @@ static int lxc_serve_state_clients(const char *name, strncpy(msg.name, name, sizeof(msg.name)); msg.name[sizeof(msg.name) - 1] = 0; - lxc_list_for_each_safe(cur, &handler->state_clients, next) { + lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) { client = cur->elem; - if (!client->states[state]) { + if (client->states[state] == 0) { TRACE("State %s not registered for state client %d", lxc_state2str(state), client->clientfd); continue; @@ -527,7 +544,8 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf, handler->lxcpath = lxcpath; handler->pinfd = -1; handler->state_socket_pair[0] = handler->state_socket_pair[1] = -1; - lxc_list_init(&handler->state_clients); + if (handler->conf->reboot == 0) + lxc_list_init(&handler->conf->state_clients); for (i = 0; i < LXC_NS_MAX; i++) handler->nsfd[i] = -1; @@ -550,10 +568,12 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf, handler->state_socket_pair[1]); } - handler->conf->maincmd_fd = lxc_cmd_init(name, lxcpath, "command"); - if (handler->conf->maincmd_fd < 0) { - ERROR("Failed to set up command socket"); - goto on_error; + if (handler->conf->reboot == 0) { + handler->conf->maincmd_fd = lxc_cmd_init(name, lxcpath, "command"); + if (handler->conf->maincmd_fd < 0) { + ERROR("Failed to set up command socket"); + goto on_error; + } } TRACE("Unix domain socket %d for command server is ready", handler->conf->maincmd_fd); @@ -711,9 +731,11 @@ void lxc_fini(const char *name, struct lxc_handler *handler) lxc_set_state(name, handler, STOPPED); - /* close command socket */ - close(handler->conf->maincmd_fd); - handler->conf->maincmd_fd = -1; + if (handler->conf->reboot == 0) { + /* close command socket */ + close(handler->conf->maincmd_fd); + handler->conf->maincmd_fd = -1; + } if (run_lxc_hooks(name, "post-stop", handler->conf, handler->lxcpath, NULL)) { ERROR("Failed to run lxc.hook.post-stop for container \"%s\".", name); @@ -735,8 +757,13 @@ void lxc_fini(const char *name, struct lxc_handler *handler) /* The command socket is now closed, no more state clients can register * themselves from now on. So free the list of state clients. */ - lxc_list_for_each_safe(cur, &handler->state_clients, next) { - struct state_client *client = cur->elem; + lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) { + struct lxc_state_client *client = cur->elem; + + /* Keep state clients that want to be notified about reboots. */ + if ((handler->conf->reboot > 0) && (client->states[RUNNING] == 2)) + continue; + /* close state client socket */ close(client->clientfd); lxc_list_del(cur); @@ -797,9 +824,8 @@ static int do_start(void *data) lxc_sync_fini_parent(handler); /* Don't leak the pinfd to the container. */ - if (handler->pinfd >= 0) { + if (handler->pinfd >= 0) close(handler->pinfd); - } if (lxc_sync_wait_parent(handler, LXC_SYNC_STARTUP)) return -1; diff --git a/src/lxc/start.h b/src/lxc/start.h index c67322693..e2b13141b 100644 --- a/src/lxc/start.h +++ b/src/lxc/start.h @@ -85,9 +85,6 @@ struct lxc_handler { /* The container's in-memory configuration. */ struct lxc_conf *conf; - /* A list of clients registered to be informed about a container state. */ - struct lxc_list state_clients; - /* A set of operations to be performed at various stages of the * container's life. */ @@ -110,11 +107,6 @@ struct lxc_operations { int (*post_start)(struct lxc_handler *, void *); }; -struct state_client { - int clientfd; - lxc_state_t states[MAX_STATE]; -}; - extern int lxc_poll(const char *name, struct lxc_handler *handler); extern int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state); extern void lxc_abort(const char *name, struct lxc_handler *handler); diff --git a/src/lxc/state.c b/src/lxc/state.c index 97b22a98e..9c9bf8318 100644 --- a/src/lxc/state.c +++ b/src/lxc/state.c @@ -45,9 +45,9 @@ lxc_log_define(lxc_state, lxc); -static const char * const strstate[] = { - "STOPPED", "STARTING", "RUNNING", "STOPPING", - "ABORTING", "FREEZING", "FROZEN", "THAWED", +static const char *const strstate[] = { + "STOPPED", "STARTING", "RUNNING", "STOPPING", + "ABORTING", "FREEZING", "FROZEN", "THAWED", }; const char *lxc_state2str(lxc_state_t state) diff --git a/src/lxc/tools/lxc_stop.c b/src/lxc/tools/lxc_stop.c index 53795a31e..9d2ba6ed1 100644 --- a/src/lxc/tools/lxc_stop.c +++ b/src/lxc/tools/lxc_stop.c @@ -94,62 +94,6 @@ Options :\n\ .timeout = -2, }; -/* returns -1 on failure, 0 on success */ -static int do_reboot_and_check(struct lxc_arguments *a, struct lxc_container *c) -{ - int ret; - pid_t pid; - pid_t newpid; - int timeout = a->timeout; - - pid = c->init_pid(c); - if (pid == -1) - return -1; - if (!c->reboot(c)) - return -1; - if (a->nowait) - return 0; - if (timeout == 0) - goto out; - - for (;;) { - /* can we use c-> wait for this, assuming it will - * re-enter RUNNING? For now just sleep */ - int elapsed_time, curtime = 0; - struct timeval tv; - - newpid = c->init_pid(c); - if (newpid != -1 && newpid != pid) - return 0; - - if (timeout != -1) { - ret = gettimeofday(&tv, NULL); - if (ret) - break; - curtime = tv.tv_sec; - } - - sleep(1); - if (timeout != -1) { - ret = gettimeofday(&tv, NULL); - if (ret) - break; - elapsed_time = tv.tv_sec - curtime; - if (timeout - elapsed_time <= 0) - break; - timeout -= elapsed_time; - } - } - -out: - newpid = c->init_pid(c); - if (newpid == -1 || newpid == pid) { - printf("Reboot did not complete before timeout\n"); - return -1; - } - return 0; -} - int main(int argc, char *argv[]) { struct lxc_container *c; @@ -259,7 +203,11 @@ int main(int argc, char *argv[]) /* reboot */ if (my_args.reboot) { - ret = do_reboot_and_check(&my_args, c) < 0 ? EXIT_SUCCESS : EXIT_FAILURE; + ret = c->reboot2(c, my_args.timeout); + if (ret < 0) + ret = EXIT_FAILURE; + else + ret = EXIT_SUCCESS; goto out; }