From: Christian Brauner Date: Sun, 2 Jul 2017 13:42:07 +0000 (+0200) Subject: commands: make state server interface flexible X-Git-Tag: lxc-2.1.0~57^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=92e350183244023264af973092cccdb013dea394;p=thirdparty%2Flxc.git commands: make state server interface flexible 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 --- diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 54fad1226..f659f3736 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -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 \ diff --git a/src/lxc/commands.c b/src/lxc/commands.c index c39db35d2..61ac6e708 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -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, diff --git a/src/lxc/commands.h b/src/lxc/commands.h index 6b48f69f0..28428c774 100644 --- a/src/lxc/commands.h +++ b/src/lxc/commands.h @@ -24,6 +24,10 @@ #ifndef __LXC_COMMANDS_H #define __LXC_COMMANDS_H +#include +#include +#include + #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 index 000000000..1d5e9a51b --- /dev/null +++ b/src/lxc/commands_utils.c @@ -0,0 +1,91 @@ +/* liblxcapi + * + * Copyright © 2017 Christian Brauner . + * 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 +#include +#include +#include +#include +#include + +#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 index 000000000..c1acdb5f1 --- /dev/null +++ b/src/lxc/commands_utils.h @@ -0,0 +1,51 @@ +/* liblxcapi + * + * Copyright © 2017 Christian Brauner . + * 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 + +#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 */ diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 1d59ef4a9..75dda44ed 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -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; } diff --git a/src/lxc/state.c b/src/lxc/state.c index cab34d78b..97b22a98e 100644 --- a/src/lxc/state.c +++ b/src/lxc/state.c @@ -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;