Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect whether we are running in containers and act responsively. #2063

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
77 changes: 65 additions & 12 deletions host-bin/enter-chroot
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,32 @@ bindmount() {
fi
mkdir -p "$target"
mount --bind $bindopts "$1" "$target"
if [ -n "$3" ]; then
# Determine whether we really need to remount or not by checking the existing
# mount options. We are doing this since remount is not allowed in containers.
if [ -n "$3" ] && ! checkmountopt "$target" "$3"; then
mount -i -o "remount,$3" "$target"
fi
}

# Recursively bind-mounts $1 into $CHROOT/${2:-"$1"} if $2 is not already mounted
# If $3 is specified, remounts with the specified options.
# If $1 starts with a -, it's considered options to the bind mount, and the rest
# of the parameters are shifted.
rbindmount() {
bindopts=''
if [ ! "${1#"-"}" = "$1" ]; then
bindopts="$1"
shift
fi
local target="`fixabslinks "${2:-"$1"}"`"
if mountpoint -q "$target"; then
return 0
fi
mkdir -p "$target"
mount --rbind $bindopts "$1" "$target"
# Determine whether we really need to remount or not by checking the existing
# mount options. We are doing this since remount is not allowed in containers.
if [ -n "$3" ] && ! checkmountopt "$target" "$3"; then
mount -i -o "remount,$3" "$target"
fi
}
Expand All @@ -338,23 +363,37 @@ tmpfsmount() {
mount -i -t tmpfs -o "rw${2:+,}$2" tmpfs "$target"
}

# Bind-mounts /dev/$1 into $CHROOT/dev/$1 if it is not already mounted
devmknod() {
local target="`fixabslinks "/dev/$1"`"
if mountpoint -q "$target"; then
return 0;
fi
if [ ! -f "$target" ]; then
touch $target
fi
mount --bind "/dev/$1" "$target"
}

# If /var/run isn't mounted, we know the chroot hasn't been started yet.
if mountpoint -q "`fixabslinks '/var/run'`"; then
firstrun=''
else
firstrun='y'
fi

bindmount /dev
bindmount /dev/pts
bindmount /dev/shm
rbindmount /dev
bindmount /tmp /tmp exec
bindmount /proc
tmpfsmount /var/run 'noexec,nosuid,mode=0755,size=10%'
tmpfsmount /var/run/lock 'noexec,nosuid,nodev,size=5120k'
bindmount /var/run/dbus /var/host/dbus
bindmount /var/run/dbus /var/host/dbus 2>/dev/null || true
bindmount /var/run/shill /var/host/shill
bindmount /var/lib/timezone /var/host/timezone
# If findnacld socket exists, bind-mount it into chroot.
if [ -S "/var/run/crouton-ext/socket" ]; then
bindmount /var/run/crouton-ext
fi
for m in /lib/modules/*; do
if [ -d "$m" ]; then
bindmount '-o ro' "$m"
Expand Down Expand Up @@ -384,8 +423,10 @@ WARNING: CRAS not running in Chromium OS. Audio forwarding will not work." 1>&2
fi

# Bind-mount /media, specifically the removable directory
# It is possible that /media is not mounted inside containers,so bind-mount it
# only if /media is a mountpoint.
destmedia="`fixabslinks '/var/host/media'`"
if ! mountpoint -q "$destmedia"; then
if mountpoint -q "/media" && ! mountpoint -q "$destmedia"; then
mount --make-shared /media
mkdir -p "$destmedia" "$CHROOT/media"
ln -sf "/var/host/media/removable" "$CHROOT/media/"
Expand Down Expand Up @@ -523,7 +564,9 @@ if [ -z "$NOLOGIN" -a -f "$shares" ]; then
fi

# Bind-mount /sys recursively, making it a slave in the chroot
if ! mountpoint -q "$CHROOT/sys"; then
# It is possible that /sys is not mounted inside containers,so bind-mount it
# only if /sys is a mountpoint.
if mountpoint -q "/sys" && ! mountpoint -q "$CHROOT/sys"; then
mkdir -p "$CHROOT/sys"
mount --make-rshared /sys
mount --rbind /sys "$CHROOT/sys"
Expand Down Expand Up @@ -638,13 +681,18 @@ if [ ! "$NOLOGIN" = 1 ] && grep -q '^root:' "$passwd" 2>/dev/null; then
mkdir -p "$systemd_dir"

# Create systemd cgroup if necessary
start_systemd=true
if ! mountpoint -q "$CHROOT/sys/fs/cgroup/systemd"; then
mkdir -p "$CHROOT/sys/fs/cgroup/systemd"
mount -t cgroup -o nosuid,noexec,nodev,none,name=systemd systemd \
"$CHROOT/sys/fs/cgroup/systemd"
if ! mkdir -p "$CHROOT/sys/fs/cgroup/systemd" || \
! mount -t cgroup -o nosuid,noexec,nodev,none,name=systemd systemd \
"$CHROOT/sys/fs/cgroup/systemd"; then
start_systemd=false
fi
fi
# We cannot create/mount cgroup dirs in containers, so don't start systemd.
if $start_systemd; then
chrootcmd "/lib/systemd/systemd-logind >/dev/null 2>&1 </dev/null &"
fi
# systemd-logind doesn't fork
chrootcmd "/lib/systemd/systemd-logind >/dev/null 2>&1 </dev/null &"
fi

# Launch a daemon that can kill frecon when an X server is launched.
Expand All @@ -667,6 +715,11 @@ if [ ! "$NOLOGIN" = 1 ] && grep -q '^root:' "$passwd" 2>/dev/null; then
) &"
addtrap "(echo $$ >> '$CROUTONLOCKDIR/frecon') & :"
fi

# If the socket file does not exist, it means findnacld is not started.
if [ ! -S "/var/run/crouton-ext/socket" ]; then
chrootcmd "/usr/local/bin/croutonfindnacld >/dev/null 2>&1 </dev/null &"
fi
fi

# Start the chroot and any specified command
Expand Down
7 changes: 6 additions & 1 deletion host-bin/mount-chroot
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,12 @@ for NAME in "$@"; do
if ! mountpoint -q "$CHROOT"; then
if [ -z "$ENCRYPT" ]; then
mount --bind "$CHROOTSRC" "$CHROOT"
mount -i -o "remount,$MOUNTOPTS" "$CHROOT"
# Determine whether we really need to remount by checking the
# existing mount options. Doing this because we are not able to do
# remount in containers.
if ! checkmountopt "$CHROOT" "$MOUNTOPTS"; then
mount -i -o "remount,$MOUNTOPTS" "$CHROOT"
fi
continue
fi

Expand Down
13 changes: 11 additions & 2 deletions host-bin/unmount-chroot
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,11 @@ unmount() {
# point actually ends with ' (deleted)')
# umount has a bug and may return 0 when many mount points cannot be
# unmounted, so we call it once per mount point ('-n 1')
arg=''
while ! sed "s=\\\\040=//=g" /proc/mounts | cut -d' ' -f2 | filter \
| sed -e 's=//= =g;s/^\(\(.*\) (deleted)\)$/\1\n\2/' \
| sort -r | xargs --no-run-if-empty -d '
' -n 1 umount 2>/dev/null; do
' -n 1 umount "$arg" 2>/dev/null; do
if [ "$ntries" -eq "$TRIES" ]; then
# Send signal to all processes running under the chroot
# ...but confirm first.
Expand All @@ -267,6 +268,11 @@ unmount() {
if [ -z "$printonly" ]; then
echo "Sending SIG$SIGNAL to processes under $CHROOTSRC..." 1>&2
fi
# If there is no process running under $CHROOTSRC and we cannot
# unmount, then use lazy unmount.
# FIXME: This is a hack for running in containers, since we cannot
# unmount recursive bindmount in it.
arg='-l'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is a kernel bug. I'll try to reproduce on recent kernels and ask on LKML...

for root in /proc/*/root; do
if [ ! -r "$root" ] \
|| [ ! "`readlink -f "$root"`" = "$base" ]; then
Expand All @@ -280,6 +286,7 @@ unmount() {
if [ -z "$printonly" ]; then
kill "-$SIGNAL" "$pid" 2>/dev/null || true
fi
arg=''
done

# Escalate
Expand Down Expand Up @@ -324,7 +331,9 @@ done
if checkusage "$ROOT/media"; then
for usbp in /sys/bus/usb/devices/*/power/persist; do
if [ -e "$usbp" ]; then
echo 0 > "$usbp"
# Don't fail since we don't have permission to write /sys/* if we are in
# container.
echo 0 > "$usbp" || true
fi
done
fi
Expand Down
41 changes: 40 additions & 1 deletion installer/functions
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,43 @@ undotrap() {
settrap "$TRAP"
}

# Check if we are able to mknod.
capmknod() {
local tmp=`mktemp -d --tmpdir=/tmp 'crouton-mknod.XXX'`
local ret=0
if ! mknod "$tmp/test-mknod" c 0 0 2>/dev/null; then
ret=1
fi
rm -rf "$tmp"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You leave the directory $tmp dangling if the test fails.

return $ret
}

# Check whether $1 is mounted with options $2.
# We check the options of a mount point by inspecting /proc/mounts.
# For options dev, suid and exec, since they will not show up in /proc/mounts,
# we check whether their opposite options (nodev, nosuid, noexec) exists.
checkmountopt() {
local opt
local opts="$2,"
while [ -n "$opts" ]; do
opt=${opts%%,*}
opts=${opts#*,}
case $opt in
dev|suid|exec)
if ! awk -v dir="$1" -v opt="no$opt(,|$)" \
'$2 == dir && $4 ~ opt { exit 1 }' "/proc/mounts"; then
return 1
fi;;
*)
if ! awk -v dir="$1" -v opt="$opt(,|$)" \
'$2 == dir && $4 !~ opt { exit 1 }' "/proc/mounts"; then
return 1
fi;;
esac
done
return 0
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return 0 for clarity


# Works mostly like built-in getopts but silently coalesces positional arguments.
# Does not take parameters. Set getopts_string prior to calling.
# Sets getopts_var and getopts_arg.
Expand Down Expand Up @@ -156,7 +193,9 @@ release() {
# sure the kernel does not panic (this is the default configuration of a vanilla
# kernel). See crbug.com/260955 for details.
disablehungtask() {
echo 0 > /proc/sys/kernel/hung_task_panic
# Don't fail since we do not have permission to write /proc/sys/* if we are
# in container.
echo 0 2>/dev/null > /proc/sys/kernel/hung_task_panic || true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

echo 0 > /proc/sys/kernel/hung_task_panic 2>/dev/null is a bit clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this, but it didn't work on my machine. It still printed out the error messages, and I'm not sure if it's the same on ChromeBook.
I guess what it did is something like this

open("proc/sys/kernel/hung_task_panic")
dup2()
open("/dev/null")
dup2()

and the error occurs (at first open) before we redirect the stderr.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah that makes sense... Thanks.

}

# Run an awk program, without buffering its output.
Expand Down
2 changes: 1 addition & 1 deletion installer/main.sh
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ vboot_is_safe() {
local tmp="`mktemp -d --tmpdir=/tmp 'crouton-rwtest.XXX'`"
local unmount="umount -l '$tmp' 2>/dev/null; rmdir '$tmp'"
addtrap "$unmount"
mount --bind / "$tmp" >/dev/null
mount --bind / "$tmp" >/dev/null 2>&1
local ret=1
mount -o remount,rw "$tmp" 2>/dev/null || ret=0
undotrap
Expand Down
11 changes: 10 additions & 1 deletion installer/ubuntu/bootstrap
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Check your internet connection or proxy settings and try again.'
fi
fi

# Patch debootstrap so that it retries downloading packages
# Patch debootstrap so that it retries downloading packages and does not mount /sys
echo 'Patching debootstrap...' 1>&2
if awk '
t == 3 && /warning RETRY/ { print "sleep 1"; t=4 }
Expand All @@ -42,6 +42,7 @@ if awk '
sub(/continue 2; fi/, "failed=y")
t=1
}
/if \[ -d \"\$TARGET\/sys\" \] && \\/ { sub(/if/, "if false \&\&") }
1
END { if (t != 4) exit 1 }
' "$tmp/functions" > "$tmp/functions.new"; then
Expand All @@ -66,6 +67,14 @@ if [ ! -f "$tmp/scripts/$RELEASE" ]; then
ln -s "$tmp/scripts/$BOOTSTRAP_RELEASE" "$tmp/scripts/$RELEASE"
fi

# Create a fake mknod for debootstrap if we don't have CAP_MKNOD.
if ! capmknod; then
mkdir "$tmp/bin"
echo "touch \$1" > "$tmp/bin/mknod"
chmod +x "$tmp/bin/mknod"
newpath="$tmp/bin:$newpath"
fi

# Grab the release and drop it into the subdirectory
echo 'Downloading bootstrap files...' 1>&2
PATH="$newpath" DEBOOTSTRAP_DIR="$tmp" $FAKEROOT \
Expand Down
24 changes: 20 additions & 4 deletions installer/ubuntu/prepare
Original file line number Diff line number Diff line change
Expand Up @@ -219,15 +219,31 @@ export DEBIAN_FRONTEND=noninteractive

# Run debootstrap second stage if it hasn't already happened
if [ -r /debootstrap ]; then
# Debootstrap doesn't like anything mounted under /sys or /var when it runs
# Debootstrap doesn't like anything mounted under /var when it runs.
# We request a re-mount after bootstrapping, so these mounts will be fixed.
# We also can't detect the mounts properly due to the chroot, so we have to
# recursively find mountpoints under /sys, and hardcode /var umounts.
find /sys/* -maxdepth 2 -depth -type d \
-exec mountpoint -q {} \; -exec umount {} \;
# hardcode /var umounts.
if mountpoint -q '/var/run/crouton-ext'; then
umount '/var/run/crouton-ext'
fi
umount '/var/run/lock' '/var/run'
# Create a fake mknod for debootstrap if we don't have CAP_MKNOD.
if ! capmknod; then
cp /bin/mknod /bin/mknod.old
echo "touch \$1" > /bin/mknod
fi
# Start the bootstrap
/debootstrap/debootstrap --second-stage
# If we cannot mknod, the installation of makedev will fail, so mark it as
# installed manually.
if ! capmknod; then
awk '
BEGIN { matched = 0 }
/^Package: makedev$/ { matched = 1 }
matched == 1 && /^Status:/ { sub($4, "installed"); matched = 0 }
1' /var/lib/dpkg/status > /var/lib/dpkg/status.new
mv -f /var/lib/dpkg/status.new /var/lib/dpkg/status
fi
# Clean contents of /var/run (since we'll always be mounting over it).
rm -rf --one-file-system /var/run/*
# Request a script restart to refresh the mount environment
Expand Down
Loading