On Mon, 06 Mar 2017 at 09:32:22 +0000, Simon McVittie wrote:
> systemd-nspawn creates a pty outside the container, but then bind-mounts
> it onto /dev/console inside the container. Maybe that would be another
> possibility?

This seems to work fine. tty(1) prints "/dev/console", the same as it does
inside a systemd-nspawn container, and I can run screen.

The attached patch also corrects the cleanup code path to only try to
unmount /dev/ptmx if it is actually mounted, which was a bug in my
previous patch. This requires mountpoint(1), which was in initscripts
(transitively Essential via either sysvinit-core or systemd, and
Priority: required, but not itself Essential) in jessie; it moved to
util-linux (actually Essential) in stretch.

This might be more of a post-stretch feature, I don't know. I wanted
to make sure there was a patch available that would work with the
chroots where /dev/pts is a symlink, even if the debootstrap change is
reverted in the short term.

If you have an informed opinion on my proposed patch on #817236, please
respond there - I'm still hoping that one can be fixed before stretch.

On Mon, 06 Mar 2017 at 09:46:57 +0000, Thorsten Glaser wrote:
> Simon McVittie dixit:
> >Perhaps bind-mounting the host's /dev/pts and also the host's /dev/ptmx
> >would work? I'll try that.

That didn't work in at least some of the situations covered by my
debootstrap test-case (which is essentially just running
script -c 'cat /etc/debian_version' /dev/null inside various types of
chroot). Unfortunately I've lost track of which scenarios failed.

> I’m running sid ;) With chroots from sarge up to sid.
> 
> Thankfully, with “old and then upgraded” chroots, I have the
> proper device node instead of the symlink and have, so far,
> not seen any problems. (Manual package cleanup aside.)

I'm surprised by this. I would have expected that interactive use of
tty things inside pbuilder would have started failing when you upgraded
to a v4.7+ kernel, because the /dev/pts inside the chroot would no longer
be able to "see" the terminal that is on pbuilder's stdin.

If you do a `pbuilder login` (or use your failing-build hook) and run
tty(1) at the resulting prompt, what do you get?

I should point out that if your chroots are sufficiently old, and you
are operating via an "upgrade old chroots" model, then the chroots are
not as minimal as they should be (for instance init is no longer Essential
in stretch). However, you're using pbuilder and not sbuild, so you
have already accepted some risk of having your build environment not match
official buildds' build environments.

Regards,
    S
>From 1afb1579c0e456c826bbc1a670050aba130dbf76 Mon Sep 17 00:00:00 2001
From: Simon McVittie <s...@debian.org>
Date: Mon, 20 Feb 2017 09:58:12 +0000
Subject: [PATCH] pbuilder-modules: Set up /dev/ptmx, /dev/pts for modern Linux

Mounting a new instance of /dev/pts is best-practice in all Linux
versions since v2.6.29, but will not actually work in all configurations
of those kernels prior to v4.7.

See the new comments for the gory details.

Depend on recent util-linux or on older initscripts, for mountpoint(1)
(which moved from initscripts to util-linux between jessie and stretch).

Signed-off-by: Simon McVittie <s...@debian.org>
---
 debian/control   |  1 +
 pbuilder-modules | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/debian/control b/debian/control
index 53e07bb..4c94ad1 100644
--- a/debian/control
+++ b/debian/control
@@ -25,6 +25,7 @@ Multi-Arch: foreign
 Depends:
  debootstrap | cdebootstrap,
  dpkg-dev (>= 1.17.0),
+ util-linux (>= 2.26.2-4) | initscripts (<< 2.88dsf-59.1),
  wget,
  ${misc:Depends},
 Recommends:
diff --git a/pbuilder-modules b/pbuilder-modules
index 18e14b5..61351a1 100644
--- a/pbuilder-modules
+++ b/pbuilder-modules
@@ -276,6 +276,12 @@ function umountproc () {
         umount_one "$SELINUX"
     fi
     if [ "$DEB_BUILD_ARCH_OS" = "linux" ] && [ "$USEDEVPTS" = "yes" ]; then
+        if mountpoint -q "$BUILDPLACE/dev/console"; then
+            umount_one "dev/console"
+        fi
+        if mountpoint -q "$BUILDPLACE/dev/ptmx"; then
+            umount_one "dev/ptmx"
+        fi
         umount_one "dev/pts"
     fi
     if [ "$DEB_BUILD_ARCH_OS" = "kfreebsd" ] || [ "$USEDEVFS" = "yes" ]; then
@@ -383,8 +389,52 @@ function mountproc () {
         TTYGRP=5
         TTYMODE=620
         [ -f /etc/default/devpts ] && . /etc/default/devpts
-        mount -t devpts none "$BUILDPLACE/dev/pts" -onoexec,nosuid,gid=$TTYGRP,mode=$TTYMODE
+        # There are three possibilities here:
+        # - Modern (Linux >= 4.7): /dev/pts is always a new instance,
+        #   and opening /dev/ptmx really opens /dev/pts/ptmx.
+        #   The first mount attempt will succeed.
+        # - Awkward transitional period (Linux < 4.7 with
+        #   CONFIG_DEVPTS_MULTIPLE_INSTANCES=y): /dev/pts can be a new
+        #   instance but is not by default. We want to mount a new
+        #   instance, but if we do that, we must ensure that /dev/ptmx is
+        #   a symbolic link to /dev/pts/ptmx or a bind-mount of
+        #   /dev/pts/ptmx, and that /dev/pts/ptmx has usable permissions
+        #   (which for some reason it does not by default).
+        #   The first mount attempt will succeed.
+        # - Legacy mode (Linux < 2.6.29, or < 4.7 with
+        #   CONFIG_DEVPTS_MULTIPLE_INSTANCES=n): /dev/pts is not
+        #   virtualized at all. The second mount attempt will succeed.
+        #   /dev/pts/ptmx will not exist.
+        local devpts_options="noexec,nosuid,gid=$TTYGRP,mode=$TTYMODE"
+        if ! mount -t devpts none "$BUILDPLACE/dev/pts" -o "$devpts_options,newinstance,ptmxmode=666"; then
+            mount -t devpts none "$BUILDPLACE/dev/pts" -o "$devpts_options"
+        fi
         mounted[${#mounted[@]}]="$BUILDPLACE/dev/pts"
+
+        # Depending on how /dev was set up, /dev/ptmx might either be
+        # character device (5,2), or a symbolic link to pts/ptmx.
+        # For the transitional case, we want it to be equivalent to
+        # /dev/pts/ptmx, but we can't do that unconditionally because
+        # in the legacy case, /dev/pts/ptmx won't exist.
+        if [ -e "$BUILDPLACE/dev/pts/ptmx" ] && \
+                ! [ -L "$BUILDPLACE/dev/ptmx" ]; then
+            chmod 666 "$BUILDPLACE/dev/pts/ptmx"
+            mount --bind "$BUILDPLACE/dev/pts/ptmx" "$BUILDPLACE/dev/ptmx"
+            mounted[${#mounted[@]}]="$BUILDPLACE/dev/ptmx"
+        fi
+
+        # If pbuilder was invoked from a terminal, we still want to be able to
+        # access that terminal. lxc and systemd-nspawn achieve this by
+        # binding it onto /dev/console; so can we.
+        if stdin_tty="$(tty)"; then
+            if [ ! -e "$BUILDPLACE/dev/console" ]; then
+                # We need something to mount onto, and it might as well be
+                # the correctly-numbered device node.
+                mknod -m700 "$BUILDPLACE/dev/console" c 5 1
+            fi
+
+            mount --bind "$stdin_tty" "$BUILDPLACE/dev/console"
+        fi
     fi
     if [ -n "$SELINUX" ]; then
         log.i "mounting selinux filesystem"
-- 
2.11.0

Reply via email to