From b0a33c1eb65d2c87e886c740a0dadd8ad5f8d87d Mon Sep 17 00:00:00 2001 From: dlezcano Date: Sun, 25 Jan 2009 21:52:38 +0000 Subject: [PATCH] From: Daniel Lezcano Console support for the system container. Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano --- configure.in | 3 +- doc/Makefile.am | 1 + doc/lxc-console.sgml.in | 162 +++++++++++++++++++++++++++ doc/lxc.conf.sgml.in | 27 +++++ doc/lxc.sgml.in | 19 ++++ scripts/lxc-debian.in | 5 + src/lxc/Makefile.am | 5 + src/lxc/af_unix.c | 243 ++++++++++++++++++++++++++++++++++++++++ src/lxc/af_unix.h | 31 +++++ src/lxc/checkpoint.c | 10 +- src/lxc/console.c | 71 ++++++++++++ src/lxc/create.c | 2 +- src/lxc/destroy.c | 9 +- src/lxc/error.c | 14 ++- src/lxc/error.h | 14 ++- src/lxc/lxc.h | 3 +- src/lxc/lxc_conf.c | 132 +++++++++++++++++++++- src/lxc/lxc_conf.h | 35 +++++- src/lxc/lxc_config.c | 12 ++ src/lxc/lxc_console.c | 150 ++++++++++++++++++++++++- src/lxc/lxc_init.c | 4 +- src/lxc/lxc_lock.c | 32 +++++- src/lxc/lxc_start.c | 19 +++- src/lxc/mainloop.c | 178 +++++++++++++++++++++++++++++ src/lxc/mainloop.h | 45 ++++++++ src/lxc/restart.c | 10 +- src/lxc/start.c | 236 +++++++++++++++++++++++++++++++++++--- src/lxc/stop.c | 14 +-- 28 files changed, 1414 insertions(+), 72 deletions(-) create mode 100644 doc/lxc-console.sgml.in create mode 100644 src/lxc/af_unix.c create mode 100644 src/lxc/af_unix.h create mode 100644 src/lxc/console.c create mode 100644 src/lxc/mainloop.c create mode 100644 src/lxc/mainloop.h 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 */ + +#define _GNU_SOURCE #include +#undef _GNU_SOURCE +#include +#include +#include +#include +#include #include +#include +#include +#include #include +#include +#include -#include +#include "error.h" +#include "lxc.h" -int main(int argc, char *argv[]) +void usage(char *cmd) { - return 0; + 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[]) +{ + 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]); + if (tcgetattr(0, &tios)) { + lxc_log_error("failed to get current terminal settings"); + fprintf(stderr, "%s\n", lxc_strerror(err)); + return 1; + } + err = lxc_start(name, args); if (err) { fprintf(stderr, "%s\n", lxc_strerror(err)); - return 1; + err = 1; } - return 0; + 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; } -- 2.39.5