#include "caps.h"
#include "cgroup.h"
#include "commands.h"
+#include "commands_utils.h"
#include "conf.h"
#include "console.h"
#include "error.h"
#include "log.h"
+#include "lxccontainer.h"
#include "lxclock.h"
#include "lxcseccomp.h"
#include "lxcutmp.h"
#include "monitor.h"
#include "namespace.h"
#include "start.h"
+#include "storage_utils.h"
#include "sync.h"
#include "utils.h"
#include "lsm/lsm.h"
return (fd == 0 || fd == 1 || fd == 2);
}
-/* Check for any fds we need to close.
- * - If fd_to_ignore != -1, then if we find that fd open we will ignore it.
- * - 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)
+int lxc_check_inherited(struct lxc_conf *conf, bool closeall,
+ int *fds_to_ignore, size_t len_fds)
{
struct dirent *direntp;
int fd, fddir;
+ size_t i;
DIR *dir;
if (conf && conf->close_all_fds)
restart:
dir = opendir("/proc/self/fd");
if (!dir) {
- WARN("Failed to open directory: %m.");
+ WARN("Failed to open directory: %s.", strerror(errno));
return -1;
}
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;
if (current_config && fd == current_config->logfd)
return 1;
}
-int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state)
+static int lxc_serve_state_clients(const char *name,
+ struct lxc_handler *handler,
+ lxc_state_t state)
{
+ ssize_t ret;
+ struct lxc_list *cur, *next;
+ struct state_client *client;
+ struct lxc_msg msg = {.type = lxc_msg_state, .value = state};
+
+ process_lock();
+
+ /* Only set state under process lock held so that we don't cause
+ * lxc_cmd_add_state_client() to miss a state.
+ */
handler->state = state;
+ TRACE("set container state to %s", lxc_state2str(state));
+
+ if (lxc_list_empty(&handler->state_clients)) {
+ TRACE("no state clients registered");
+ process_unlock();
+ lxc_monitor_send_state(name, state, handler->lxcpath);
+ return 0;
+ }
+
+ strncpy(msg.name, name, sizeof(msg.name));
+ msg.name[sizeof(msg.name) - 1] = 0;
+
+ lxc_list_for_each_safe(cur, &handler->state_clients, next) {
+ client = cur->elem;
+
+ if (!client->states[state]) {
+ TRACE("state %s not registered for state client %d",
+ lxc_state2str(state), client->clientfd);
+ continue;
+ }
+
+ TRACE("sending state %s to state client %d",
+ lxc_state2str(state), client->clientfd);
+
+ again:
+ ret = send(client->clientfd, &msg, sizeof(msg), 0);
+ if (ret < 0) {
+ if (errno == EINTR)
+ goto again;
+
+ ERROR("failed to send message to client");
+ }
+
+ /* kick client from list */
+ close(client->clientfd);
+ lxc_list_del(cur);
+ free(cur->elem);
+ free(cur);
+ }
+ 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;
+
+ ret = lxc_abstract_unix_send_credential(handler->state_socket_pair[1],
+ &(int){state}, sizeof(int));
+ if (ret != sizeof(int))
+ SYSERROR("Failed to send state to %d",
+ handler->state_socket_pair[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
+ * server and only exists for backwards compatibility.
+ */
lxc_monitor_send_state(name, state, handler->lxcpath);
+
return 0;
}
DEBUG("Not starting utmp handler as CAP_SYS_BOOT cannot be dropped without capabilities support.");
#endif
}
+ TRACE("lxc mainloop is ready");
return lxc_mainloop(&descr, -1);
return -1;
}
-struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char *lxcpath)
+void lxc_free_handler(struct lxc_handler *handler)
{
- int i;
+ if (handler->conf && 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)
+ free(handler->name);
+
+ handler->conf = NULL;
+ free(handler);
+}
+
+struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
+ const char *lxcpath, bool daemonize)
+{
+ int i, ret;
struct lxc_handler *handler;
handler = malloc(sizeof(*handler));
- if (!handler)
+ if (!handler) {
+ ERROR("failed to allocate memory");
return NULL;
+ }
memset(handler, 0, sizeof(*handler));
handler->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);
for (i = 0; i < LXC_NS_MAX; i++)
handler->nsfd[i] = -1;
- lsm_init();
-
handler->name = strdup(name);
if (!handler->name) {
- ERROR("Failed to allocate memory.");
- goto out_free;
+ ERROR("failed to allocate memory");
+ 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)) {
+ ERROR("failed to set up command socket");
+ goto on_error;
}
- if (lxc_cmd_init(name, handler, lxcpath))
- goto out_free_name;
+ TRACE("unix domain socket %d for command server is ready",
+ handler->conf->maincmd_fd);
+
+ return handler;
+
+on_error:
+ lxc_free_handler(handler);
+
+ return NULL;
+}
+
+int lxc_init(const char *name, struct lxc_handler *handler)
+{
+ struct lxc_conf *conf = handler->conf;
+
+ lsm_init();
+ TRACE("initialized LSM");
if (lxc_read_seccomp_config(conf) != 0) {
ERROR("Failed loading seccomp policy.");
goto out_close_maincmd_fd;
}
+ TRACE("read seccomp policy");
/* Begin by setting the state to STARTING. */
if (lxc_set_state(name, handler, STARTING)) {
ERROR("Failed to set state for container \"%s\" to \"%s\".", name, lxc_state2str(STARTING));
goto out_close_maincmd_fd;
}
+ TRACE("set container state to \"STARTING\"");
/* Start of environment variable setup for hooks. */
if (name && setenv("LXC_NAME", name, 1))
SYSERROR("Failed to set environment variable LXC_CGNS_AWARE=1.");
/* End of environment variable setup for hooks. */
+ TRACE("set environment variables");
+
if (run_lxc_hooks(name, "pre-start", conf, handler->lxcpath, NULL)) {
ERROR("Failed to run lxc.hook.pre-start for container \"%s\".", name);
goto out_aborting;
}
+ TRACE("ran pre-start hooks");
/* The signal fd has to be created before forking otherwise if the child
* process exits before we setup the signal fd, the event will be lost
ERROR("Failed to setup SIGCHLD fd handler.");
goto out_delete_tty;
}
+ TRACE("set up signal fd");
/* Do this after setting up signals since it might unblock SIGWINCH. */
if (lxc_console_create(conf)) {
ERROR("Failed to create console for container \"%s\".", name);
goto out_restore_sigmask;
}
+ TRACE("created console");
- if (ttys_shift_ids(conf) < 0) {
+ if (lxc_ttys_shift_ids(conf) < 0) {
ERROR("Failed to shift tty into container.");
goto out_restore_sigmask;
}
+ TRACE("shifted tty ids");
- INFO("Container \"%s\" is initialized.", name);
- return handler;
+ INFO("container \"%s\" is initialized", name);
+ return 0;
out_restore_sigmask:
sigprocmask(SIG_SETMASK, &handler->oldmask, NULL);
out_close_maincmd_fd:
close(conf->maincmd_fd);
conf->maincmd_fd = -1;
-out_free_name:
- free(handler->name);
- handler->name = NULL;
-out_free:
- free(handler);
- return NULL;
+ return -1;
}
void lxc_fini(const char *name, struct lxc_handler *handler)
{
int i, rc;
+ struct lxc_list *cur, *next;
pid_t self = getpid();
- char *namespaces[LXC_NS_MAX+1];
+ char *namespaces[LXC_NS_MAX + 1];
size_t namespace_count = 0;
/* The STOPPING state is there for future cleanup code which can take
lxc_console_delete(&handler->conf->console);
lxc_delete_tty(&handler->conf->tty_info);
+
+ /* close the command socket */
close(handler->conf->maincmd_fd);
handler->conf->maincmd_fd = -1;
+
+ /* 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;
+ /* close state client socket */
+ close(client->clientfd);
+ lxc_list_del(cur);
+ free(cur->elem);
+ free(cur);
+ }
+
free(handler->name);
if (handler->ttysock[0] != -1) {
close(handler->ttysock[0]);
return -1;
}
if (wait(&status) < 0) {
- SYSERROR("Unexpected wait error: %m.");
+ SYSERROR("Unexpected wait error: %s.", strerror(errno));
return -1;
}
return 0;
}
-static int recv_fd(int sock, int *fd)
-{
- if (lxc_abstract_unix_recv_fd(sock, fd, NULL, 0) < 0) {
- SYSERROR("Error receiving tty file descriptor from child process.");
- return -1;
- }
- if (*fd == -1)
- return -1;
- return 0;
-}
-
-static int recv_ttys_from_child(struct lxc_handler *handler)
+static int lxc_recv_ttys_from_child(struct lxc_handler *handler)
{
+ int i;
+ int *ttyfds;
+ struct lxc_pty_info *pty_info;
+ int ret = -1;
+ int sock = handler->ttysock[1];
struct lxc_conf *conf = handler->conf;
- int i, sock = handler->ttysock[1];
struct lxc_tty_info *tty_info = &conf->tty_info;
+ size_t num_ttyfds = (2 * conf->tty);
if (!conf->tty)
return 0;
if (!tty_info->pty_info)
return -1;
- for (i = 0; i < conf->tty; i++) {
- struct lxc_pty_info *pty_info = &tty_info->pty_info[i];
+ ttyfds = malloc(num_ttyfds * sizeof(int));
+ if (!ttyfds)
+ return -1;
+
+ ret = lxc_abstract_unix_recv_fds(sock, ttyfds, num_ttyfds, NULL, 0);
+ for (i = 0; (ret >= 0 && *ttyfds != -1) && (i < num_ttyfds); i++) {
+ pty_info = &tty_info->pty_info[i / 2];
pty_info->busy = 0;
- if (recv_fd(sock, &pty_info->slave) < 0 ||
- recv_fd(sock, &pty_info->master) < 0) {
- ERROR("Error receiving tty info from child process.");
- return -1;
- }
+ pty_info->slave = ttyfds[i++];
+ pty_info->master = ttyfds[i];
+ TRACE("received pty with master fd %d and slave fd %d from "
+ "parent", pty_info->master, pty_info->slave);
}
+
tty_info->nbtty = conf->tty;
- return 0;
+ free(ttyfds);
+
+ if (ret < 0)
+ ERROR("failed to receive %d ttys from child: %s", conf->tty,
+ strerror(errno));
+ else
+ TRACE("received %d ttys from child", conf->tty);
+
+ return ret;
}
void resolve_clone_flags(struct lxc_handler *handler)
/* That should be done before the clone because we will
* fill the netdev index and use them in the child.
*/
- if (lxc_create_network(handler)) {
+ if (lxc_setup_networks_in_parent_namespaces(handler)) {
ERROR("Failed to create the network.");
lxc_sync_fini(handler);
return -1;
cgroups_connected = false;
/* Read tty fds allocated by child. */
- if (recv_ttys_from_child(handler) < 0) {
+ if (lxc_recv_ttys_from_child(handler) < 0) {
ERROR("Failed to receive tty info from child process.");
goto out_delete_net;
}
return -1;
}
-int __lxc_start(const char *name, struct lxc_conf *conf,
+int __lxc_start(const char *name, struct lxc_handler *handler,
struct lxc_operations* ops, void *data, const char *lxcpath,
bool backgrounded)
{
- struct lxc_handler *handler;
- int err = -1;
int status;
+ int err = -1;
bool removed_all_netdevs = true;
+ struct lxc_conf *conf = handler->conf;
- handler = lxc_init(name, conf, lxcpath);
- if (!handler) {
+ if (lxc_init(name, handler) < 0) {
ERROR("Failed to initialize container \"%s\".", name);
return -1;
}
.post_start = post_start
};
-int lxc_start(const char *name, char *const argv[], struct lxc_conf *conf,
+int lxc_start(const char *name, char *const argv[], struct lxc_handler *handler,
const char *lxcpath, bool backgrounded)
{
struct start_args start_arg = {
.argv = argv,
};
- conf->need_utmp_watch = 1;
- return __lxc_start(name, conf, &start_ops, &start_arg, lxcpath, backgrounded);
+ handler->conf->need_utmp_watch = 1;
+ return __lxc_start(name, handler, &start_ops, &start_arg, lxcpath, backgrounded);
}
static void lxc_destroy_container_on_signal(struct lxc_handler *handler,
}
if (am_unpriv())
- ret = userns_exec_1(handler->conf, lxc_rmdir_onedev_wrapper, destroy);
+ ret = userns_exec_1(handler->conf, lxc_rmdir_onedev_wrapper,
+ destroy, "lxc_rmdir_onedev_wrapper");
else
ret = lxc_rmdir_onedev(destroy, NULL);
static bool do_destroy_container(struct lxc_conf *conf) {
if (am_unpriv()) {
- if (userns_exec_1(conf, bdev_destroy_wrapper, conf) < 0)
+ if (userns_exec_1(conf, bdev_destroy_wrapper, conf,
+ "bdev_destroy_wrapper") < 0)
return false;
return true;
}