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: 's avatarChristian Brauner <christian.brauner@ubuntu.com>
parent 24b0bd9a
...@@ -909,8 +909,8 @@ int lxc_cmd_add_state_client(const char *name, const char *lxcpath, ...@@ -909,8 +909,8 @@ int lxc_cmd_add_state_client(const char *name, const char *lxcpath,
if (state < 0) { if (state < 0) {
TRACE("%s - Failed to retrieve state of container", strerror(errno)); TRACE("%s - Failed to retrieve state of container", strerror(errno));
return -1; return -1;
} else if (states[state]) { } else if (states[state] == 1) {
TRACE("Container is %s state", lxc_state2str(state)); TRACE("Container is in %s state", lxc_state2str(state));
return state; return state;
} }
...@@ -1125,7 +1125,7 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler, ...@@ -1125,7 +1125,7 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler,
struct lxc_epoll_descr *descr, struct lxc_epoll_descr *descr,
const lxc_cmd_t cmd) const lxc_cmd_t cmd)
{ {
struct state_client *client; struct lxc_state_client *client;
struct lxc_list *cur, *next; struct lxc_list *cur, *next;
lxc_console_free(handler->conf, fd); lxc_console_free(handler->conf, fd);
...@@ -1136,7 +1136,7 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler, ...@@ -1136,7 +1136,7 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler,
} }
process_lock(); 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; client = cur->elem;
if (client->clientfd != fd) if (client->clientfd != fd)
continue; continue;
......
...@@ -193,7 +193,7 @@ int lxc_cmd_connect(const char *name, const char *lxcpath, ...@@ -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, int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler,
lxc_state_t states[MAX_STATE]) lxc_state_t states[MAX_STATE])
{ {
struct state_client *newclient; struct lxc_state_client *newclient;
struct lxc_list *tmplist; struct lxc_list *tmplist;
newclient = malloc(sizeof(*newclient)); newclient = malloc(sizeof(*newclient));
...@@ -212,7 +212,7 @@ int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler, ...@@ -212,7 +212,7 @@ int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler,
process_lock(); process_lock();
lxc_list_add_elem(tmplist, newclient); 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(); process_unlock();
TRACE("added state client %d to state client list", state_client_fd); TRACE("added state client %d to state client list", state_client_fd);
......
...@@ -2419,6 +2419,7 @@ struct lxc_conf *lxc_conf_init(void) ...@@ -2419,6 +2419,7 @@ struct lxc_conf *lxc_conf_init(void)
for (i = 0; i < NUM_LXC_HOOKS; i++) for (i = 0; i < NUM_LXC_HOOKS; i++)
lxc_list_init(&new->hooks[i]); lxc_list_init(&new->hooks[i]);
lxc_list_init(&new->groups); lxc_list_init(&new->groups);
lxc_list_init(&new->state_clients);
new->lsm_aa_profile = NULL; new->lsm_aa_profile = NULL;
new->lsm_se_context = NULL; new->lsm_se_context = NULL;
new->tmp_umount_proc = 0; new->tmp_umount_proc = 0;
......
...@@ -248,6 +248,11 @@ enum lxchooks { ...@@ -248,6 +248,11 @@ enum lxchooks {
extern char *lxchook_names[NUM_LXC_HOOKS]; extern char *lxchook_names[NUM_LXC_HOOKS];
struct lxc_state_client {
int clientfd;
lxc_state_t states[MAX_STATE];
};
struct lxc_conf { struct lxc_conf {
int is_execute; int is_execute;
char *fstab; char *fstab;
...@@ -363,6 +368,8 @@ struct lxc_conf { ...@@ -363,6 +368,8 @@ struct lxc_conf {
/* init working directory */ /* init working directory */
char* init_cwd; 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, int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf,
......
...@@ -1793,6 +1793,73 @@ static bool do_lxcapi_reboot(struct lxc_container *c) ...@@ -1793,6 +1793,73 @@ static bool do_lxcapi_reboot(struct lxc_container *c)
WRAP_API(bool, lxcapi_reboot) 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) static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout)
{ {
int ret, state_client_fd = -1; int ret, state_client_fd = -1;
...@@ -4595,6 +4662,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath ...@@ -4595,6 +4662,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
c->createl = lxcapi_createl; c->createl = lxcapi_createl;
c->shutdown = lxcapi_shutdown; c->shutdown = lxcapi_shutdown;
c->reboot = lxcapi_reboot; c->reboot = lxcapi_reboot;
c->reboot2 = lxcapi_reboot2;
c->clear_config = lxcapi_clear_config; c->clear_config = lxcapi_clear_config;
c->clear_config_item = lxcapi_clear_config_item; c->clear_config_item = lxcapi_clear_config_item;
c->get_config_item = lxcapi_get_config_item; c->get_config_item = lxcapi_get_config_item;
......
...@@ -846,6 +846,17 @@ struct lxc_container { ...@@ -846,6 +846,17 @@ struct lxc_container {
* \return \c 0 on success, nonzero on failure. * \return \c 0 on success, nonzero on failure.
*/ */
int (*console_log)(struct lxc_container *c, struct lxc_console_log *log); 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);
}; };
/*! /*!
......
...@@ -200,6 +200,9 @@ restart: ...@@ -200,6 +200,9 @@ restart:
fddir = dirfd(dir); fddir = dirfd(dir);
while ((direntp = readdir(dir))) { while ((direntp = readdir(dir))) {
struct lxc_list *cur;
bool matched = false;
if (!direntp) if (!direntp)
break; break;
...@@ -222,6 +225,20 @@ restart: ...@@ -222,6 +225,20 @@ restart:
(i < len_fds && fd == fds_to_ignore[i])) (i < len_fds && fd == fds_to_ignore[i]))
continue; 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) if (current_config && fd == current_config->logfd)
continue; continue;
...@@ -338,14 +355,14 @@ static int lxc_serve_state_clients(const char *name, ...@@ -338,14 +355,14 @@ static int lxc_serve_state_clients(const char *name,
{ {
ssize_t ret; ssize_t ret;
struct lxc_list *cur, *next; struct lxc_list *cur, *next;
struct state_client *client; struct lxc_state_client *client;
struct lxc_msg msg = {.type = lxc_msg_state, .value = state}; struct lxc_msg msg = {.type = lxc_msg_state, .value = state};
handler->state = state; handler->state = state;
TRACE("Set container state to %s", lxc_state2str(state)); TRACE("Set container state to %s", lxc_state2str(state));
process_lock(); process_lock();
if (lxc_list_empty(&handler->state_clients)) { if (lxc_list_empty(&handler->conf->state_clients)) {
TRACE("No state clients registered"); TRACE("No state clients registered");
process_unlock(); process_unlock();
lxc_monitor_send_state(name, state, handler->lxcpath); lxc_monitor_send_state(name, state, handler->lxcpath);
...@@ -355,10 +372,10 @@ static int lxc_serve_state_clients(const char *name, ...@@ -355,10 +372,10 @@ static int lxc_serve_state_clients(const char *name,
strncpy(msg.name, name, sizeof(msg.name)); strncpy(msg.name, name, sizeof(msg.name));
msg.name[sizeof(msg.name) - 1] = 0; 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; client = cur->elem;
if (!client->states[state]) { if (client->states[state] == 0) {
TRACE("State %s not registered for state client %d", TRACE("State %s not registered for state client %d",
lxc_state2str(state), client->clientfd); lxc_state2str(state), client->clientfd);
continue; continue;
...@@ -527,7 +544,8 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf, ...@@ -527,7 +544,8 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
handler->lxcpath = lxcpath; handler->lxcpath = lxcpath;
handler->pinfd = -1; handler->pinfd = -1;
handler->state_socket_pair[0] = handler->state_socket_pair[1] = -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++) for (i = 0; i < LXC_NS_MAX; i++)
handler->nsfd[i] = -1; handler->nsfd[i] = -1;
...@@ -550,10 +568,12 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf, ...@@ -550,10 +568,12 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
handler->state_socket_pair[1]); handler->state_socket_pair[1]);
} }
handler->conf->maincmd_fd = lxc_cmd_init(name, lxcpath, "command"); if (handler->conf->reboot == 0) {
if (handler->conf->maincmd_fd < 0) { handler->conf->maincmd_fd = lxc_cmd_init(name, lxcpath, "command");
ERROR("Failed to set up command socket"); if (handler->conf->maincmd_fd < 0) {
goto on_error; ERROR("Failed to set up command socket");
goto on_error;
}
} }
TRACE("Unix domain socket %d for command server is ready", TRACE("Unix domain socket %d for command server is ready",
handler->conf->maincmd_fd); handler->conf->maincmd_fd);
...@@ -711,9 +731,11 @@ void lxc_fini(const char *name, struct lxc_handler *handler) ...@@ -711,9 +731,11 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
lxc_set_state(name, handler, STOPPED); lxc_set_state(name, handler, STOPPED);
/* close command socket */ if (handler->conf->reboot == 0) {
close(handler->conf->maincmd_fd); /* close command socket */
handler->conf->maincmd_fd = -1; close(handler->conf->maincmd_fd);
handler->conf->maincmd_fd = -1;
}
if (run_lxc_hooks(name, "post-stop", handler->conf, handler->lxcpath, NULL)) { if (run_lxc_hooks(name, "post-stop", handler->conf, handler->lxcpath, NULL)) {
ERROR("Failed to run lxc.hook.post-stop for container \"%s\".", name); 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) ...@@ -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 /* The command socket is now closed, no more state clients can register
* themselves from now on. So free the list of state clients. * themselves from now on. So free the list of state clients.
*/ */
lxc_list_for_each_safe(cur, &handler->state_clients, next) { lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) {
struct state_client *client = cur->elem; 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 state client socket */
close(client->clientfd); close(client->clientfd);
lxc_list_del(cur); lxc_list_del(cur);
...@@ -797,9 +824,8 @@ static int do_start(void *data) ...@@ -797,9 +824,8 @@ static int do_start(void *data)
lxc_sync_fini_parent(handler); lxc_sync_fini_parent(handler);
/* Don't leak the pinfd to the container. */ /* Don't leak the pinfd to the container. */
if (handler->pinfd >= 0) { if (handler->pinfd >= 0)
close(handler->pinfd); close(handler->pinfd);
}
if (lxc_sync_wait_parent(handler, LXC_SYNC_STARTUP)) if (lxc_sync_wait_parent(handler, LXC_SYNC_STARTUP))
return -1; return -1;
......
...@@ -85,9 +85,6 @@ struct lxc_handler { ...@@ -85,9 +85,6 @@ struct lxc_handler {
/* The container's in-memory configuration. */ /* The container's in-memory configuration. */
struct lxc_conf *conf; 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 /* A set of operations to be performed at various stages of the
* container's life. * container's life.
*/ */
...@@ -110,11 +107,6 @@ struct lxc_operations { ...@@ -110,11 +107,6 @@ struct lxc_operations {
int (*post_start)(struct lxc_handler *, void *); 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_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 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); extern void lxc_abort(const char *name, struct lxc_handler *handler);
......
...@@ -45,9 +45,9 @@ ...@@ -45,9 +45,9 @@
lxc_log_define(lxc_state, lxc); lxc_log_define(lxc_state, lxc);
static const char * const strstate[] = { static const char *const strstate[] = {
"STOPPED", "STARTING", "RUNNING", "STOPPING", "STOPPED", "STARTING", "RUNNING", "STOPPING",
"ABORTING", "FREEZING", "FROZEN", "THAWED", "ABORTING", "FREEZING", "FROZEN", "THAWED",
}; };
const char *lxc_state2str(lxc_state_t state) const char *lxc_state2str(lxc_state_t state)
......
...@@ -94,62 +94,6 @@ Options :\n\ ...@@ -94,62 +94,6 @@ Options :\n\
.timeout = -2, .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[]) int main(int argc, char *argv[])
{ {
struct lxc_container *c; struct lxc_container *c;
...@@ -259,7 +203,11 @@ int main(int argc, char *argv[]) ...@@ -259,7 +203,11 @@ int main(int argc, char *argv[])
/* reboot */ /* reboot */
if (my_args.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; goto out;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment