Console support for the system container.
Signed-off-by: Daniel Lezcano <daniel.lezcano@free.fr>
Signed-off-by: Daniel Lezcano <dlezcano@fr.ibm.com>
# -*- 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])
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
lxc-execute.1 \
lxc-start.1 \
lxc-stop.1 \
+ lxc-console.1 \
lxc-freeze.1 \
lxc-unfreeze.1 \
lxc-monitor.1 \
--- /dev/null
+<!--
+
+lxc: linux Container library
+
+(C) Copyright IBM Corp. 2007, 2008
+
+Authors:
+Daniel Lezcano <dlezcano at fr.ibm.com>
+
+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
+
+-->
+
+<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN">
+
+<refentry>
+
+ <docinfo><date>@LXC_GENERATE_DATE@</date></docinfo>
+
+ <refmeta>
+ <refentrytitle>lxc-console</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>lxc-console</refname>
+
+ <refpurpose>
+ Launch a console for the specified container
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>lxc-console <replaceable>-n name</replaceable>
+ <replaceable>-t ttynum</replaceable>
+ </command>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+ <variablelist>
+
+ <varlistentry>
+ <term>
+ <option>-n <replaceable>name</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Specify the container name to open a console.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-t <replaceable>ttynum</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Specify the tty number to connect.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Diagnostic</title>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>tty service denied</term>
+ <listitem>
+ <para>
+ 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.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simpara>
+ <citerefentry>
+ <refentrytitle><command>lxc.conf</command></refentrytitle>
+ <manvolnum>5</manvolnum>
+ </citerefentry>
+ </simpara>
+
+
+ </refsect1>
+
+ <refsect1>
+ <title>Author</title>
+ <para>Daniel Lezcano <email>daniel.lezcano@free.fr</email></para>
+ </refsect1>
+
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:2
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:nil
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+-->
</refsect2>
+ <refsect2>
+ <title>Console through the ttys</title>
+ <para>
+ 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.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>lxc.tty</option>
+ </term>
+ <listitem>
+ <para>
+ Specify the number of tty to make available to the
+ container.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
+
<refsect2>
<title>Mount points</title>
<para>
</para>
</refsect2>
+ <refsect2>
+ <title>Connect to an available tty</title>
+ <para>
+ 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.
+ <programlisting>
+ lxc-console -n foo -t 3
+ </programlisting>
+ </para>
+ </refsect2>
+
<refsect2>
<title>Freeze / Unfreeze a container</title>
<para>
<manvolnum>1</manvolnum>
</citerefentry>,
+ <citerefentry>
+ <refentrytitle><command>lxc-console</command></refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>,
+
<citerefentry>
<refentrytitle><command>lxc-monitor</command></refentrytitle>
<manvolnum>1</manvolnum>
# 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
}
write_lxc_configuration() {
cat <<EOF > $CONFFILE
lxc.utsname = $UTSNAME
+lxc.tty = 4
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
INCLUDES= -I$(top_srcdir)/src -DLXCPATH="\"$(localstatedir)/lxc\"" -DLXCBINDIR="\"$(bindir)\""
+AM_LDFLAGS= -lutil
lib_LTLIBRARIES = liblxc.la
pkginclude_HEADERS = \
monitor.h \
start.c \
stop.c \
monitor.c monitor.h \
+ console.c \
freezer.c \
checkpoint.c \
restart.c \
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@
--- /dev/null
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano <dlezcano at fr.ibm.com>
+ *
+ * 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 <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#define __USE_GNU
+#include <sys/socket.h>
+#undef __USE_GNU
+#include <sys/un.h>
+
+
+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;
+}
--- /dev/null
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano <dlezcano at fr.ibm.com>
+ *
+ * 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);
+
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) {
out_close:
close(fd);
-out_unlock:
- lxc_put_lock(lock);
return ret;
}
--- /dev/null
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano <dlezcano at fr.ibm.com>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#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;
+}
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)
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);
[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",
[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",
};
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,
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,
#include <lxc/lxc_lock.h>
#include <lxc/lxc_namespace.h>
#include <lxc/lxc_utils.h>
+#include <lxc/error.h>
#include <lxc/cgroup.h>
#include <lxc/monitor.h>
* @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 <name>
#include <dirent.h>
#include <mntent.h>
#include <unistd.h>
+#include <pty.h>
#include <sys/types.h>
#include <sys/utsname.h>
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];
}
return symlink(absrootfs, path);
-
}
static int configure_mount(const char *name, const char *fstab)
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];
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;
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);
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;
#define _conf_h
#include <netinet/in.h>
+#include <sys/param.h>
enum {
EMPTY,
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
*/
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);
#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
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 *);
static struct config config[] = {
+ { "lxc.tty", config_tty },
{ "lxc.cgroup", config_cgroup },
{ "lxc.mount", config_mount },
{ "lxc.rootfs", config_rootfs },
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.";
conf->rootfs = NULL;
conf->fstab = NULL;
conf->utsname = NULL;
+ conf->tty = 0;
lxc_list_init(&conf->cgroup);
lxc_list_init(&conf->networks);
return 0;
* 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 <stdio.h>
+#undef _GNU_SOURCE
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <termios.h>
#include <unistd.h>
+#include <signal.h>
+#include <libgen.h>
+#include <sys/param.h>
#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
-#include <lxc/lxc.h>
+#include "error.h"
+#include "lxc.h"
-int main(int argc, char *argv[])
+void usage(char *cmd)
{
- return 0;
+ fprintf(stderr, "%s <command>\n", basename(cmd));
+ fprintf(stderr, "\t -n <name> : name of the container\n");
+ fprintf(stderr, "\t -t <tty#> : 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 <Ctrl+a q> 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;
+}
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
+#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
int main(int argc, char *argv[])
{
pid_t pid;
-
int nbargs = 0;
char **aargv;
exit(1);
}
+
+
for (;;) {
int status;
if (wait(&status) < 0) {
#include <sys/file.h>
#include <sys/param.h>
+#include "error.h"
#include <lxc/lxc.h>
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)
#include <libgen.h>
#include <unistd.h>
#include <string.h>
+#include <termios.h>
+#include <errno.h>
#include <sys/param.h>
#include <sys/utsname.h>
#include <sys/types.h>
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',
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;
}
--- /dev/null
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano <dlezcano at fr.ibm.com>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+
+#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);
+}
+
--- /dev/null
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano <dlezcano at fr.ibm.com>
+ *
+ * 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);
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;
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;
}
}
/* 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");
#include <errno.h>
#include <unistd.h>
#include <signal.h>
+#include <fcntl.h>
+#include <termios.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/capability.h>
#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/poll.h>
+#include <sys/signalfd.h>
#include "error.h"
+#include "af_unix.h"
+#include "mainloop.h"
#include <lxc/lxc.h>
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)) {
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");
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 */
}
/* 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)
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))
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);
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));
#include <sys/stat.h>
#include <fcntl.h>
-#include "error.h"
#include <lxc/lxc.h>
#define MAXPIDLEN 20
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) {
out_close:
close(fd);
-out_unlock:
- lxc_put_lock(lock);
return ret;
}