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,
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;
......
......@@ -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);
......
......@@ -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;
......
......@@ -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,
......
......@@ -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;
......
......@@ -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);
};
/*!
......
......@@ -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;
......
......@@ -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);
......
......@@ -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)
......
......@@ -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;
}
......
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