glibc's grantpt and ptsname cannot be used on a fd for a pty not in
/dev/pts. The lxc controller tries to do just that. So if you try to
start a container on a system where /dev/pts/0 is not available, it
will fail. You can make this happen by opening a terminal on
/dev/pts/0, and doing 'sleep 2h & disown; exit'. To fix this, I call
the virFileOpenTtyAt() from a forked task in a new mount ns, and first
mount the container's /dev/pts onto /dev/pts. (Then the opened fd must
be passed back to the lxc driver). Another solution would be to just
do it all by hand without grantpt and ptsname.
Bug-Ubuntu:
https://bugs.launchpad.net/ubuntu/+source/libvirt/+bug/863629
Signed-off-by: Serge Hallyn <serge.hallyn(a)canonical.com>
---
src/lxc/lxc_controller.c | 117 ++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 112 insertions(+), 5 deletions(-)
diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c
index 51488e7..1a56e0c 100644
--- a/src/lxc/lxc_controller.c
+++ b/src/lxc/lxc_controller.c
@@ -780,6 +780,113 @@ static int lxcSetPersonality(virDomainDefPtr def)
# define MS_SLAVE (1<<19)
#endif
+static int send_pty(int sock, int *pty)
+{
+ struct iovec vector;
+ struct msghdr msg;
+ struct cmsghdr * cmsg;
+ int ret;
+
+ vector.iov_base = "PTY";
+ vector.iov_len = 3;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &vector;
+ msg.msg_iovlen = 1;
+
+ cmsg = alloca(sizeof(struct cmsghdr) + sizeof(*pty));
+ cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(*pty);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ memcpy(CMSG_DATA(cmsg), pty, sizeof(*pty));
+
+ msg.msg_control = cmsg;
+ msg.msg_controllen = cmsg->cmsg_len;
+
+ ret = sendmsg(sock, &msg, 0);
+ if (ret < 0)
+ return -1;
+ return 0;
+}
+
+static int recv_pty(int sock, int *pty, char **path, char *devpts)
+{
+ char buf[50];
+ struct iovec vector;
+ struct msghdr msg;
+ struct cmsghdr * cmsg;
+ int ret;
+
+ vector.iov_base = buf;
+ vector.iov_len = 50;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &vector;
+ msg.msg_iovlen = 1;
+
+ cmsg = alloca(sizeof(struct cmsghdr) + sizeof(*pty));
+ cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(*pty);
+ msg.msg_control = cmsg;
+ msg.msg_controllen = cmsg->cmsg_len;
+
+ ret = recvmsg(sock, &msg, 0);
+ if (ret < 0)
+ return ret;
+
+ memcpy(pty, CMSG_DATA(cmsg), sizeof(*pty));
+
+ if (VIR_ALLOC_N(*path, PATH_MAX) < 0) {
+ virReportSystemError(errno, "%s",
+ _("Failed to allocate space for ptyname"));
+ return -ENOMEM;
+ }
+ //snprintf(*path, PATH_MAX, "%s/0", devpts);
+ snprintf(*path, PATH_MAX, "/dev/pts/0");
+ return 0;
+}
+
+static int private_open_tty_at(char *devpts, char *devptmx,
+ int *containerPty,
+ char **containerPtyPath, int rawmode)
+{
+ int pid;
+ int ret;
+ int status;
+ int s[2];
+
+ ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, s);
+ if (ret < 0)
+ return ret;
+
+ pid = fork();
+ if (pid < 0)
+ exit(pid);
+ if (pid == 0) {
+ close(s[1]);
+ ret = unshare(CLONE_NEWNS);
+ if (ret < 0)
+ exit(ret);
+ ret = mount(devpts, "/dev/pts", "none", MS_BIND, NULL);
+ if (ret < 0)
+ exit(ret);
+ ret = virFileOpenTtyAt(devptmx, containerPty, containerPtyPath, rawmode);
+ if (ret < 0)
+ exit(ret);
+ send_pty(s[0], containerPty);
+ exit(ret);
+ }
+ close(s[0]);
+ ret = recv_pty(s[1], containerPty, containerPtyPath, devpts);
+ close(s[1]);
+ if (ret)
+ return ret;
+ waitpid(pid, &status, 0);
+ return WEXITSTATUS(status);
+}
+
static int
lxcControllerRun(virDomainDefPtr def,
unsigned int nveths,
@@ -894,12 +1001,12 @@ lxcControllerRun(virDomainDefPtr def,
if (devptmx) {
VIR_DEBUG("Opening tty on private %s", devptmx);
- if (virFileOpenTtyAt(devptmx,
- &containerPty,
- &containerPtyPath,
- 0) < 0) {
+ if (private_open_tty_at(devpts, devptmx,
+ &containerPty,
+ &containerPtyPath,
+ 0) < 0) {
virReportSystemError(errno, "%s",
- _("Failed to allocate tty"));
+ _("Failed to allocate tty"));
goto cleanup;
}
} else {
--
1.7.5.4