Commit 3f0af363 by Serge Hallyn Committed by GitHub

Merge pull request #1659 from brauner/2017-06-28/do_not_use_cmd_socket_on_daemonized_start

start: use separate socket on daemonized start
parents 616e0593 ee8377bd
...@@ -96,6 +96,7 @@ src/tests/lxc-test-utils* ...@@ -96,6 +96,7 @@ src/tests/lxc-test-utils*
src/tests/lxc-usernic-test src/tests/lxc-usernic-test
src/tests/lxc-test-config-jump-table src/tests/lxc-test-config-jump-table
src/tests/lxc-test-parse-config-file src/tests/lxc-test-parse-config-file
src/tests/lxc-test-shortlived
config/compile config/compile
config/config.guess config/config.guess
......
...@@ -92,6 +92,7 @@ liblxc_la_SOURCES = \ ...@@ -92,6 +92,7 @@ liblxc_la_SOURCES = \
cgroups/cgfsng.c \ cgroups/cgfsng.c \
cgroups/cgroup.c cgroups/cgroup.h \ cgroups/cgroup.c cgroups/cgroup.h \
commands.c commands.h \ commands.c commands.h \
commands_utils.c commands_utils.h \
start.c start.h \ start.c start.h \
execute.c \ execute.c \
monitor.c monitor.h \ monitor.c monitor.h \
......
...@@ -24,6 +24,10 @@ ...@@ -24,6 +24,10 @@
#ifndef __LXC_COMMANDS_H #ifndef __LXC_COMMANDS_H
#define __LXC_COMMANDS_H #define __LXC_COMMANDS_H
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include "state.h" #include "state.h"
#define LXC_CMD_DATA_MAX (MAXPATHLEN * 2) #define LXC_CMD_DATA_MAX (MAXPATHLEN * 2)
...@@ -43,7 +47,7 @@ typedef enum { ...@@ -43,7 +47,7 @@ typedef enum {
LXC_CMD_GET_CONFIG_ITEM, LXC_CMD_GET_CONFIG_ITEM,
LXC_CMD_GET_NAME, LXC_CMD_GET_NAME,
LXC_CMD_GET_LXCPATH, LXC_CMD_GET_LXCPATH,
LXC_CMD_STATE_SERVER, LXC_CMD_ADD_STATE_CLIENT,
LXC_CMD_MAX, LXC_CMD_MAX,
} lxc_cmd_t; } lxc_cmd_t;
...@@ -85,8 +89,23 @@ extern char *lxc_cmd_get_lxcpath(const char *hashed_sock); ...@@ -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 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_get_state(const char *name, const char *lxcpath);
extern int lxc_cmd_stop(const char *name, const char *lxcpath); extern int lxc_cmd_stop(const char *name, const char *lxcpath);
extern int lxc_cmd_state_server(const char *name, const char *lxcpath,
lxc_state_t states[MAX_STATE]); /* 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],
int *state_client_fd);
struct lxc_epoll_descr; struct lxc_epoll_descr;
struct lxc_handler; struct lxc_handler;
......
/* 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
#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "af_unix.h"
#include "commands.h"
#include "commands_utils.h"
#include "initutils.h"
#include "log.h"
#include "monitor.h"
#include "state.h"
#include "utils.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;
if (timeout >= 0) {
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) {
TRACE("Caught EINTR; retrying");
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;
}
int lxc_make_abstract_socket_name(char *path, int len, const char *lxcname,
const char *lxcpath,
const char *hashed_sock_name,
const char *suffix)
{
const char *name;
char *tmppath;
size_t tmplen;
uint64_t hash;
int ret;
name = lxcname;
if (!name)
name = "";
if (hashed_sock_name != NULL) {
ret =
snprintf(path, len, "lxc/%s/%s", hashed_sock_name, suffix);
if (ret < 0 || ret >= len) {
ERROR("Failed to create abstract socket name");
return -1;
}
return 0;
}
if (!lxcpath) {
lxcpath = lxc_global_config_value("lxc.lxcpath");
if (!lxcpath) {
ERROR("Failed to allocate memory");
return -1;
}
}
ret = snprintf(path, len, "%s/%s/%s", lxcpath, name, suffix);
if (ret < 0) {
ERROR("Failed to create abstract socket name");
return -1;
}
if (ret < len)
return 0;
/* ret >= len; lxcpath or name is too long. hash both */
tmplen = strlen(name) + strlen(lxcpath) + 2;
tmppath = alloca(tmplen);
ret = snprintf(tmppath, tmplen, "%s/%s", lxcpath, name);
if (ret < 0 || (size_t)ret >= tmplen) {
ERROR("Failed to create abstract socket name");
return -1;
}
hash = fnv_64a_buf(tmppath, ret, FNV1A_64_INIT);
ret = snprintf(path, len, "lxc/%016" PRIx64 "/%s", hash, suffix);
if (ret < 0 || ret >= len) {
ERROR("Failed to create abstract socket name");
return -1;
}
return 0;
}
int lxc_cmd_connect(const char *name, const char *lxcpath,
const char *hashed_sock_name)
{
int ret, client_fd;
char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = {0};
char *offset = &path[1];
size_t len = sizeof(path) - 2;
/* -2 here because this is an abstract unix socket so it needs a
* leading \0, and we null terminate, so it needs a trailing \0.
* Although null termination isn't required by the API, we do it anyway
* because we print the sockname out sometimes.
*/
ret = lxc_make_abstract_socket_name(offset, len, name, lxcpath,
hashed_sock_name, "command");
if (ret < 0)
return -1;
/* Get new client fd. */
client_fd = lxc_abstract_unix_connect(path);
if (client_fd < 0) {
if (errno == ECONNREFUSED)
return -ECONNREFUSED;
return -1;
}
return client_fd;
}
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_list *tmplist;
newclient = malloc(sizeof(*newclient));
if (!newclient)
return -ENOMEM;
/* copy requested states */
memcpy(newclient->states, states, sizeof(newclient->states));
newclient->clientfd = state_client_fd;
tmplist = malloc(sizeof(*tmplist));
if (!tmplist) {
free(newclient);
return -ENOMEM;
}
lxc_list_add_elem(tmplist, newclient);
lxc_list_add_tail(&handler->state_clients, tmplist);
TRACE("added state client %d to state client list", state_client_fd);
return 0;
}
/* 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"
#include "commands.h"
int lxc_make_abstract_socket_name(char *path, int len, const char *lxcname,
const char *lxcpath,
const char *hashed_sock_name,
const char *suffix);
/* 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);
/* lxc_add_state_client Add a new state client to the container's
* in-memory handler.
*
* @param[int] state_client_fd The state client fd to add.
* @param[int] handler The container's in-memory handler.
* @param[in] states The states to wait for.
*
* @return Return < 0 on error
* 0 on success
*/
extern int lxc_add_state_client(int state_client_fd,
struct lxc_handler *handler,
lxc_state_t states[MAX_STATE]);
/* lxc_cmd_connect Connect to the container's command socket.
*
* @param[in] name Name of container to connect to.
* @param[in] lxcpath The lxcpath in which the container is running.
* @param[in] hashed_sock_name The hashed name of the socket (optional). Can be
* NULL.
*
* @return Return < 0 on error
* >= 0 client fd
*/
extern int lxc_cmd_connect(const char *name, const char *lxcpath,
const char *hashed_sock_name);
#endif /* __LXC_COMMANDS_UTILS_H */
...@@ -797,7 +797,7 @@ static void do_restore(struct lxc_container *c, int status_pipe, struct migrate_ ...@@ -797,7 +797,7 @@ static void do_restore(struct lxc_container *c, int status_pipe, struct migrate_
close(fd); close(fd);
} }
handler = lxc_init_handler(c->name, c->lxc_conf, c->config_path); handler = lxc_init_handler(c->name, c->lxc_conf, c->config_path, false);
if (!handler) if (!handler)
goto out; goto out;
......
...@@ -116,7 +116,7 @@ int lxc_execute(const char *name, char *const argv[], int quiet, ...@@ -116,7 +116,7 @@ int lxc_execute(const char *name, char *const argv[], int quiet,
{ {
struct execute_args args = {.argv = argv, .quiet = quiet}; struct execute_args args = {.argv = argv, .quiet = quiet};
if (lxc_check_inherited(handler->conf, false, handler->conf->maincmd_fd)) if (lxc_check_inherited(handler->conf, false, &handler->conf->maincmd_fd, 1))
return -1; return -1;
handler->conf->is_execute = 1; handler->conf->is_execute = 1;
......
...@@ -370,7 +370,7 @@ int lxc_monitord_spawn(const char *lxcpath) ...@@ -370,7 +370,7 @@ int lxc_monitord_spawn(const char *lxcpath)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
lxc_check_inherited(NULL, true, pipefd[1]); lxc_check_inherited(NULL, true, &pipefd[1], 1);
if (null_stdfds() < 0) { if (null_stdfds() < 0) {
SYSERROR("Failed to dup2() standard file descriptors to /dev/null."); SYSERROR("Failed to dup2() standard file descriptors to /dev/null.");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
......
...@@ -1410,7 +1410,7 @@ static bool is_ovs_bridge(const char *bridge) ...@@ -1410,7 +1410,7 @@ static bool is_ovs_bridge(const char *bridge)
*/ */
static void ovs_cleanup_nic(const char *lxcpath, const char *name, const char *bridge, const char *nic) static void ovs_cleanup_nic(const char *lxcpath, const char *name, const char *bridge, const char *nic)
{ {
if (lxc_check_inherited(NULL, true, -1) < 0) if (lxc_check_inherited(NULL, true, &(int){-1}, 1) < 0)
return; return;
if (lxc_wait(name, "STOPPED", -1, lxcpath) < 0) if (lxc_wait(name, "STOPPED", -1, lxcpath) < 0)
return; return;
......
...@@ -67,6 +67,7 @@ ...@@ -67,6 +67,7 @@
#include "caps.h" #include "caps.h"
#include "cgroup.h" #include "cgroup.h"
#include "commands.h" #include "commands.h"
#include "commands_utils.h"
#include "conf.h" #include "conf.h"
#include "console.h" #include "console.h"
#include "error.h" #include "error.h"
...@@ -189,18 +190,12 @@ static int match_fd(int fd) ...@@ -189,18 +190,12 @@ static int match_fd(int fd)
return (fd == 0 || fd == 1 || fd == 2); return (fd == 0 || fd == 1 || fd == 2);
} }
/* Check for any fds we need to close. int lxc_check_inherited(struct lxc_conf *conf, bool closeall,
* - If fd_to_ignore != -1, then if we find that fd open we will ignore it. int *fds_to_ignore, size_t len_fds)
* - By default we warn about open fds we find.
* - If closeall is true, we will close open fds.
* - If lxc-start was passed "-C", then conf->close_all_fds will be true, in
* which case we also close all open fds.
* - A daemonized container will always pass closeall=true.
*/
int lxc_check_inherited(struct lxc_conf *conf, bool closeall, int fd_to_ignore)
{ {
struct dirent *direntp; struct dirent *direntp;
int fd, fddir; int fd, fddir;
size_t i;
DIR *dir; DIR *dir;
if (conf && conf->close_all_fds) if (conf && conf->close_all_fds)
...@@ -230,7 +225,12 @@ restart: ...@@ -230,7 +225,12 @@ restart:
continue; continue;
} }
if (fd == fddir || fd == lxc_log_fd || fd == fd_to_ignore) for (i = 0; i < len_fds; i++)
if (fds_to_ignore[i] == fd)
break;
if (fd == fddir || fd == lxc_log_fd ||
(i < len_fds && fd == fds_to_ignore[i]))
continue; continue;
if (current_config && fd == current_config->logfd) if (current_config && fd == current_config->logfd)
...@@ -343,8 +343,9 @@ static int signal_handler(int fd, uint32_t events, void *data, ...@@ -343,8 +343,9 @@ static int signal_handler(int fd, uint32_t events, void *data,
return 1; return 1;
} }
int lxc_set_state(const char *name, struct lxc_handler *handler, static int lxc_serve_state_clients(const char *name,
lxc_state_t state) struct lxc_handler *handler,
lxc_state_t state)
{ {
ssize_t ret; ssize_t ret;
struct lxc_list *cur, *next; struct lxc_list *cur, *next;
...@@ -352,8 +353,9 @@ int lxc_set_state(const char *name, struct lxc_handler *handler, ...@@ -352,8 +353,9 @@ int lxc_set_state(const char *name, struct lxc_handler *handler,
struct lxc_msg msg = {.type = lxc_msg_state, .value = state}; struct lxc_msg msg = {.type = lxc_msg_state, .value = state};
process_lock(); process_lock();
/* Only set state under process lock held so that we don't cause /* Only set state under process lock held so that we don't cause
* lxc_cmd_state_server() to miss a state. * lxc_cmd_add_state_client() to miss a 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));
...@@ -382,9 +384,11 @@ int lxc_set_state(const char *name, struct lxc_handler *handler, ...@@ -382,9 +384,11 @@ int lxc_set_state(const char *name, struct lxc_handler *handler,
again: again:
ret = send(client->clientfd, &msg, sizeof(msg), 0); ret = send(client->clientfd, &msg, sizeof(msg), 0);
if (ret < 0) { if (ret <= 0) {
if (errno == EINTR) if (errno == EINTR) {
TRACE("Caught EINTR; retrying");
goto again; goto again;
}
ERROR("failed to send message to client"); ERROR("failed to send message to client");
} }
...@@ -397,6 +401,60 @@ int lxc_set_state(const char *name, struct lxc_handler *handler, ...@@ -397,6 +401,60 @@ int lxc_set_state(const char *name, struct lxc_handler *handler,
} }
process_unlock(); process_unlock();
return 0;
}
static int lxc_serve_state_socket_pair(const char *name,
struct lxc_handler *handler,
lxc_state_t state)
{
ssize_t ret;
if (!handler->backgrounded ||
handler->state_socket_pair[1] < 0 ||
state == STARTING)
return 0;
/* Close read end of the socket pair. */
close(handler->state_socket_pair[0]);
handler->state_socket_pair[0] = -1;
again:
ret = lxc_abstract_unix_send_credential(handler->state_socket_pair[1],
&(int){state}, sizeof(int));
if (ret != sizeof(int)) {
if (errno == EINTR)
goto again;
SYSERROR("Failed to send state to %d",
handler->state_socket_pair[1]);
return -1;
}
TRACE("Sent container state \"%s\" to %d", lxc_state2str(state),
handler->state_socket_pair[1]);
/* Close write end of the socket pair. */
close(handler->state_socket_pair[1]);
handler->state_socket_pair[1] = -1;
return 0;
}
int lxc_set_state(const char *name, struct lxc_handler *handler,
lxc_state_t state)
{
int ret;
ret = lxc_serve_state_socket_pair(name, handler, state);
if (ret < 0) {
ERROR("Failed to synchronize via anonymous pair of unix sockets");
return -1;
}
ret = lxc_serve_state_clients(name, handler, state);
if (ret < 0)
return -1;
/* This function will try to connect to the legacy lxc-monitord state /* This function will try to connect to the legacy lxc-monitord state
* server and only exists for backwards compatibility. * server and only exists for backwards compatibility.
*/ */
...@@ -459,6 +517,12 @@ void lxc_free_handler(struct lxc_handler *handler) ...@@ -459,6 +517,12 @@ void lxc_free_handler(struct lxc_handler *handler)
if (handler->conf && handler->conf->maincmd_fd) if (handler->conf && handler->conf->maincmd_fd)
close(handler->conf->maincmd_fd); close(handler->conf->maincmd_fd);
if (handler->state_socket_pair[0] >= 0)
close(handler->state_socket_pair[0]);
if (handler->state_socket_pair[1] >= 0)
close(handler->state_socket_pair[1]);
if (handler->name) if (handler->name)
free(handler->name); free(handler->name);
...@@ -467,9 +531,9 @@ void lxc_free_handler(struct lxc_handler *handler) ...@@ -467,9 +531,9 @@ void lxc_free_handler(struct lxc_handler *handler)
} }
struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf, struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
const char *lxcpath) const char *lxcpath, bool daemonize)
{ {
int i; int i, ret;
struct lxc_handler *handler; struct lxc_handler *handler;
handler = malloc(sizeof(*handler)); handler = malloc(sizeof(*handler));
...@@ -484,6 +548,7 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf, ...@@ -484,6 +548,7 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
handler->conf = conf; handler->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;
lxc_list_init(&handler->state_clients); lxc_list_init(&handler->state_clients);
for (i = 0; i < LXC_NS_MAX; i++) for (i = 0; i < LXC_NS_MAX; i++)
...@@ -495,6 +560,22 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf, ...@@ -495,6 +560,22 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
goto on_error; goto on_error;
} }
if (daemonize && !handler->conf->reboot) {
/* Create socketpair() to synchronize on daemonized startup.
* When the container reboots we don't need to synchronize again
* currently so don't open another socketpair().
*/
ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,
handler->state_socket_pair);
if (ret < 0) {
ERROR("Failed to create anonymous pair of unix sockets");
goto on_error;
}
TRACE("Created anonymous pair {%d,%d} of unix sockets",
handler->state_socket_pair[0],
handler->state_socket_pair[1]);
}
if (lxc_cmd_init(name, handler, lxcpath)) { if (lxc_cmd_init(name, handler, lxcpath)) {
ERROR("failed to set up command socket"); ERROR("failed to set up command socket");
goto on_error; goto on_error;
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include <signal.h> #include <signal.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdbool.h> #include <stdbool.h>
#include "conf.h" #include "conf.h"
...@@ -50,6 +52,10 @@ struct lxc_handler { ...@@ -50,6 +52,10 @@ struct lxc_handler {
bool backgrounded; // indicates whether should we close std{in,out,err} on start bool backgrounded; // indicates whether should we close std{in,out,err} on start
int nsfd[LXC_NS_MAX]; int nsfd[LXC_NS_MAX];
int netnsfd; int netnsfd;
/* The socketpair() fds used to wait on successful daemonized
* startup.
*/
int state_socket_pair[2];
struct lxc_list state_clients; struct lxc_list state_clients;
}; };
...@@ -68,12 +74,21 @@ extern int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_stat ...@@ -68,12 +74,21 @@ extern int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_stat
extern void lxc_abort(const char *name, struct lxc_handler *handler); extern void lxc_abort(const char *name, struct lxc_handler *handler);
extern struct lxc_handler *lxc_init_handler(const char *name, extern struct lxc_handler *lxc_init_handler(const char *name,
struct lxc_conf *conf, struct lxc_conf *conf,
const char *lxcpath); const char *lxcpath,
bool daemonize);
extern void lxc_free_handler(struct lxc_handler *handler); extern void lxc_free_handler(struct lxc_handler *handler);
extern int lxc_init(const char *name, struct lxc_handler *handler); extern int lxc_init(const char *name, struct lxc_handler *handler);
extern void lxc_fini(const char *name, struct lxc_handler *handler); extern void lxc_fini(const char *name, struct lxc_handler *handler);
extern int lxc_check_inherited(struct lxc_conf *conf, bool closeall, int fd_to_ignore); /* lxc_check_inherited: Check for any open file descriptors and close them if
* requested.
* @param[in] conf The container's configuration.
* @param[in] closeall Whether we should close all open file descriptors.
* @param[in] fds_to_ignore Array of file descriptors to ignore.
* @param[in] len_fds Length of fds_to_ignore array.
*/
extern int lxc_check_inherited(struct lxc_conf *conf, bool closeall,
int *fds_to_ignore, size_t len_fds);
int __lxc_start(const char *, struct lxc_handler *, struct lxc_operations *, int __lxc_start(const char *, struct lxc_handler *, struct lxc_operations *,
void *, const char *, bool); void *, const char *, bool);
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "cgroup.h" #include "cgroup.h"
#include "commands.h" #include "commands.h"
#include "commands_utils.h"
#include "config.h" #include "config.h"
#include "log.h" #include "log.h"
#include "lxc.h" #include "lxc.h"
...@@ -114,7 +115,7 @@ extern int lxc_wait(const char *lxcname, const char *states, int timeout, ...@@ -114,7 +115,7 @@ extern int lxc_wait(const char *lxcname, const char *states, int timeout,
if (fillwaitedstates(states, s)) if (fillwaitedstates(states, s))
return -1; return -1;
state = lxc_cmd_state_server(lxcname, lxcpath, s); state = lxc_cmd_sock_get_state(lxcname, lxcpath, s, timeout);
if (state < 0) { if (state < 0) {
SYSERROR("failed to receive state from monitor"); SYSERROR("failed to receive state from monitor");
return -1; return -1;
......
...@@ -178,6 +178,8 @@ int main(int argc, char *argv[]) ...@@ -178,6 +178,8 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
if (!pid) { if (!pid) {
int ret;
/* restore default signal handlers */ /* restore default signal handlers */
for (i = 1; i < NSIG; i++) for (i = 1; i < NSIG; i++)
signal(i, SIG_DFL); signal(i, SIG_DFL);
...@@ -189,9 +191,9 @@ int main(int argc, char *argv[]) ...@@ -189,9 +191,9 @@ int main(int argc, char *argv[])
NOTICE("About to exec '%s'", aargv[0]); NOTICE("About to exec '%s'", aargv[0]);
execvp(aargv[0], aargv); ret = execvp(aargv[0], aargv);
ERROR("Failed to exec: '%s' : %s", aargv[0], strerror(errno)); ERROR("Failed to exec: '%s' : %s", aargv[0], strerror(errno));
exit(err); exit(ret);
} }
/* let's process the signals now */ /* let's process the signals now */
......
...@@ -23,11 +23,13 @@ ...@@ -23,11 +23,13 @@
#include "config.h" #include "config.h"
#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */
#include <ctype.h> #include <ctype.h>
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <grp.h> #include <grp.h>
#include <inttypes.h>
#include <libgen.h> #include <libgen.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
......
...@@ -26,6 +26,7 @@ lxc_test_apparmor_SOURCES = aa.c ...@@ -26,6 +26,7 @@ lxc_test_apparmor_SOURCES = aa.c
lxc_test_utils_SOURCES = lxc-test-utils.c lxctest.h lxc_test_utils_SOURCES = lxc-test-utils.c lxctest.h
lxc_test_parse_config_file_SOURCES = parse_config_file.c lxctest.h lxc_test_parse_config_file_SOURCES = parse_config_file.c lxctest.h
lxc_test_config_jump_table_SOURCES = config_jump_table.c lxctest.h lxc_test_config_jump_table_SOURCES = config_jump_table.c lxctest.h
lxc_test_shortlived_SOURCES = shortlived.c
AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
-DLXCPATH=\"$(LXCPATH)\" \ -DLXCPATH=\"$(LXCPATH)\" \
...@@ -54,7 +55,7 @@ bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \ ...@@ -54,7 +55,7 @@ bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \
lxc-test-snapshot lxc-test-concurrent lxc-test-may-control \ lxc-test-snapshot lxc-test-concurrent lxc-test-may-control \
lxc-test-reboot lxc-test-list lxc-test-attach lxc-test-device-add-remove \ lxc-test-reboot lxc-test-list lxc-test-attach lxc-test-device-add-remove \
lxc-test-apparmor lxc-test-utils lxc-test-parse-config-file \ lxc-test-apparmor lxc-test-utils lxc-test-parse-config-file \
lxc-test-config-jump-table lxc-test-config-jump-table lxc-test-shortlived
bin_SCRIPTS = lxc-test-automount \ bin_SCRIPTS = lxc-test-automount \
lxc-test-autostart \ lxc-test-autostart \
...@@ -107,6 +108,7 @@ EXTRA_DIST = \ ...@@ -107,6 +108,7 @@ EXTRA_DIST = \
may_control.c \ may_control.c \
parse_config_file.c \ parse_config_file.c \
saveconfig.c \ saveconfig.c \
shortlived.c \
shutdowntest.c \ shutdowntest.c \
snapshot.c \ snapshot.c \
startone.c startone.c
......
/* 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.
*/
#include <lxc/lxccontainer.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#define MYNAME "shortlived"
static int destroy_container(void)
{
int status, ret;
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return -1;
}
if (pid == 0) {
ret = execlp("lxc-destroy", "lxc-destroy", "-f", "-n", MYNAME, NULL);
// Should not return
perror("execl");
exit(1);
}
again:
ret = waitpid(pid, &status, 0);
if (ret == -1) {
if (errno == EINTR)
goto again;
perror("waitpid");
return -1;
}
if (ret != pid)
goto again;
if (!WIFEXITED(status)) { // did not exit normally
fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__);
return -1;
}
return WEXITSTATUS(status);
}
static int create_container(void)
{
int status, ret;
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return -1;
}
if (pid == 0) {
ret = execlp("lxc-create", "lxc-create", "-t", "busybox", "-n", MYNAME, NULL);
// Should not return
perror("execl");
exit(1);
}
again:
ret = waitpid(pid, &status, 0);
if (ret == -1) {
if (errno == EINTR)
goto again;
perror("waitpid");
return -1;
}
if (ret != pid)
goto again;
if (!WIFEXITED(status)) { // did not exit normally
fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__);
return -1;
}
return WEXITSTATUS(status);
}
int main(int argc, char *argv[])
{
struct lxc_container *c;
const char *s;
bool b;
int i;
int ret = 0;
ret = 1;
/* test a real container */
c = lxc_container_new(MYNAME, NULL);
if (!c) {
fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, MYNAME);
ret = 1;
goto out;
}
if (c->is_defined(c)) {
fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME);
goto out;
}
ret = create_container();
if (ret) {
fprintf(stderr, "%d: failed to create a container\n", __LINE__);
goto out;
}
b = c->is_defined(c);
if (!b) {
fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME);
goto out;
}
s = c->state(c);
if (!s || strcmp(s, "STOPPED")) {
fprintf(stderr, "%d: %s is in state %s, not in STOPPED.\n", __LINE__, c->name, s ? s : "undefined");
goto out;
}
b = c->load_config(c, NULL);
if (!b) {
fprintf(stderr, "%d: %s failed to read its config\n", __LINE__, c->name);
goto out;
}
if (!c->set_config_item(c, "lxc.init.cmd", "echo hello")) {
fprintf(stderr, "%d: failed setting lxc.init.cmd\n", __LINE__);
goto out;
}
c->want_daemonize(c, true);
/* Test whether we can start a really short-lived daemonized container.
*/
for (i = 0; i < 10; i++) {
if (!c->startl(c, 0, NULL)) {
fprintf(stderr, "%d: %s failed to start\n", __LINE__, c->name);
goto out;
}
sleep(1);
}
if (!c->set_config_item(c, "lxc.init.cmd", "you-shall-fail")) {
fprintf(stderr, "%d: failed setting lxc.init.cmd\n", __LINE__);
goto out;
}
/* Test whether we catch the start failure of a really short-lived
* daemonized container.
*/
for (i = 0; i < 10; i++) {
if (c->startl(c, 0, NULL)) {
fprintf(stderr, "%d: %s failed to start\n", __LINE__, c->name);
goto out;
}
sleep(1);
}
c->stop(c);
fprintf(stderr, "all lxc_container tests passed for %s\n", c->name);
ret = 0;
out:
if (c) {
c->stop(c);
destroy_container();
}
lxc_container_put(c);
exit(ret);
}
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