diff --git a/configure.in b/configure.in
index c91177fc3..1f7629452 100644
--- a/configure.in
+++ b/configure.in
@@ -1,7 +1,7 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
-AC_INIT([lxc], [0.5.2])
+AC_INIT([lxc], [0.6.0])
AC_CONFIG_SRCDIR([configure.in])
AC_CONFIG_MACRO_DIR([m4])
@@ -60,6 +60,7 @@ AC_CONFIG_FILES([
doc/lxc-execute.sgml
doc/lxc-start.sgml
doc/lxc-stop.sgml
+ doc/lxc-console.sgml
doc/lxc-freeze.sgml
doc/lxc-unfreeze.sgml
doc/lxc-monitor.sgml
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 2e495967c..46a7dbb96 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -8,6 +8,7 @@ man_MANS = \
lxc-execute.1 \
lxc-start.1 \
lxc-stop.1 \
+ lxc-console.1 \
lxc-freeze.1 \
lxc-unfreeze.1 \
lxc-monitor.1 \
diff --git a/doc/lxc-console.sgml.in b/doc/lxc-console.sgml.in
new file mode 100644
index 000000000..4d06deee8
--- /dev/null
+++ b/doc/lxc-console.sgml.in
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+ @LXC_GENERATE_DATE@
+
+
+ lxc-console
+ 1
+
+
+
+ lxc-console
+
+
+ Launch a console for the specified container
+
+
+
+
+
+ lxc-console -n name
+ -t ttynum
+
+
+
+
+
+ Description
+
+
+ If the tty service has been configured and is available for the
+ container specified as parameter, this command will launch a
+ console allowing to log to the container.
+
+
+
+ The available tty are free slots taken by this command. That
+ means if the container has four ttys available and the command
+ has been launched four times taking the different tty, the fifth
+ command will fail because no console will be available.
+
+
+
+ The command will connect to a tty. If the connection is lost or
+ broken, the command can be launched again and regain the tty at
+ the state it was before the disconnection.
+
+
+
+
+
+ Options
+
+
+
+
+
+
+
+
+ Specify the container name to open a console.
+
+
+
+
+
+
+
+
+
+
+ Specify the tty number to connect.
+
+
+
+
+
+
+
+
+
+ Diagnostic
+
+
+
+
+ tty service denied
+
+
+ No tty is available or there is not enough privilege to
+ use the console. For example, the container belongs to
+ user "foo" and "bar" is trying to open a console to it.
+
+
+
+
+
+
+
+
+
+ See Also
+
+
+
+ lxc.conf
+ 5
+
+
+
+
+
+
+
+ Author
+ Daniel Lezcano daniel.lezcano@free.fr
+
+
+
+
+
diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in
index 6752a4435..a61669488 100644
--- a/doc/lxc.conf.sgml.in
+++ b/doc/lxc.conf.sgml.in
@@ -243,6 +243,33 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Console through the ttys
+
+ If the container is configured with a root filesystem and the
+ inittab file is setup to launch a getty on the ttys. This
+ option will specify the number of ttys to be available for the
+ container. The number of getty in the inittab file of the
+ container and the number of tty specified in this
+ configuration file should be equal, otherwise the getty will
+ die and respawn indefinitly giving annoying messages on the
+ console.
+
+
+
+
+
+
+
+
+ Specify the number of tty to make available to the
+ container.
+
+
+
+
+
+
Mount points
diff --git a/doc/lxc.sgml.in b/doc/lxc.sgml.in
index 39de600c0..2dfd79feb 100644
--- a/doc/lxc.sgml.in
+++ b/doc/lxc.sgml.in
@@ -367,6 +367,20 @@ rootfs
+
+ Connect to an available tty
+
+ If the container is configured with the ttys, it is possible
+ to access it through them. It is up to the container to
+ provide a set of available tty to be used by the following
+ command. When the tty is lost, it is possible to reconnect it
+ without login again.
+
+ lxc-console -n foo -t 3
+
+
+
+
Freeze / Unfreeze a container
@@ -591,6 +605,11 @@ rootfs
1
,
+
+ lxc-console
+ 1
+ ,
+
lxc-monitor
1
diff --git a/scripts/lxc-debian.in b/scripts/lxc-debian.in
index 55f848886..fa244ad0f 100755
--- a/scripts/lxc-debian.in
+++ b/scripts/lxc-debian.in
@@ -49,6 +49,10 @@ l6:6:wait:/etc/init.d/rc 6
# Normally not reached, but fallthrough in case of emergency.
z6:6:respawn:/sbin/sulogin
1:2345:respawn:/sbin/getty 38400 console
+c1:12345:respawn:/sbin/getty 38400 tty1 linux
+c2:12345:respawn:/sbin/getty 38400 tty2 linux
+c3:12345:respawn:/sbin/getty 38400 tty3 linux
+c4:12345:respawn:/sbin/getty 38400 tty4 linux
EOF
}
@@ -107,6 +111,7 @@ EOF
write_lxc_configuration() {
cat < $CONFFILE
lxc.utsname = $UTSNAME
+lxc.tty = 4
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index e0e5b0e6d..677ebe4f6 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -1,4 +1,5 @@
INCLUDES= -I$(top_srcdir)/src -DLXCPATH="\"$(localstatedir)/lxc\"" -DLXCBINDIR="\"$(bindir)\""
+AM_LDFLAGS= -lutil
lib_LTLIBRARIES = liblxc.la
pkginclude_HEADERS = \
monitor.h \
@@ -18,6 +19,7 @@ liblxc_la_SOURCES = \
start.c \
stop.c \
monitor.c monitor.h \
+ console.c \
freezer.c \
checkpoint.c \
restart.c \
@@ -38,6 +40,9 @@ liblxc_la_SOURCES = \
rtnl.c rtnl.h \
genl.c genl.h \
\
+ mainloop.c mainloop.h \
+ af_unix.c af_unix.h \
+ \
cr_plugin_columbia.c lxc_plugin.h
liblxc_la_LDFLAGS = -release @PACKAGE_VERSION@
diff --git a/src/lxc/af_unix.c b/src/lxc/af_unix.c
new file mode 100644
index 000000000..c24df8b7b
--- /dev/null
+++ b/src/lxc/af_unix.c
@@ -0,0 +1,243 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include
+#include
+#include
+#define __USE_GNU
+#include
+#undef __USE_GNU
+#include
+
+
+int lxc_af_unix_open(const char *path, int type, int flags)
+{
+ int fd;
+ struct sockaddr_un addr;
+
+ if (flags & O_TRUNC)
+ unlink(path);
+
+ fd = socket(PF_UNIX, type, 0);
+ if (fd < 0)
+ return -1;
+
+ memset(&addr, 0, sizeof(addr));
+
+ if (!path)
+ return fd;
+
+ addr.sun_family = AF_UNIX;
+ /* copy entire buffer in case of abstract socket */
+ memcpy(addr.sun_path, path,
+ path[0]?strlen(path):sizeof(addr.sun_path));
+
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))) {
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, 100)) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int lxc_af_unix_close(int fd)
+{
+ struct sockaddr_un addr;
+ socklen_t addrlen;
+
+ if (!getsockname(fd, (struct sockaddr *)&addr, &addrlen) &&
+ addr.sun_path[0])
+ unlink(addr.sun_path);
+
+ close(fd);
+
+ return 0;
+}
+
+int lxc_af_unix_connect(const char *path)
+{
+ int fd;
+ struct sockaddr_un addr;
+
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ memset(&addr, 0, sizeof(addr));
+
+ addr.sun_family = AF_UNIX;
+ /* copy entire buffer in case of abstract socket */
+ memcpy(addr.sun_path, path,
+ path[0]?strlen(path):sizeof(addr.sun_path));
+
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr))) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int lxc_af_unix_send_fd(int fd, int sendfd, void *data, size_t size)
+{
+ struct msghdr msg = { 0 };
+ struct iovec iov;
+ struct cmsghdr *cmsg;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ char buf[1];
+
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *((int *) CMSG_DATA(cmsg)) = sendfd;
+ msg.msg_controllen = cmsg->cmsg_len;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+
+ iov.iov_base = data ? data : buf;
+ iov.iov_len = data ? size : sizeof(buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ return sendmsg(fd, &msg, 0);
+}
+
+int lxc_af_unix_recv_fd(int fd, int *recvfd, void *data, size_t size)
+{
+ struct msghdr msg = { 0 };
+ struct iovec iov;
+ struct cmsghdr *cmsg;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ char buf[1];
+ int ret;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+
+ iov.iov_base = data ? data : buf;
+ iov.iov_len = data ? size : sizeof(buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ ret = recvmsg(fd, &msg, 0);
+ if (ret <= 0)
+ goto out;
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+
+ /* if the message is wrong the variable will not be
+ * filled and the peer will notified about a problem */
+ *recvfd = -1;
+
+ if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int)) &&
+ cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ *recvfd = *((int *) CMSG_DATA(cmsg));
+ }
+out:
+ return ret;
+}
+
+int lxc_af_unix_send_credential(int fd, void *data, size_t size)
+{
+ struct msghdr msg = { 0 };
+ struct iovec iov;
+ struct cmsghdr *cmsg;
+ struct ucred cred = {
+ .pid = getpid(),
+ .uid = getuid(),
+ .gid = getgid(),
+ };
+ char cmsgbuf[CMSG_SPACE(sizeof(cred))];
+ char buf[1];
+
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_CREDENTIALS;
+ *((struct ucred *) CMSG_DATA(cmsg)) = cred;
+ msg.msg_controllen = cmsg->cmsg_len;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+
+ iov.iov_base = data ? data : buf;
+ iov.iov_len = data ? size : sizeof(buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ return sendmsg(fd, &msg, 0);
+}
+
+int lxc_af_unix_rcv_credential(int fd, void *data, size_t size)
+{
+ struct msghdr msg = { 0 };
+ struct iovec iov;
+ struct cmsghdr *cmsg;
+ struct ucred cred;
+ char cmsgbuf[CMSG_SPACE(sizeof(cred))];
+ char buf[1];
+ int ret;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+
+ iov.iov_base = data ? data : buf;
+ iov.iov_len = data ? size : sizeof(buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ ret = recvmsg(fd, &msg, 0);
+ if (ret <= 0)
+ goto out;
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+
+ ret = -1;
+
+ if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) &&
+ cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS) {
+ cred = *((struct ucred *) CMSG_DATA(cmsg));
+ if (cred.uid == getuid() && cred.gid == getgid())
+ ret = 0;
+ }
+out:
+ return ret;
+}
diff --git a/src/lxc/af_unix.h b/src/lxc/af_unix.h
new file mode 100644
index 000000000..e1d5db7cc
--- /dev/null
+++ b/src/lxc/af_unix.h
@@ -0,0 +1,31 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+extern int lxc_af_unix_open(const char *path, int type, int flags);
+extern int lxc_af_unix_close(int fd);
+extern int lxc_af_unix_connect(const char *path);
+extern int lxc_af_unix_send_fd(int fd, int sendfd, void *data, size_t size);
+extern int lxc_af_unix_recv_fd(int fd, int *recvfd, void *data, size_t size);
+extern int lxc_af_unix_send_credential(int fd, void *data, size_t size);
+extern int lxc_af_unix_rcv_credential(int fd, void *data, size_t size);
+
diff --git a/src/lxc/checkpoint.c b/src/lxc/checkpoint.c
index 995de100f..724c24c60 100644
--- a/src/lxc/checkpoint.c
+++ b/src/lxc/checkpoint.c
@@ -53,17 +53,17 @@ int lxc_checkpoint(const char *name, const char *statefile,
lock = lxc_get_lock(name);
if (lock >= 0) {
lxc_put_lock(lock);
- return -LXC_ERROR_EMPTY;
+ return -LXC_ERROR_ESRCH;
}
- if (lock < 0 && lock != -EWOULDBLOCK)
- return -LXC_ERROR_LOCK;
+ if (lock < 0 && lock != -LXC_ERROR_EBUSY)
+ return lock;
snprintf(init, MAXPATHLEN, LXCPATH "/%s/init", name);
fd = open(init, O_RDONLY);
if (fd < 0) {
lxc_log_syserror("failed to open init file for %s", name);
- goto out_unlock;
+ goto out_close;
}
if (read(fd, val, sizeof(val)) < 0) {
@@ -82,7 +82,5 @@ int lxc_checkpoint(const char *name, const char *statefile,
out_close:
close(fd);
-out_unlock:
- lxc_put_lock(lock);
return ret;
}
diff --git a/src/lxc/console.c b/src/lxc/console.c
new file mode 100644
index 000000000..6953b93b2
--- /dev/null
+++ b/src/lxc/console.c
@@ -0,0 +1,71 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "lxc_log.h"
+#include "af_unix.h"
+#include "error.h"
+
+extern int lxc_console(const char *name, int ttynum, int *fd)
+{
+ struct sockaddr_un addr = { 0 };
+ int sock, ret = -LXC_ERROR_TTY_EAGAIN;
+
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "@%s", name);
+ addr.sun_path[0] = '\0';
+
+ sock = lxc_af_unix_connect(addr.sun_path);
+ if (sock < 0) {
+ lxc_log_error("failed to connect to the tty service");
+ goto out_err;
+ }
+
+ ret = lxc_af_unix_send_credential(sock, &ttynum, sizeof(ttynum));
+ if (ret < 0) {
+ lxc_log_syserror("failed to send credentials");
+ goto out_err;
+ }
+
+ ret = lxc_af_unix_recv_fd(sock, fd, NULL, 0);
+ if (ret < 0) {
+ lxc_log_error("failed to connect to the tty");
+ goto out_err;
+ }
+
+ if (!ret) {
+ lxc_log_error("tty%d denied by '%s'", ttynum, name);
+ ret = -LXC_ERROR_TTY_DENIED;
+ goto out_err;
+ }
+
+ ret = 0;
+
+out_err:
+ close(sock);
+ return ret;
+}
diff --git a/src/lxc/create.c b/src/lxc/create.c
index 89d46c446..9af6f3800 100644
--- a/src/lxc/create.c
+++ b/src/lxc/create.c
@@ -99,7 +99,7 @@ int lxc_create(const char *name, struct lxc_conf *conf)
err = create_lxc_directory(name);
if (err < 0)
return err == -EEXIST ?
- -LXC_ERROR_ALREADY_EXISTS:LXC_ERROR_INTERNAL;
+ -LXC_ERROR_EEXIST : LXC_ERROR_INTERNAL;
lock = lxc_get_lock(name);
if (lock < 0)
diff --git a/src/lxc/destroy.c b/src/lxc/destroy.c
index 01b4b4311..e96489ff7 100644
--- a/src/lxc/destroy.c
+++ b/src/lxc/destroy.c
@@ -52,13 +52,8 @@ int lxc_destroy(const char *name)
char path[MAXPATHLEN];
lock = lxc_get_lock(name);
- if (lock < 0) {
- if (lock == -EWOULDBLOCK)
- return -LXC_ERROR_BUSY;
- if (lock == -ENOENT)
- return -LXC_ERROR_NOT_FOUND;
- return -LXC_ERROR_INTERNAL;
- }
+ if (lock < 0)
+ return lock;
if (lxc_rmstate(name)) {
lxc_log_error("failed to remove state file for %s", name);
diff --git a/src/lxc/error.c b/src/lxc/error.c
index 8f2dbc8b5..4af8d1065 100644
--- a/src/lxc/error.c
+++ b/src/lxc/error.c
@@ -29,17 +29,18 @@ static const char *const catalogue[] = {
[LXC_ERROR_LOCK] = "Failed to lock the container",
- [LXC_ERROR_EMPTY] = "The container is empty",
- [LXC_ERROR_ALREADY_EXISTS] = "The container already exists",
- [LXC_ERROR_BUSY] = "The container is busy",
- [LXC_ERROR_NOT_FOUND] = "The container was not found",
- [LXC_ERROR_PERMISSION_DENIED] = "Permission denied",
+ [LXC_ERROR_ESRCH] = "The container is empty",
+ [LXC_ERROR_EEXIST] = "The container already exists",
+ [LXC_ERROR_EBUSY] = "The container is busy",
+ [LXC_ERROR_ENOENT] = "The container was not found",
+ [LXC_ERROR_EACCES] = "Not enough privilege to use the container",
[LXC_ERROR_WRONG_COMMAND] = "Wrong command",
[LXC_ERROR_CONF_CGROUP] = "Failed to configure the control group",
[LXC_ERROR_CONF_MOUNT] = "Failed to configure the mount points",
[LXC_ERROR_CONF_UTSNAME] = "Failed to configure the utsname",
[LXC_ERROR_CONF_NETWORK] = "Failed to configure the network",
+ [LXC_ERROR_CONF_TTY] = "Failed to configure the tty",
[LXC_ERROR_CONF_ROOTFS] = "Failed to configure the root fs",
[LXC_ERROR_SETUP_CGROUP] = "Failed to setup the control group",
@@ -47,8 +48,11 @@ static const char *const catalogue[] = {
[LXC_ERROR_SETUP_UTSNAME] = "Failed to setup the utsname",
[LXC_ERROR_SETUP_NETWORK] = "Failed to setup the network",
[LXC_ERROR_SETUP_CONSOLE] = "Failed to setup the console",
+ [LXC_ERROR_SETUP_TTY] = "Failed to setup the tty",
[LXC_ERROR_SETUP_ROOTFS] = "Failed to setup the root fs",
+ [LXC_ERROR_TTY_DENIED] = "tty service denied",
+ [LXC_ERROR_TTY_EAGAIN] = "tty service is not available",
[LXC_ERROR_INTERNAL] = "Internal system error",
};
diff --git a/src/lxc/error.h b/src/lxc/error.h
index e0296440f..b79adf696 100644
--- a/src/lxc/error.h
+++ b/src/lxc/error.h
@@ -28,17 +28,18 @@ typedef enum {
LXC_ERROR_LOCK,
- LXC_ERROR_EMPTY,
- LXC_ERROR_BUSY,
- LXC_ERROR_ALREADY_EXISTS,
- LXC_ERROR_NOT_FOUND,
- LXC_ERROR_PERMISSION_DENIED,
+ LXC_ERROR_ESRCH,
+ LXC_ERROR_EEXIST,
+ LXC_ERROR_EBUSY,
+ LXC_ERROR_ENOENT,
+ LXC_ERROR_EACCES,
LXC_ERROR_WRONG_COMMAND,
LXC_ERROR_CONF_CGROUP,
LXC_ERROR_CONF_MOUNT,
LXC_ERROR_CONF_UTSNAME,
LXC_ERROR_CONF_NETWORK,
+ LXC_ERROR_CONF_TTY,
LXC_ERROR_CONF_ROOTFS,
LXC_ERROR_SETUP_CGROUP,
@@ -46,8 +47,11 @@ typedef enum {
LXC_ERROR_SETUP_UTSNAME,
LXC_ERROR_SETUP_NETWORK,
LXC_ERROR_SETUP_CONSOLE,
+ LXC_ERROR_SETUP_TTY,
LXC_ERROR_SETUP_ROOTFS,
+ LXC_ERROR_TTY_DENIED,
+ LXC_ERROR_TTY_EAGAIN,
LXC_ERROR_INTERNAL,
LXC_LAST_ERROR,
diff --git a/src/lxc/lxc.h b/src/lxc/lxc.h
index de8c3533c..1dd74bb50 100644
--- a/src/lxc/lxc.h
+++ b/src/lxc/lxc.h
@@ -40,6 +40,7 @@ extern "C" {
#include
#include
#include
+#include
#include
#include
@@ -120,7 +121,7 @@ extern int lxc_monitor_close(int fd);
* @name : the name of container
* Returns 0 on sucess, < 0 otherwise
*/
-extern int lxc_console(const char *name);
+extern int lxc_console(const char *name, int ttynum, int *fd);
/*
* Freeze all the tasks running inside the container
diff --git a/src/lxc/lxc_conf.c b/src/lxc/lxc_conf.c
index ab3586035..826cc96c1 100644
--- a/src/lxc/lxc_conf.c
+++ b/src/lxc/lxc_conf.c
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
#include
#include
@@ -427,6 +428,28 @@ static int configure_cgroup(const char *name, struct lxc_list *cgroup)
return 0;
}
+static int configure_tty(const char *name, int tty)
+{
+ char path[MAXPATHLEN];
+ char *nbtty;
+ int ret;
+
+ if (asprintf(&nbtty, "%d", tty) < 0) {
+ lxc_log_error("failed to convert tty number");
+ return -1;
+ }
+
+ snprintf(path, MAXPATHLEN, LXCPATH "/%s", name);
+
+ ret = write_info(path, "tty", nbtty);
+ if (ret)
+ lxc_log_error("failed to write the tty info");
+
+ free(nbtty);
+
+ return ret;
+}
+
static int configure_rootfs(const char *name, const char *rootfs)
{
char path[MAXPATHLEN];
@@ -447,7 +470,6 @@ static int configure_rootfs(const char *name, const char *rootfs)
}
return symlink(absrootfs, path);
-
}
static int configure_mount(const char *name, const char *fstab)
@@ -647,6 +669,31 @@ static int setup_utsname(const char *name)
return 0;
}
+static int setup_tty(const char *name, const struct lxc_tty_info *tty_info)
+{
+ char path[MAXPATHLEN];
+ int i;
+
+ for (i = 0; i < tty_info->nbtty; i++) {
+
+ struct lxc_pty_info *pty_info = &tty_info->pty_info[i];
+
+ snprintf(path, MAXPATHLEN, LXCPATH "/%s/rootfs/dev/tty%d", name, i + 1);
+
+ /* At this point I can not use the "access" function
+ * to check the file is present or not because it fails
+ * with EACCES errno and I don't know why :( */
+
+ if (mount(pty_info->name, path, "none", MS_BIND, 0)) {
+ lxc_log_warning("failed to mount '%s'->'%s'",
+ pty_info->name, path);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
static int setup_rootfs(const char *name)
{
char path[MAXPATHLEN];
@@ -1067,6 +1114,11 @@ int lxc_configure(const char *name, struct lxc_conf *conf)
return -LXC_ERROR_CONF_NETWORK;
}
+ if (conf->tty && configure_tty(name, conf->tty)) {
+ lxc_log_error("failed to configure the tty");
+ return -LXC_ERROR_CONF_TTY;
+ }
+
if (conf->rootfs && configure_rootfs(name, conf->rootfs)) {
lxc_log_error("failed to configure the rootfs");
return -LXC_ERROR_CONF_ROOTFS;
@@ -1393,7 +1445,78 @@ int conf_destroy_network(const char *name)
return 0;
}
-int lxc_setup(const char *name, const char *tty)
+int lxc_create_tty(const char *name, struct lxc_tty_info *tty_info)
+{
+ char path[MAXPATHLEN];
+ char tty[4];
+ int i, ret = -1;
+
+ tty_info->nbtty = 0;
+
+ if (!conf_has_tty(name))
+ return 0;
+
+ if (!conf_has_rootfs(name)) {
+ lxc_log_warning("no rootfs is configured, ignoring ttys");
+ return 0;
+ }
+
+ snprintf(path, MAXPATHLEN, LXCPATH "/%s", name);
+
+ if (read_info(path, "tty", tty, sizeof(tty)) < 0) {
+ lxc_log_syserror("failed to read tty info");
+ goto out;
+ }
+
+ tty_info->nbtty = atoi(tty);
+ tty_info->pty_info =
+ malloc(sizeof(*tty_info->pty_info)*tty_info->nbtty);
+
+ if (!tty_info->pty_info) {
+ lxc_log_syserror("failed to allocate pty_info");
+ goto out;
+ }
+
+ for (i = 0; i < tty_info->nbtty; i++) {
+
+ struct lxc_pty_info *pty_info = &tty_info->pty_info[i];
+
+ if (openpty(&pty_info->master, &pty_info->slave,
+ pty_info->name, NULL, NULL)) {
+ lxc_log_syserror("failed to create pty #%d", i);
+ goto out_free;
+ }
+
+ pty_info->busy = 0;
+ }
+
+ ret = 0;
+out:
+ return ret;
+
+out_free:
+ free(tty_info->pty_info);
+ goto out;
+}
+
+void lxc_delete_tty(struct lxc_tty_info *tty_info)
+{
+ int i;
+
+ for (i = 0; i < tty_info->nbtty; i++) {
+ struct lxc_pty_info *pty_info = &tty_info->pty_info[i];
+
+ close(pty_info->master);
+ close(pty_info->slave);
+ }
+
+ free(tty_info->pty_info);
+ tty_info->nbtty = 0;
+}
+
+int lxc_setup(const char *name, const char *tty,
+ const struct lxc_tty_info *tty_info)
+
{
if (conf_has_utsname(name) && setup_utsname(name)) {
lxc_log_error("failed to setup the utsname for '%s'", name);
@@ -1420,6 +1543,11 @@ int lxc_setup(const char *name, const char *tty)
return -LXC_ERROR_SETUP_CONSOLE;
}
+ if (tty_info->nbtty && setup_tty(name, tty_info)) {
+ lxc_log_error("failed to setup the ttys for '%s'", name);
+ return -LXC_ERROR_SETUP_TTY;
+ }
+
if (conf_has_rootfs(name) && setup_rootfs(name)) {
lxc_log_error("failed to set rootfs for '%s'", name);
return -LXC_ERROR_SETUP_ROOTFS;
diff --git a/src/lxc/lxc_conf.h b/src/lxc/lxc_conf.h
index fe4b80fdf..708a6ca8e 100644
--- a/src/lxc/lxc_conf.h
+++ b/src/lxc/lxc_conf.h
@@ -24,6 +24,7 @@
#define _conf_h
#include
+#include
enum {
EMPTY,
@@ -117,11 +118,36 @@ struct lxc_cgroup {
struct lxc_conf {
char *rootfs;
char *fstab;
+ int tty;
struct utsname *utsname;
struct lxc_list cgroup;
struct lxc_list networks;
};
+/*
+ * Defines a structure containing a pty information for
+ * virtualizing a tty
+ * @name : the path name of the slave pty side
+ * @master : the file descriptor of the master
+ * @slave : the file descriptor of the slave
+ */
+struct lxc_pty_info {
+ char name[MAXPATHLEN];
+ int master;
+ int slave;
+ int busy;
+};
+
+/*
+ * Defines the number of tty configured and contains the
+ * instanciated ptys
+ * @nbtty = number of configured ttys
+ */
+struct lxc_tty_info {
+ int nbtty;
+ struct lxc_pty_info *pty_info;
+};
+
/*
* Configure the external resources for the container
*/
@@ -136,10 +162,14 @@ extern int conf_create_network(const char *name, pid_t pid);
extern int conf_destroy_network(const char *name);
+extern int lxc_create_tty(const char *name, struct lxc_tty_info *tty_info);
+extern void lxc_delete_tty(struct lxc_tty_info *tty_info);
+
/*
* Configure the container from inside
*/
-extern int lxc_setup(const char *name, const char *tty);
+extern int lxc_setup(const char *name, const char *tty,
+ const struct lxc_tty_info *tty_info);
extern int conf_has(const char *name, const char *info);
@@ -148,6 +178,7 @@ extern int conf_has(const char *name, const char *info);
#define conf_has_utsname(__name) conf_has(__name, "utsname")
#define conf_has_network(__name) conf_has(__name, "network")
#define conf_has_console(__name) conf_has(__name, "console")
-#define conf_has_cgroup(__name) conf_has(__name, "cgroup")
+#define conf_has_cgroup(__name) conf_has(__name, "cgroup")
+#define conf_has_tty(__name) conf_has(__name, "tty")
#endif
diff --git a/src/lxc/lxc_config.c b/src/lxc/lxc_config.c
index 10361486d..8b83a83d5 100644
--- a/src/lxc/lxc_config.c
+++ b/src/lxc/lxc_config.c
@@ -37,6 +37,7 @@
typedef int (*file_cb)(char* buffer, void *data);
typedef int (*config_cb)(const char *, char *, struct lxc_conf *);
+static int config_tty(const char *, char *, struct lxc_conf *);
static int config_cgroup(const char *, char *, struct lxc_conf *);
static int config_mount(const char *, char *, struct lxc_conf *);
static int config_rootfs(const char *, char *, struct lxc_conf *);
@@ -56,6 +57,7 @@ struct config {
static struct config config[] = {
+ { "lxc.tty", config_tty },
{ "lxc.cgroup", config_cgroup },
{ "lxc.mount", config_mount },
{ "lxc.rootfs", config_rootfs },
@@ -417,6 +419,15 @@ static int config_network_ipv6(const char *key, char *value, struct lxc_conf *lx
return 0;
}
+static int config_tty(const char *key, char *value, struct lxc_conf *lxc_conf)
+{
+ int nbtty = atoi(value);
+
+ lxc_conf->tty = nbtty;
+
+ return 0;
+}
+
static int config_cgroup(const char *key, char *value, struct lxc_conf *lxc_conf)
{
char *token = "lxc.cgroup.";
@@ -580,6 +591,7 @@ int lxc_config_init(struct lxc_conf *conf)
conf->rootfs = NULL;
conf->fstab = NULL;
conf->utsname = NULL;
+ conf->tty = 0;
lxc_list_init(&conf->cgroup);
lxc_list_init(&conf->networks);
return 0;
diff --git a/src/lxc/lxc_console.c b/src/lxc/lxc_console.c
index b16b29fc0..6a9881a4f 100644
--- a/src/lxc/lxc_console.c
+++ b/src/lxc/lxc_console.c
@@ -20,14 +20,158 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include
-#include
-#include
-#include
+#define _GNU_SOURCE
+#include
+#undef _GNU_SOURCE
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "error.h"
+#include "lxc.h"
+
+void usage(char *cmd)
+{
+ fprintf(stderr, "%s \n", basename(cmd));
+ fprintf(stderr, "\t -n : name of the container\n");
+ fprintf(stderr, "\t -t : tty number\n");
+ _exit(1);
+}
int main(int argc, char *argv[])
{
- return 0;
-}
+ char opt;
+ char *name = NULL;
+ int ttynum = 0;
+ int nbargs = 0;
+ int master = -1;
+ int wait4q = 0;
+ int err = LXC_ERROR_INTERNAL;
+ struct termios tios, oldtios;
+ while ((opt = getopt(argc, argv, "t:n:")) != -1) {
+ switch (opt) {
+ case 'n':
+ name = optarg;
+ break;
+ case 't':
+ ttynum = atoi(optarg);
+ break;
+ }
+
+ nbargs++;
+ }
+
+ if (!name || !ttynum)
+ usage(argv[0]);
+
+ /* Get current termios */
+ if (tcgetattr(0, &tios)) {
+ lxc_log_error("failed to get current terminal settings");
+ fprintf(stderr, "%s\n", lxc_strerror(err));
+ return 1;
+ }
+
+ oldtios = tios;
+
+ /* Remove the echo characters and signal reception, the echo
+ * will be done below with master proxying */
+ tios.c_iflag &= ~IGNBRK;
+ tios.c_iflag &= BRKINT;
+ tios.c_lflag &= ~(ECHO|ICANON|ISIG);
+ tios.c_cc[VMIN] = 1;
+ tios.c_cc[VTIME] = 0;
+
+ /* Set new attributes */
+ if (tcsetattr(0, TCSAFLUSH, &tios)) {
+ lxc_log_syserror("failed to set new terminal settings");
+ fprintf(stderr, "%s\n", lxc_strerror(err));
+ return 1;
+ }
+
+ err = lxc_console(name, ttynum, &master);
+ if (err) {
+ fprintf(stderr, "%s\n", lxc_strerror(err));
+ goto out;
+ }
+
+ fprintf(stderr, "\nType to exit the console\n");
+
+ setsid();
+
+ err = 0;
+
+ /* let's proxy the tty */
+ for (;;) {
+ char c;
+ struct pollfd pfd[2] = {
+ { .fd = 0,
+ .events = POLLIN|POLLPRI,
+ .revents = 0 },
+ { .fd = master,
+ .events = POLLIN|POLLPRI,
+ .revents = 0 },
+ };
+
+ if (poll(pfd, 2, -1) < 0) {
+ if (errno == EINTR)
+ continue;
+ lxc_log_syserror("failed to poll");
+ goto out_err;
+ }
+
+ /* read the "stdin" and write that to the master
+ */
+ if (pfd[0].revents & POLLIN) {
+ read(0, &c, 1);
+
+ /* we want to exit the console with Ctrl+a q */
+ if (c == 1) {
+ wait4q = !wait4q;
+ continue;
+ }
+
+ if (c == 'q' && wait4q)
+ goto out;
+
+ wait4q = 0;
+ write(master, &c, 1);
+ }
+
+ /* other side has closed the connection */
+ if (pfd[1].revents & POLLHUP)
+ goto out;
+
+ /* read the master and write to "stdout" */
+ if (pfd[1].revents & POLLIN) {
+ read(master, &c, 1);
+ printf("%c", c);
+ fflush(stdout);
+ }
+ }
+out:
+ /* Restore previous terminal parameter */
+ tcsetattr(0, TCSAFLUSH, &oldtios);
+
+ /* Return to line it is */
+ printf("\n");
+
+ close(master);
+
+ return err;
+
+out_err:
+ fprintf(stderr, "%s\n", lxc_strerror(-LXC_ERROR_INTERNAL));
+ err = 1;
+ goto out;
+}
diff --git a/src/lxc/lxc_init.c b/src/lxc/lxc_init.c
index e18eaef6f..13caec63e 100644
--- a/src/lxc/lxc_init.c
+++ b/src/lxc/lxc_init.c
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -42,7 +43,6 @@ static struct option options[] = {
int main(int argc, char *argv[])
{
pid_t pid;
-
int nbargs = 0;
char **aargv;
@@ -85,6 +85,8 @@ int main(int argc, char *argv[])
exit(1);
}
+
+
for (;;) {
int status;
if (wait(&status) < 0) {
diff --git a/src/lxc/lxc_lock.c b/src/lxc/lxc_lock.c
index 0eadfabc2..a5408d527 100644
--- a/src/lxc/lxc_lock.c
+++ b/src/lxc/lxc_lock.c
@@ -30,6 +30,7 @@
#include
#include
+#include "error.h"
#include
int lxc_get_lock(const char *name)
@@ -38,23 +39,46 @@ int lxc_get_lock(const char *name)
int fd, ret;
snprintf(lock, MAXPATHLEN, LXCPATH "/%s", name);
+
+ /* need to check access because of cap_dac_override */
+ if (access(lock, R_OK |W_OK | X_OK)) {
+ ret = errno;
+ goto out_err;
+ }
+
fd = open(lock, O_RDONLY|O_DIRECTORY, S_IRUSR|S_IWUSR);
if (fd < 0) {
- ret = -errno;
- goto out;
+ ret = errno;
+ goto out_err;
}
fcntl(fd, F_SETFD, FD_CLOEXEC);
if (flock(fd, LOCK_EX|LOCK_NB)) {
- ret = -errno;
+ ret = errno;
close(fd);
- goto out;
+ goto out_err;
}
ret = fd;
out:
return ret;
+
+out_err:
+ switch (ret) {
+ case EWOULDBLOCK:
+ ret = -LXC_ERROR_EBUSY;
+ goto out;
+ case ENOENT:
+ ret = -LXC_ERROR_ENOENT;
+ goto out;
+ case EACCES:
+ ret = -LXC_ERROR_EACCES;
+ goto out;
+ default:
+ ret = -LXC_ERROR_LOCK;
+ goto out;
+ }
}
void lxc_put_lock(int lock)
diff --git a/src/lxc/lxc_start.c b/src/lxc/lxc_start.c
index a4f84d1ef..ed7af4756 100644
--- a/src/lxc/lxc_start.c
+++ b/src/lxc/lxc_start.c
@@ -24,6 +24,8 @@
#include
#include
#include
+#include
+#include
#include
#include
#include
@@ -46,7 +48,9 @@ int main(int argc, char *argv[])
char opt;
char *name = NULL;
char **args;
- int err, nbargs = 0;
+ int err = LXC_ERROR_INTERNAL, nbargs = 0;
+ struct termios tios;
+
char *default_args[] = {
"/sbin/init",
'\0',
@@ -72,12 +76,21 @@ int main(int argc, char *argv[])
if (!name)
usage(argv[0]);
- err = lxc_start(name, args);
- if (err) {
+ if (tcgetattr(0, &tios)) {
+ lxc_log_error("failed to get current terminal settings");
fprintf(stderr, "%s\n", lxc_strerror(err));
return 1;
}
- return 0;
+ err = lxc_start(name, args);
+ if (err) {
+ fprintf(stderr, "%s\n", lxc_strerror(err));
+ err = 1;
+ }
+
+ if (tcsetattr(0, TCSAFLUSH, &tios))
+ lxc_log_syserror("failed to restore terminal attributes");
+
+ return err;
}
diff --git a/src/lxc/mainloop.c b/src/lxc/mainloop.c
new file mode 100644
index 000000000..d951801aa
--- /dev/null
+++ b/src/lxc/mainloop.c
@@ -0,0 +1,178 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "mainloop.h"
+
+struct lxc_handler {
+ lxc_mainloop_callback_t callback;
+ int fd;
+ void *data;
+};
+
+int lxc_mainloop(struct lxc_epoll_descr *descr)
+{
+ int i, nfds, triggered;
+ struct lxc_handler *handler;
+
+ for (;;) {
+
+ triggered = 0;
+
+ nfds = epoll_wait(descr->epfd, descr->ev, descr->nfds, -1);
+ if (nfds < 0) {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+
+ for (i = 0; i < descr->nfds; i++) {
+
+ if (!(descr->ev[i].events & EPOLLIN) &&
+ !(descr->ev[i].events & EPOLLHUP))
+ continue;
+
+ triggered++;
+ handler = (struct lxc_handler *)descr->ev[i].data.ptr;
+
+ /* If the handler returns a positive value, exit
+ the mainloop */
+ if (handler->callback(handler->fd, handler->data,
+ descr) > 0)
+ return 0;
+
+ if (triggered == nfds)
+ break;
+ }
+
+ if (!descr->nfds)
+ return 0;
+ }
+}
+
+int lxc_mainloop_add_handler(struct lxc_epoll_descr *descr, int fd,
+ lxc_mainloop_callback_t callback, void *data)
+{
+ struct epoll_event *ev;
+ struct lxc_handler *handler;
+ int ret = -1;
+
+ handler = malloc(sizeof(*handler));
+ if (!handler)
+ return -1;
+
+ handler->callback = callback;
+ handler->fd = fd;
+ handler->data = data;
+
+ ev = malloc(sizeof(*descr->ev) * (descr->nfds + 1));
+ if (!ev)
+ goto out_free;
+
+ if (descr->nfds) {
+ memcpy(ev, descr->ev, sizeof(*descr->ev) * (descr->nfds));
+ free(descr->ev);
+ }
+
+ descr->ev = ev;
+ descr->ev[descr->nfds].events = EPOLLIN;
+ descr->ev[descr->nfds].data.ptr = handler;
+
+ ret = epoll_ctl(descr->epfd, EPOLL_CTL_ADD, fd,
+ &descr->ev[descr->nfds]);
+
+ descr->nfds++;
+out:
+ return ret;
+
+out_free:
+ free(handler);
+ goto out;
+}
+
+int lxc_mainloop_del_handler(struct lxc_epoll_descr *descr, int fd)
+{
+ struct epoll_event *ev;
+ struct lxc_handler *handler;
+ int i, j, idx = 0;
+
+ for (i = 0; i < descr->nfds; i++) {
+
+ handler = descr->ev[i].data.ptr;
+
+ if (handler->fd != fd)
+ continue;
+
+ if (epoll_ctl(descr->epfd, EPOLL_CTL_DEL, fd, NULL))
+ return -1;
+
+ ev = malloc(sizeof(*ev) * (descr->nfds - 1));
+ if (!ev)
+ return -1;
+
+ for (j = 0; j < descr->nfds; j++) {
+ if (i == j)
+ continue;
+ ev[idx] = descr->ev[idx];
+ idx++;
+ }
+
+ free(descr->ev[i].data.ptr);
+ free(descr->ev);
+ descr->ev = ev;
+ descr->nfds--;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+int lxc_mainloop_open(int size, struct lxc_epoll_descr *descr)
+{
+ descr->nfds = 0;
+ descr->ev = NULL;
+
+ descr->epfd = epoll_create(size);
+ if (descr->epfd < 0)
+ return -1;
+
+ return 0;
+}
+
+int lxc_mainloop_close(struct lxc_epoll_descr *descr)
+{
+ int i;
+
+ for (i = 0; i < descr->nfds; i++)
+ free(descr->ev[i].data.ptr);
+ free(descr->ev);
+
+ return close(descr->epfd);
+}
+
diff --git a/src/lxc/mainloop.h b/src/lxc/mainloop.h
new file mode 100644
index 000000000..5ee02280f
--- /dev/null
+++ b/src/lxc/mainloop.h
@@ -0,0 +1,45 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+struct epoll_event;
+
+struct lxc_epoll_descr {
+ int epfd;
+ int nfds;
+ struct epoll_event *ev;
+};
+
+typedef int (*lxc_mainloop_callback_t)(int fd, void *data,
+ struct lxc_epoll_descr *descr);
+
+extern int lxc_mainloop(struct lxc_epoll_descr *descr);
+
+extern int lxc_mainloop_add_handler(struct lxc_epoll_descr *descr, int fd,
+ lxc_mainloop_callback_t callback,
+ void *data);
+
+extern int lxc_mainloop_del_handler(struct lxc_epoll_descr *descr, int fd);
+
+extern int lxc_mainloop_open(int size, struct lxc_epoll_descr *descr);
+
+extern int lxc_mainloop_close(struct lxc_epoll_descr *descr);
diff --git a/src/lxc/restart.c b/src/lxc/restart.c
index 38ba092c5..bc2329312 100644
--- a/src/lxc/restart.c
+++ b/src/lxc/restart.c
@@ -47,6 +47,7 @@ LXC_TTY_HANDLER(SIGQUIT);
int lxc_restart(const char *name, const char *statefile,
unsigned long flags)
{
+ struct lxc_tty_info tty_info = { 0 };
char *init = NULL, *val = NULL;
char tty[MAXPATHLEN];
int fd, lock, sv[2], sync = 0, err = -1;
@@ -55,13 +56,12 @@ int lxc_restart(const char *name, const char *statefile,
lock = lxc_get_lock(name);
if (lock < 0)
- return lock == -EWOULDBLOCK ?
- -LXC_ERROR_BUSY :
- -LXC_ERROR_LOCK;
+ return lock;
/* Begin the set the state to STARTING*/
if (lxc_setstate(name, STARTING)) {
- lxc_log_error("failed to set state %s", lxc_state2str(STARTING));
+ lxc_log_error("failed to set state %s",
+ lxc_state2str(STARTING));
goto out;
}
@@ -113,7 +113,7 @@ int lxc_restart(const char *name, const char *statefile,
}
/* Setup the container, ip, names, utsname, ... */
- if (lxc_setup(name, tty)) {
+ if (lxc_setup(name, tty, &tty_info)) {
lxc_log_error("failed to setup the container");
if (write(sv[0], &sync, sizeof(sync)) < 0)
lxc_log_syserror("failed to write the socket");
diff --git a/src/lxc/start.c b/src/lxc/start.c
index c539fa94e..b7e587000 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -30,6 +30,8 @@
#include
#include
#include
+#include
+#include
#include
#include
#include
@@ -37,31 +39,216 @@
#include
#include
#include
+#include
+#include
+#include
#include "error.h"
+#include "af_unix.h"
+#include "mainloop.h"
#include
LXC_TTY_HANDLER(SIGINT);
LXC_TTY_HANDLER(SIGQUIT);
+static int setup_sigchld_fd(sigset_t *oldmask)
+{
+ sigset_t mask;
+ int fd;
+
+ if (sigprocmask(SIG_BLOCK, NULL, &mask)) {
+ lxc_log_syserror("failed to get mask signal");
+ return -1;
+ }
+
+ if (sigaddset(&mask, SIGCHLD) || sigprocmask(SIG_BLOCK, &mask, oldmask)) {
+ lxc_log_syserror("failed to set mask signal");
+ return -1;
+ }
+
+ fd = signalfd(-1, &mask, 0);
+ if (fd < 0) {
+ lxc_log_syserror("failed to create the signal fd");
+ return -1;
+ }
+
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
+ lxc_log_syserror("failed to set sigfd to close-on-exec");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int setup_tty_service(const char *name, int *ttyfd)
+{
+ int fd;
+ struct sockaddr_un addr = { 0 };
+ char *offset = &addr.sun_path[1];
+
+ strcpy(offset, name);
+ addr.sun_path[0] = '\0';
+
+ fd = lxc_af_unix_open(addr.sun_path, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
+ lxc_log_syserror("failed to close-on-exec flag");
+ close(fd);
+ return -1;
+ }
+
+ *ttyfd = fd;
+
+ return 0;
+}
+
+static int sigchld_handler(int fd, void *data,
+ struct lxc_epoll_descr *descr)
+{
+ pid_t *pid = data;
+
+ waitpid(*pid, NULL, 0);
+
+ return 1;
+}
+
+static int ttyclient_handler(int fd, void *data,
+ struct lxc_epoll_descr *descr)
+{
+ int i;
+ struct lxc_tty_info *tty_info = data;
+
+ for (i = 0; i < tty_info->nbtty; i++) {
+
+ if (tty_info->pty_info[i].busy != fd)
+ continue;
+
+ lxc_mainloop_del_handler(descr, fd);
+ tty_info->pty_info[i].busy = 0;
+ close(fd);
+ }
+
+ return 0;
+}
+
+static int ttyservice_handler(int fd, void *data,
+ struct lxc_epoll_descr *descr)
+{
+ int conn, ttynum, val = 1, ret = -1;
+ struct lxc_tty_info *tty_info = data;
+
+ conn = accept(fd, NULL, 0);
+ if (conn < 0) {
+ lxc_log_syserror("failed to accept tty client");
+ return -1;
+ }
+
+ if (setsockopt(conn, SOL_SOCKET, SO_PASSCRED, &val, sizeof(val))) {
+ lxc_log_syserror("failed to enable credential on socket");
+ goto out_close;
+ }
+
+ if (lxc_af_unix_rcv_credential(conn, &ttynum, sizeof(ttynum)))
+ goto out_close;
+
+ if (ttynum <= 0 || ttynum > tty_info->nbtty)
+ goto out_close;
+
+ /* fixup index array (eg. tty1 is index 0) */
+ ttynum--;
+
+ if (tty_info->pty_info[ttynum].busy)
+ goto out_close;
+
+ if (lxc_af_unix_send_fd(conn, tty_info->pty_info[ttynum].master,
+ NULL, 0) < 0) {
+ lxc_log_error("failed to send tty to client");
+ goto out_close;
+ }
+
+ if (lxc_mainloop_add_handler(descr, conn,
+ ttyclient_handler, tty_info)) {
+ lxc_log_error("failed to add tty client handler");
+ goto out_close;
+ }
+
+ tty_info->pty_info[ttynum].busy = conn;
+
+ ret = 0;
+
+out:
+ return ret;
+out_close:
+ close(conn);
+ goto out;
+}
+
+static int mainloop(const char *name, pid_t pid, int sigfd,
+ const struct lxc_tty_info *tty_info)
+{
+ int nfds, ttyfd = -1, ret = -1;
+ struct lxc_epoll_descr descr;
+
+ if (tty_info->nbtty && setup_tty_service(name, &ttyfd)) {
+ lxc_log_error("failed to create the tty service point");
+ goto out_sigfd;
+ }
+
+ /* sigfd + nb tty + tty service
+ * if tty is enabled */
+ nfds = tty_info->nbtty + 1 + tty_info->nbtty ? 1 : 0;
+
+ if (lxc_mainloop_open(nfds, &descr)) {
+ lxc_log_error("failed to create mainloop");
+ goto out_ttyfd;
+ }
+
+ if (lxc_mainloop_add_handler(&descr, sigfd, sigchld_handler, &pid)) {
+ lxc_log_error("failed to add handler for the signal");
+ goto out_mainloop_open;
+ }
+
+ if (tty_info->nbtty) {
+ if (lxc_mainloop_add_handler(&descr, ttyfd,
+ ttyservice_handler,
+ (void *)tty_info)) {
+ lxc_log_error("failed to add handler for the tty");
+ goto out_mainloop_open;
+ }
+ }
+
+ ret = lxc_mainloop(&descr);
+
+out:
+ return ret;
+
+out_mainloop_open:
+ lxc_mainloop_close(&descr);
+out_ttyfd:
+ close(ttyfd);
+out_sigfd:
+ close(sigfd);
+ goto out;
+}
+
int lxc_start(const char *name, char *argv[])
{
+ struct lxc_tty_info tty_info = { 0 };
+ sigset_t oldmask;
char init[MAXPATHLEN];
char tty[MAXPATHLEN];
char *val = NULL;
- int fd, lock, sv[2], sync = 0, err = -LXC_ERROR_INTERNAL;
+ int fd, sigfd, lock, sv[2], sync = 0, err = -LXC_ERROR_INTERNAL;
pid_t pid;
int clone_flags;
lock = lxc_get_lock(name);
- if (lock < 0) {
- if (lock == -EWOULDBLOCK)
- return -LXC_ERROR_BUSY;
- if (lock == -ENOENT)
- return -LXC_ERROR_NOT_FOUND;
- return -LXC_ERROR_LOCK;
- }
+ if (lock < 0)
+ return lock;
/* Begin the set the state to STARTING*/
if (lxc_setstate(name, STARTING)) {
@@ -74,6 +261,20 @@ int lxc_start(const char *name, char *argv[])
if (ttyname_r(0, tty, sizeof(tty)))
tty[0] = '\0';
+ if (lxc_create_tty(name, &tty_info)) {
+ lxc_log_error("failed to create the ttys");
+ goto out;
+ }
+
+ /* 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 and the command will be stuck */
+ sigfd = setup_sigchld_fd(&oldmask);
+ if (sigfd < 0) {
+ lxc_log_error("failed to set sigchild fd handler");
+ return -1;
+ }
+
/* Synchro socketpair */
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv)) {
lxc_log_syserror("failed to create communication socketpair");
@@ -99,6 +300,11 @@ int lxc_start(const char *name, char *argv[])
if (!pid) {
+ if (sigprocmask(SIG_SETMASK, &oldmask, NULL)) {
+ lxc_log_syserror("failed to set sigprocmask");
+ return -1;
+ }
+
close(sv[1]);
/* Be sure we don't inherit this after the exec */
@@ -117,7 +323,7 @@ int lxc_start(const char *name, char *argv[])
}
/* Setup the container, ip, names, utsname, ... */
- err = lxc_setup(name, tty);
+ err = lxc_setup(name, tty, &tty_info);
if (err) {
lxc_log_error("failed to setup the container");
if (write(sv[0], &err, sizeof(err)) < 0)
@@ -201,12 +407,9 @@ int lxc_start(const char *name, char *argv[])
goto err_state_failed;
}
-wait_again:
- if (waitpid(pid, NULL, 0) < 0) {
- if (errno == EINTR)
- goto wait_again;
- lxc_log_syserror("failed to wait the pid %d", pid);
- goto err_waitpid_failed;
+ if (mainloop(name, pid, sigfd, &tty_info)) {
+ lxc_log_error("mainloop exited with an error");
+ goto err_mailoop_failed;
}
if (lxc_setstate(name, STOPPING))
@@ -220,6 +423,7 @@ out:
if (lxc_setstate(name, STOPPED))
lxc_log_error("failed to set state %s", lxc_state2str(STOPPED));
+ lxc_delete_tty(&tty_info);
lxc_unlink_nsgroup(name);
unlink(init);
free(val);
@@ -240,7 +444,7 @@ err_pipe_write:
conf_destroy_network(name);
err_create_network:
err_pipe_read:
-err_waitpid_failed:
+err_mailoop_failed:
if (lxc_setstate(name, ABORTING))
lxc_log_error("failed to set state %s", lxc_state2str(STOPPED));
diff --git a/src/lxc/stop.c b/src/lxc/stop.c
index 4b359152c..eb1044ea1 100644
--- a/src/lxc/stop.c
+++ b/src/lxc/stop.c
@@ -31,7 +31,6 @@
#include
#include
-#include "error.h"
#include
#define MAXPIDLEN 20
@@ -46,20 +45,17 @@ int lxc_stop(const char *name)
lock = lxc_get_lock(name);
if (lock >= 0) {
lxc_put_lock(lock);
- return -LXC_ERROR_EMPTY;
+ return -LXC_ERROR_ESRCH;
}
- if (lock < 0 && lock != -EWOULDBLOCK) {
- if (lock == -ENOENT)
- return -LXC_ERROR_NOT_FOUND;
- return -LXC_ERROR_LOCK;
- }
+ if (lock < 0 && lock != -LXC_ERROR_EBUSY)
+ return lock;
snprintf(init, MAXPATHLEN, LXCPATH "/%s/init", name);
fd = open(init, O_RDONLY);
if (fd < 0) {
lxc_log_syserror("failed to open init file for %s", name);
- goto out_unlock;
+ goto out_close;
}
if (read(fd, val, sizeof(val)) < 0) {
@@ -83,7 +79,5 @@ int lxc_stop(const char *name)
out_close:
close(fd);
-out_unlock:
- lxc_put_lock(lock);
return ret;
}