]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
lxccontainer: add reboot2() API extension
authorChristian Brauner <christian.brauner@ubuntu.com>
Tue, 21 Nov 2017 20:29:57 +0000 (21:29 +0100)
committerChristian Brauner <christian.brauner@ubuntu.com>
Wed, 6 Dec 2017 15:01:22 +0000 (16:01 +0100)
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 <christian.brauner@ubuntu.com>
src/lxc/commands.c
src/lxc/commands_utils.c
src/lxc/conf.c
src/lxc/conf.h
src/lxc/lxccontainer.c
src/lxc/lxccontainer.h
src/lxc/start.c
src/lxc/start.h
src/lxc/state.c
src/lxc/tools/lxc_stop.c

index 4056fb7ac4df98acce27e44c63988a57a5585302..d7e41e5a738ce94d3bc2f08df5fe8a630944267e 100644 (file)
@@ -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;
index e03022f2821ab467ac966b58b0348cc1c7b253e7..394f0a1d7c3bdc6dc43ab73bb25ff166decddf57 100644 (file)
@@ -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);
index 359482580ebaa2cf1c9da6420f2b06faeb6ec677..2aa057b1e36d57e1b5d420406d892b9678dba46f 100644 (file)
@@ -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;
index 43fc2b450947adb81f02032c811f2b6f3b77e080..ce259c42b034b18eb7bc997a1ac400ff249e4f37 100644 (file)
@@ -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,
index 413dd375bd9a6d456de9fb9e0ab558188352c751..46ca47f0f74bf54f592c0886348fe56e89f7afea 100644 (file)
@@ -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;
index 9ea67df7f1d83b665aa67cad7a017c1a0e2328fa..5938fa3d1510897b220f242667873f30a59c33e2 100644 (file)
@@ -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);
 };
 
 /*!
index a7bb6a66531aa8ef9024b2dbfae566562079326b..ce13a3c50a504bb876c1f73c17c3e59979b5f4ad 100644 (file)
@@ -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;
index c6732269390b7fa3590a4c59b8e2a71ab0b57bac..e2b13141b04d1fb6a11a4153a71e4ea52179622e 100644 (file)
@@ -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);
index 97b22a98e55e9b8508f4abb3787c4ec8529e8aeb..9c9bf8318433751a535909b2554f3f833c2978aa 100644 (file)
@@ -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)
index 53795a31e7fde7445f6dd81e1effec0eeb1d22d8..9d2ba6ed14ddc3458447d4588a002946562edd41 100644 (file)
@@ -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;
        }