#include <fcntl.h>
#include <grp.h>
#include <poll.h>
+#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include "commands_utils.h"
#include "conf.h"
#include "confile_utils.h"
-#include "console.h"
#include "error.h"
#include "list.h"
+#include "lsm/lsm.h"
#include "log.h"
#include "lxccontainer.h"
#include "lxclock.h"
#include "namespace.h"
#include "network.h"
#include "start.h"
-#include "sync.h"
-#include "utils.h"
-#include "lsm/lsm.h"
#include "storage/storage.h"
#include "storage/storage_utils.h"
+#include "sync.h"
+#include "terminal.h"
+#include "utils.h"
+
+#ifndef HAVE_STRLCPY
+#include "include/strlcpy.h"
+#endif
lxc_log_define(lxc_start, lxc);
}
}
-/* lxc_preserve_namespaces: open /proc/@pid/ns/@ns for each namespace specified
- * in ns_clone_flags.
+static int lxc_try_preserve_ns(const int pid, const char *ns)
+{
+ int fd;
+
+ fd = lxc_preserve_ns(pid, ns);
+ if (fd < 0) {
+ if (errno != ENOENT) {
+ SYSERROR("Failed to preserve %s namespace", ns);
+ return -EINVAL;
+ }
+
+ WARN("%s - Kernel does not support preserving %s namespaces",
+ strerror(errno), ns);
+ return -EOPNOTSUPP;
+ }
+
+ return fd;
+}
+
+/* lxc_try_preserve_namespaces: open /proc/@pid/ns/@ns for each namespace
+ * specified in ns_clone_flags.
* Return true on success, false on failure.
*/
-static bool lxc_preserve_namespaces(struct lxc_handler *handler, int ns_clone_flags, pid_t pid)
+static bool lxc_try_preserve_namespaces(struct lxc_handler *handler,
+ int ns_clone_flags, pid_t pid)
{
int i;
handler->nsfd[i] = -EBADF;
for (i = 0; i < LXC_NS_MAX; i++) {
+ int fd;
+
if ((ns_clone_flags & ns_info[i].clone_flag) == 0)
continue;
- handler->nsfd[i] = lxc_preserve_ns(pid, ns_info[i].proc_name);
- if (handler->nsfd[i] < 0)
- goto error;
+ fd = lxc_try_preserve_ns(pid, ns_info[i].proc_name);
+ if (fd < 0) {
+ handler->nsfd[i] = -EBADF;
+
+ /* Do not fail to start container on kernels that do
+ * not support interacting with namespaces through
+ * /proc.
+ */
+ if (fd == -EOPNOTSUPP)
+ continue;
+
+ lxc_put_nsfds(handler);
+ return false;
+ }
- DEBUG("Preserved %s namespace via fd %d", ns_info[i].proc_name, handler->nsfd[i]);
+ handler->nsfd[i] = fd;
+ DEBUG("Preserved %s namespace via fd %d", ns_info[i].proc_name,
+ handler->nsfd[i]);
}
return true;
-
-error:
- if (errno == ENOENT)
- SYSERROR("Kernel does not support attaching to %s namespaces",
- ns_info[i].proc_name);
- else
- SYSERROR("Failed to open file descriptor for %s namespace",
- ns_info[i].proc_name);
- lxc_put_nsfds(handler);
- return false;
}
static int match_fd(int fd)
return -EBADF;
}
- ret = sigprocmask(SIG_BLOCK, &mask, oldmask);
+ ret = pthread_sigmask(SIG_BLOCK, &mask, oldmask);
if (ret < 0) {
SYSERROR("Failed to set signal mask");
return -EBADF;
return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0;
}
+ if (siginfo.ssi_signo != SIGCHLD) {
+ kill(hdlr->pid, siginfo.ssi_signo);
+ INFO("Forwarded signal %d to pid %d", siginfo.ssi_signo, hdlr->pid);
+ return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0;
+ }
+
/* More robustness, protect ourself from a SIGCHLD sent
* by a process different from the container init.
*/
return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0;
}
- if (siginfo.ssi_signo != SIGCHLD) {
- kill(hdlr->pid, siginfo.ssi_signo);
- INFO("Forwarded signal %d to pid %d", siginfo.ssi_signo, hdlr->pid);
- return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0;
- }
-
if (siginfo.ssi_code == CLD_STOPPED) {
INFO("Container init process was stopped");
return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0;
int lxc_serve_state_clients(const char *name, struct lxc_handler *handler,
lxc_state_t state)
{
+ size_t retlen;
ssize_t ret;
struct lxc_list *cur, *next;
struct lxc_state_client *client;
return 0;
}
- strncpy(msg.name, name, sizeof(msg.name));
- msg.name[sizeof(msg.name) - 1] = 0;
+ retlen = strlcpy(msg.name, name, sizeof(msg.name));
+ if (retlen >= sizeof(msg.name))
+ return -E2BIG;
lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) {
client = cur->elem;
}
if (has_console) {
- struct lxc_console *console = &handler->conf->console;
+ struct lxc_terminal *console = &handler->conf->console;
- ret = lxc_console_mainloop_add(&descr, console);
+ ret = lxc_terminal_mainloop_add(&descr, console);
if (ret < 0) {
ERROR("Failed to add console handlers to mainloop");
goto out_mainloop_console;
}
- ret = lxc_console_mainloop_add(&descr_console, console);
+ ret = lxc_terminal_mainloop_add(&descr_console, console);
if (ret < 0) {
ERROR("Failed to add console handlers to console mainloop");
goto out_mainloop_console;
TRACE("Set up signal fd");
/* Do this after setting up signals since it might unblock SIGWINCH. */
- ret = lxc_console_create(conf);
+ ret = lxc_terminal_setup(conf);
if (ret < 0) {
ERROR("Failed to create console");
goto out_restore_sigmask;
}
TRACE("Created console");
- ret = lxc_pty_map_ids(conf, &conf->console);
+ ret = lxc_terminal_map_ids(conf, &conf->console);
if (ret < 0) {
ERROR("Failed to chown console");
goto out_restore_sigmask;
}
TRACE("Chowned console");
+ handler->cgroup_ops = cgroup_init(handler);
+ if (!handler->cgroup_ops) {
+ ERROR("Failed to initialize cgroup driver");
+ goto out_restore_sigmask;
+ }
+ TRACE("Initialized cgroup driver");
+
INFO("Container \"%s\" is initialized", name);
return 0;
out_restore_sigmask:
- sigprocmask(SIG_SETMASK, &handler->oldmask, NULL);
+ pthread_sigmask(SIG_SETMASK, &handler->oldmask, NULL);
out_delete_tty:
- lxc_delete_tty(&conf->tty_info);
+ lxc_delete_tty(&conf->ttys);
out_aborting:
lxc_set_state(name, handler, ABORTING);
out_close_maincmd_fd:
struct lxc_list *cur, *next;
char *namespaces[LXC_NS_MAX + 1];
size_t namespace_count = 0;
+ struct cgroup_ops *cgroup_ops = handler->cgroup_ops;
/* The STOPPING state is there for future cleanup code which can take
* awhile.
while (namespace_count--)
free(namespaces[namespace_count]);
- cgroup_destroy(handler);
+ cgroup_ops->destroy(cgroup_ops, handler);
+ cgroup_exit(cgroup_ops);
if (handler->conf->reboot == 0) {
/* For all new state clients simply close the command socket.
}
/* Reset mask set by setup_signal_fd. */
- ret = sigprocmask(SIG_SETMASK, &handler->oldmask, NULL);
+ ret = pthread_sigmask(SIG_SETMASK, &handler->oldmask, NULL);
if (ret < 0)
WARN("%s - Failed to restore signal mask", strerror(errno));
- lxc_console_delete(&handler->conf->console);
- lxc_delete_tty(&handler->conf->tty_info);
+ lxc_terminal_delete(&handler->conf->console);
+ lxc_delete_tty(&handler->conf->ttys);
/* The command socket is now closed, no more state clients can register
* themselves from now on. So free the list of state clients.
goto out_warn_father;
}
- ret = sigprocmask(SIG_SETMASK, &handler->oldmask, NULL);
+ ret = lxc_ambient_caps_up();
+ if (ret < 0) {
+ SYSERROR("Failed to raise ambient capabilities");
+ goto out_warn_father;
+ }
+
+ ret = pthread_sigmask(SIG_SETMASK, &handler->oldmask, NULL);
if (ret < 0) {
SYSERROR("Failed to set signal mask");
goto out_warn_father;
*/
ret = lxc_sync_barrier_parent(handler, LXC_SYNC_CONFIGURE);
if (ret < 0)
- return -1;
+ goto out_error;
ret = lxc_network_recv_veth_names_from_parent(handler);
if (ret < 0) {
if (ret < 0 && (handler->am_root || errno != EPERM))
goto out_warn_father;
- if (!handler->am_root) {
- ret = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
- if (ret < 0)
- goto out_warn_father;
- }
+ ret = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+ if (ret < 0)
+ goto out_warn_father;
/* set{g,u}id() clears deathsignal */
ret = lxc_set_death_signal(SIGKILL);
/* Some init's such as busybox will set sane tty settings on stdin,
* stdout, stderr which it thinks is the console. We already set them
* the way we wanted on the real terminal, and we want init to do its
- * setup on its console ie. the pty allocated in lxc_console_create() so
+ * setup on its console ie. the pty allocated in lxc_terminal_setup() so
* make sure that that pty is stdin,stdout,stderr.
*/
if (handler->conf->console.slave >= 0) {
if (handler->backgrounded || handler->conf->is_execute == 0)
ret = set_stdfds(handler->conf->console.slave);
else
- ret = lxc_console_set_stdfds(handler->conf->console.slave);
+ ret = lxc_terminal_set_stdfds(handler->conf->console.slave);
if (ret < 0) {
ERROR("Failed to redirect std{in,out,err} to pty file "
"descriptor %d", handler->conf->console.slave);
if (ret < 0)
goto out_warn_father;
+ ret = lxc_ambient_caps_down();
+ if (ret < 0) {
+ SYSERROR("Failed to clear ambient capabilities");
+ goto out_warn_father;
+ }
+
/* After this call, we are in error because this ops should not return
* as it execs.
*/
static int lxc_recv_ttys_from_child(struct lxc_handler *handler)
{
int i;
- struct lxc_pty_info *pty_info;
+ struct lxc_terminal_info *tty;
int ret = -1;
int sock = handler->data_sock[1];
struct lxc_conf *conf = handler->conf;
- struct lxc_tty_info *tty_info = &conf->tty_info;
+ struct lxc_tty_info *ttys = &conf->ttys;
if (!conf->tty)
return 0;
- tty_info->pty_info = malloc(sizeof(*tty_info->pty_info) * conf->tty);
- if (!tty_info->pty_info)
+ ttys->tty = malloc(sizeof(*ttys->tty) * conf->tty);
+ if (!ttys->tty)
return -1;
for (i = 0; i < conf->tty; i++) {
if (ret < 0)
break;
- pty_info = &tty_info->pty_info[i];
- pty_info->busy = 0;
- pty_info->master = ttyfds[0];
- pty_info->slave = ttyfds[1];
+ tty = &ttys->tty[i];
+ tty->busy = 0;
+ tty->master = ttyfds[0];
+ tty->slave = ttyfds[1];
TRACE("Received pty with master fd %d and slave fd %d from "
- "parent", pty_info->master, pty_info->slave);
+ "parent", tty->master, tty->slave);
}
if (ret < 0)
ERROR("Failed to receive %d ttys from child: %s", conf->tty,
else
TRACE("Received %d ttys from child", conf->tty);
- tty_info->nbtty = conf->tty;
+ ttys->nbtty = conf->tty;
return ret;
}
continue;
ret = setns(handler->nsfd[i], 0);
- if (ret < 0)
+ if (ret < 0) {
+ /*
+ * Note that joining a user and/or mount namespace
+ * requires the process is not multithreaded otherwise
+ * setns() will fail here.
+ */
+ SYSERROR("Failed to inherit %s namespace",
+ ns_info[i].proc_name);
return -1;
+ }
DEBUG("Inherited %s namespace", ns_info[i].proc_name);
}
struct lxc_list *id_map;
const char *name = handler->name;
const char *lxcpath = handler->lxcpath;
- bool cgroups_connected = false, share_ns = false;
+ bool share_ns = false;
struct lxc_conf *conf = handler->conf;
+ struct cgroup_ops *cgroup_ops = handler->cgroup_ops;
id_map = &conf->id_map;
wants_to_map_ids = !lxc_list_empty(id_map);
}
}
- if (!cgroup_init(handler)) {
- ERROR("Failed initializing cgroup support");
- goto out_delete_net;
- }
-
- cgroups_connected = true;
-
- if (!cgroup_create(handler)) {
+ if (!cgroup_ops->create(cgroup_ops, handler)) {
ERROR("Failed creating cgroups");
goto out_delete_net;
}
if (handler->ns_on_clone_flags & ns_info[i].clone_flag)
INFO("Cloned %s", ns_info[i].flag_name);
- if (!lxc_preserve_namespaces(handler, handler->ns_on_clone_flags, handler->pid)) {
+ if (!lxc_try_preserve_namespaces(handler, handler->ns_on_clone_flags, handler->pid)) {
ERROR("Failed to preserve cloned namespaces for lxc.hook.stop");
goto out_delete_net;
}
if (ret < 0)
goto out_delete_net;
- if (!cgroup_setup_limits(handler, false)) {
+ if (!cgroup_ops->setup_limits(cgroup_ops, handler->conf, false)) {
ERROR("Failed to setup cgroup limits for container \"%s\"", name);
goto out_delete_net;
}
- if (!cgroup_enter(handler))
+ if (!cgroup_ops->enter(cgroup_ops, handler->pid))
goto out_delete_net;
- if (!cgroup_chown(handler))
+ if (!cgroup_ops->chown(cgroup_ops, handler->conf))
goto out_delete_net;
/* Now we're ready to preserve the network namespace */
- ret = lxc_preserve_ns(handler->pid, "net");
+ ret = lxc_try_preserve_ns(handler->pid, "net");
if (ret < 0) {
- ERROR("%s - Failed to preserve net namespace", strerror(errno));
- goto out_delete_net;
+ if (ret != -EOPNOTSUPP) {
+ ERROR("%s - Failed to preserve net namespace", strerror(errno));
+ goto out_delete_net;
+ }
+ } else {
+ handler->nsfd[LXC_NS_NET] = ret;
+ DEBUG("Preserved net namespace via fd %d", ret);
}
- handler->nsfd[LXC_NS_NET] = ret;
- DEBUG("Preserved net namespace via fd %d", ret);
/* Create the network configuration. */
if (handler->ns_clone_flags & CLONE_NEWNET) {
if (ret < 0)
goto out_delete_net;
- if (!cgroup_setup_limits(handler, true)) {
+ if (!cgroup_ops->setup_limits(cgroup_ops, handler->conf, true)) {
ERROR("Failed to setup legacy device cgroup controller limits");
goto out_delete_net;
}
TRACE("Set up legacy device cgroup controller limits");
- cgroup_disconnect();
- cgroups_connected = false;
-
if (handler->ns_clone_flags & CLONE_NEWCGROUP) {
/* Now we're ready to preserve the cgroup namespace */
- ret = lxc_preserve_ns(handler->pid, "cgroup");
+ ret = lxc_try_preserve_ns(handler->pid, "cgroup");
if (ret < 0) {
- ERROR("%s - Failed to preserve cgroup namespace", strerror(errno));
- goto out_delete_net;
+ if (ret != -EOPNOTSUPP) {
+ ERROR("%s - Failed to preserve cgroup namespace",
+ strerror(errno));
+ goto out_delete_net;
+ }
+ } else {
+ handler->nsfd[LXC_NS_CGROUP] = ret;
+ DEBUG("Preserved cgroup namespace via fd %d", ret);
}
- handler->nsfd[LXC_NS_CGROUP] = ret;
- DEBUG("Preserved cgroup namespace via fd %d", ret);
}
ret = snprintf(pidstr, 20, "%d", handler->pid);
return 0;
out_delete_net:
- if (cgroups_connected)
- cgroup_disconnect();
-
if (handler->ns_clone_flags & CLONE_NEWNET)
lxc_delete_network(handler);
int __lxc_start(const char *name, struct lxc_handler *handler,
struct lxc_operations* ops, void *data, const char *lxcpath,
- bool backgrounded)
+ bool backgrounded, int *error_num)
{
int ret, status;
struct lxc_conf *conf = handler->conf;
lxc_monitor_send_exit_code(name, status, handler->lxcpath);
lxc_error_set_and_log(handler->pid, status);
+ if (error_num)
+ *error_num = handler->exit_status;
out_fini:
lxc_delete_network(handler);
};
int lxc_start(const char *name, char *const argv[], struct lxc_handler *handler,
- const char *lxcpath, bool backgrounded)
+ const char *lxcpath, bool backgrounded, int *error_num)
{
struct start_args start_arg = {
.argv = argv,
};
- return __lxc_start(name, handler, &start_ops, &start_arg, lxcpath, backgrounded);
+ TRACE("Doing lxc_start");
+ return __lxc_start(name, handler, &start_ops, &start_arg, lxcpath, backgrounded, error_num);
}
static void lxc_destroy_container_on_signal(struct lxc_handler *handler,