]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
commands: make state server interface flexible
authorChristian Brauner <christian.brauner@ubuntu.com>
Sun, 2 Jul 2017 13:42:07 +0000 (15:42 +0200)
committerChristian Brauner <christian.brauner@ubuntu.com>
Sat, 8 Jul 2017 22:14:46 +0000 (00:14 +0200)
This adds a little more flexibility to the state server. The idea is to have a
command socket function "lxc_cmd_add_state_client()" whose only task is to add
a new state client to the container's in-memory handler. This function returns
either the state of the container if it is already in the requested state or it
will return the newly registered client's fd in one of its arguments to the
caller. We then provide a separate helper function "lxc_cmd_sock_rcv_state()"
which can be passed the returned client fd and listens on the fd for the
requested state.
This is useful when we want to first register a client, then send a signal to
the container and wait for a state. This ensure that the client fd is
registered before the signal can have any effect and can e.g. be used to catch
something like the "STOPPING" state that is very ephemeral.

Additionally we provide a convenience function "lxc_cmd_sock_get_state()" which
combines both tasks and is used in e.g. "lxc_wait()".

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/Makefile.am
src/lxc/commands.c
src/lxc/commands.h
src/lxc/commands_utils.c [new file with mode: 0644]
src/lxc/commands_utils.h [new file with mode: 0644]
src/lxc/lxccontainer.c
src/lxc/state.c

index 54fad1226b87a46a200572cac2f5df686f253e05..f659f3736dae998e09436754be503b8ba2a3d3f9 100644 (file)
@@ -92,6 +92,7 @@ liblxc_la_SOURCES = \
        cgroups/cgfsng.c \
        cgroups/cgroup.c cgroups/cgroup.h \
        commands.c commands.h \
+       commands_utils.c commands_utils.h \
        start.c start.h \
        execute.c \
        monitor.c monitor.h \
index c39db35d2d6e34c654da316d68756a5396dac203..61ac6e708479ec7f463cb1dcd30f0b88c049780a 100644 (file)
@@ -812,21 +812,13 @@ static int lxc_cmd_get_lxcpath_callback(int fd, struct lxc_cmd_req *req,
        return lxc_cmd_rsp_send(fd, &rsp);
 }
 
-/*
- * lxc_cmd_add_state_client: register a client fd in the handler list
- *
- * @name      : name of container to connect to
- * @lxcpath   : the lxcpath in which the container is running
- *
- * Returns the lxcpath on success, NULL on failure.
- */
 int lxc_cmd_add_state_client(const char *name, const char *lxcpath,
-                            lxc_state_t states[MAX_STATE])
+                            lxc_state_t states[MAX_STATE],
+                            int *state_client_fd)
 {
        int stopped;
        ssize_t ret;
        int state = -1;
-       struct lxc_msg msg = {0};
        struct lxc_cmd_rr cmd = {
            .req = {
                .cmd     = LXC_CMD_ADD_STATE_CLIENT,
@@ -893,23 +885,8 @@ int lxc_cmd_add_state_client(const char *name, const char *lxcpath,
                return -1;
        }
 
-again:
-       ret = recv(cmd.rsp.ret, &msg, sizeof(msg), 0);
-       if (ret < 0) {
-               if (errno == EINTR)
-                       goto again;
-
-               ERROR("failed to receive message: %s", strerror(errno));
-               return -1;
-       }
-       if (ret == 0) {
-               ERROR("length of message was 0");
-               return -1;
-       }
-
-       TRACE("received state %s from state client %d",
-             lxc_state2str(msg.value), cmd.rsp.ret);
-       return msg.value;
+       *state_client_fd = cmd.rsp.ret;
+       return MAX_STATE;
 }
 
 static int lxc_cmd_add_state_client_callback(int fd, struct lxc_cmd_req *req,
index 6b48f69f0b07f115bbfcdfa4713aed090b22dee7..28428c774e788f024dfce0c3c2f659ad8eff599c 100644 (file)
 #ifndef __LXC_COMMANDS_H
 #define __LXC_COMMANDS_H
 
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+
 #include "state.h"
 
 #define LXC_CMD_DATA_MAX (MAXPATHLEN * 2)
@@ -85,8 +89,23 @@ extern char *lxc_cmd_get_lxcpath(const char *hashed_sock);
 extern pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath);
 extern int lxc_cmd_get_state(const char *name, const char *lxcpath);
 extern int lxc_cmd_stop(const char *name, const char *lxcpath);
+
+/* lxc_cmd_add_state_client    Register a new state client fd in the container's
+ *                             in-memory handler.
+ *
+ * @param[in] name             Name of container to connect to.
+ * @param[in] lxcpath          The lxcpath in which the container is running.
+ * @param[in] states           The states to wait for.
+ * @param[out] state_client_fd The state client fd from which the state can be
+ *                             received.
+ * @return                     Return  < 0 on error
+ *                                    == MAX_STATE when state needs to retrieved
+ *                                                 via socket fd
+ *                                     < MAX_STATE current container state
+ */
 extern int lxc_cmd_add_state_client(const char *name, const char *lxcpath,
-                                   lxc_state_t states[MAX_STATE]);
+                                   lxc_state_t states[MAX_STATE],
+                                   int *state_client_fd);
 
 struct lxc_epoll_descr;
 struct lxc_handler;
diff --git a/src/lxc/commands_utils.c b/src/lxc/commands_utils.c
new file mode 100644 (file)
index 0000000..1d5e9a5
--- /dev/null
@@ -0,0 +1,91 @@
+/* liblxcapi
+ *
+ * Copyright © 2017 Christian Brauner <christian.brauner@ubuntu.com>.
+ * Copyright © 2017 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "commands.h"
+#include "commands_utils.h"
+#include "log.h"
+#include "monitor.h"
+#include "state.h"
+
+lxc_log_define(lxc_commands_utils, lxc);
+
+int lxc_cmd_sock_rcv_state(int state_client_fd, int timeout)
+{
+       int ret;
+       struct lxc_msg msg;
+       struct timeval out;
+
+       memset(&out, 0, sizeof(out));
+       out.tv_sec = timeout;
+       ret = setsockopt(state_client_fd, SOL_SOCKET, SO_RCVTIMEO,
+                        (const void *)&out, sizeof(out));
+       if (ret < 0) {
+               SYSERROR("Failed to set %ds timeout on containter state socket", timeout);
+               return -1;
+       }
+
+       memset(&msg, 0, sizeof(msg));
+
+again:
+       ret = recv(state_client_fd, &msg, sizeof(msg), 0);
+       if (ret < 0) {
+               if (errno == EINTR)
+                       goto again;
+
+               ERROR("failed to receive message: %s", strerror(errno));
+               return -1;
+       }
+
+       if (ret == 0) {
+               ERROR("length of message was 0");
+               return -1;
+       }
+
+       TRACE("received state %s from state client %d",
+             lxc_state2str(msg.value), state_client_fd);
+
+       return msg.value;
+}
+
+/* Register a new state client and retrieve state from command socket. */
+int lxc_cmd_sock_get_state(const char *name, const char *lxcpath,
+                          lxc_state_t states[MAX_STATE], int timeout)
+{
+       int ret;
+       int state_client_fd;
+
+       ret = lxc_cmd_add_state_client(name, lxcpath, states, &state_client_fd);
+       if (ret < 0)
+               return -1;
+
+       if (ret < MAX_STATE)
+               return ret;
+
+       ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout);
+       close(state_client_fd);
+       return ret;
+}
diff --git a/src/lxc/commands_utils.h b/src/lxc/commands_utils.h
new file mode 100644 (file)
index 0000000..c1acdb5
--- /dev/null
@@ -0,0 +1,51 @@
+/* liblxcapi
+ *
+ * Copyright © 2017 Christian Brauner <christian.brauner@ubuntu.com>.
+ * Copyright © 2017 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __LXC_COMMANDS_UTILS_H
+#define __LXC_COMMANDS_UTILS_H
+
+#include <stdio.h>
+
+#include "state.h"
+
+/* lxc_cmd_sock_get_state      Register a new state client fd in the container's
+ *                             in-memory handler and retrieve the requested
+ *                             states.
+ *
+ * @param[in] name             Name of container to connect to.
+ * @param[in] lxcpath          The lxcpath in which the container is running.
+ * @param[in] states           The states to wait for.
+ * @return                     Return  < 0 on error
+ *                                     < MAX_STATE current container state
+ */
+extern int lxc_cmd_sock_get_state(const char *name, const char *lxcpath,
+                                 lxc_state_t states[MAX_STATE], int timeout);
+
+/* lxc_cmd_sock_rcv_state      Retrieve the requested state from a state client
+ *                             fd registerd in the container's in-memory
+ *                             handler.
+ *
+ * @param[int] state_client_fd The state client fd from which the state can be
+ *                             received.
+ * @return                     Return  < 0 on error
+ *                                     < MAX_STATE current container state
+ */
+extern int lxc_cmd_sock_rcv_state(int state_client_fd, int timeout);
+
+#endif /* __LXC_COMMANDS_UTILS_H */
index 1d59ef4a9c8ac92a9507eb7d48acf3c332050615..75dda44ed97cfc23a0ea83d42c5f6e74f221720c 100644 (file)
@@ -47,6 +47,7 @@
 #include "conf.h"
 #include "config.h"
 #include "commands.h"
+#include "commands_utils.h"
 #include "confile.h"
 #include "confile_legacy.h"
 #include "console.h"
@@ -1746,9 +1747,11 @@ WRAP_API(bool, lxcapi_reboot)
 
 static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout)
 {
-       bool retv;
+       int ret, state_client_fd = -1;
+       bool retv = false;
        pid_t pid;
        int haltsignal = SIGPWR;
+       lxc_state_t states[MAX_STATE] = {0};
 
        if (!c)
                return false;
@@ -1768,10 +1771,33 @@ static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout)
 
        INFO("Using signal number '%d' as halt signal.", haltsignal);
 
+       /* Add a new state client before sending the shutdown signal so that we
+        * don't miss a state.
+        */
+       states[STOPPED] = 1;
+       ret = lxc_cmd_add_state_client(c->name, c->config_path, states,
+                                      &state_client_fd);
+
+       /* Send shutdown signal to container. */
        if (kill(pid, haltsignal) < 0)
                WARN("Could not send signal %d to pid %d.", haltsignal, pid);
 
-       retv = do_lxcapi_wait(c, "STOPPED", timeout);
+       /* Retrieve the state. */
+       if (state_client_fd >= 0) {
+               int state;
+               state = lxc_cmd_sock_rcv_state(state_client_fd, timeout);
+               close(state_client_fd);
+               if (state != STOPPED)
+                       return false;
+               retv = true;
+       } else if (ret == STOPPED) {
+               TRACE("Container is already stopped");
+               retv = true;
+       } else {
+               TRACE("Received state \"%s\" instead of expected \"STOPPED\"",
+                     lxc_state2str(ret));
+       }
+
        return retv;
 }
 
index cab34d78b7ce0d35c7deda94f70aebdb153b8c37..97b22a98e55e9b8508f4abb3787c4ec8529e8aeb 100644 (file)
@@ -36,6 +36,7 @@
 
 #include "cgroup.h"
 #include "commands.h"
+#include "commands_utils.h"
 #include "config.h"
 #include "log.h"
 #include "lxc.h"
@@ -114,7 +115,7 @@ extern int lxc_wait(const char *lxcname, const char *states, int timeout,
        if (fillwaitedstates(states, s))
                return -1;
 
-       state = lxc_cmd_add_state_client(lxcname, lxcpath, s);
+       state = lxc_cmd_sock_get_state(lxcname, lxcpath, s, timeout);
        if (state < 0) {
                SYSERROR("failed to receive state from monitor");
                return -1;