grml-chroot: properly handle multiple chroots [Closes: issue659] mika/grml_chroot_multi
authorMichael Prokop <mika@grml.org>
Fri, 13 Jan 2012 00:57:46 +0000 (01:57 +0100)
committerMichael Prokop <mika@grml.org>
Fri, 13 Jan 2012 02:21:26 +0000 (03:21 +0100)
Iff /tmp is available inside the chroot then use /tmp/grml-chroot
for storing information about active grml-chroot sessions.

This patch is based on work by Marc Haber and addresses the following issue
reported by him in http://bts.grml.org/grml/issue659:

| grml-chroot does not properly handle the situation when the user decides to
| chroot into the same chroot multiple times (for example, in different windows).
| When the first chroot terminates, the unconditional umount leaves the other
| chroots without mounted /proc, /dev etc.

Sadly this isn't bullet proof though.
Quoting Gebi from the according discussion on IRC (with his permission):

02:48 < gebi> you know that /tmp was to bind mounted too if you want to use X?
02:49 < gebi> not a good idea to use a bind mounted /tmp inside of the chroot for saving state which ultimatly goes back to the
              base image
02:50 < gebi> mrud: but you e.g HAVE to mount --bind /tmp chroot/tmp to use X in chroot
02:51 < gebi> so it would be no fun to use chroot/tmp as state dir for grml-chroot, because on unmounting your files would not be
              there anymore
02:51 < gebi> because hidden by the mount --bind

Thanks to Ulrich Dangel and Michael Gebetsroither for
code review and improvement suggestions.

usr_sbin/grml-chroot

index e9c1ae3..2540957 100755 (executable)
@@ -10,6 +10,12 @@ PROG_NAME_=$(basename $0)
 DEST_=""
 MOUNTED_=""     # all mounted destinations
 
+function bailout
+{
+  umount_all
+  die "Bailout"
+}
+trap bailout 1 2 3 3 6 9 14 15
 
 function die
 {
@@ -17,6 +23,16 @@ function die
     exit 1
 }
 
+function isMounted
+{
+    local dir="$1"
+    if cut -d\  -f 2 /proc/mounts | grep -q "$dir"; then
+        return 0
+    else
+        return 1
+    fi
+}
+
 function printUsage
 {
     cat <<EOT
@@ -45,19 +61,23 @@ function mountit
 
     local all_options_=""
 
-    if [[ $options_ == "--bind" ]]; then
+    if ! isMounted "${DEST_}/$dest_"; then
+      if [[ $options_ == "--bind" ]]; then
         all_options_="--bind $type_"
-    else
+      else
         all_options_="-t $type_ none"
+      fi
+      mount $all_options_ "${DEST_}/$dest_" && storeMounts "$dest_"
     fi
-    mount $all_options_ "${DEST_}/$dest_" && storeMounts "$dest_"
 }
 
 function umount_all
 {
-    for i in $MOUNTED_; do
-        umount "${DEST_}/$i"
+    for i in /proc /sys /dev; do
+        umount "${DEST_}/${i}"
     done
+
+    rm -f "$DEST_"/etc/debian_chroot
 }
 
 
@@ -89,9 +109,14 @@ mountit "proc"  "proc"
 mountit "sysfs" "sys"
 mountit "/dev"   "dev"   "--bind"
 
-WROTE_DEBIAN_CHROOT=""
-if [ ! -f "$DEST_"/etc/debian_chroot ]; then
-    WROTE_DEBIAN_CHROOT="yes"
+# do not write to /var/run of chroot if it's not present
+if [ -d "$DEST_/tmp" ] ; then
+  STATEDIR="tmp/grml-chroot"
+  mkdir -p "$DEST_/$STATEDIR"
+  touch "$DEST_/$STATEDIR/$$"
+fi
+
+if [ ! -e "$DEST_"/etc/debian_chroot ]; then
     echo "Writing /etc/debian_chroot ..."
     cat "$DEST_"/etc/hostname > "$DEST_"/etc/debian_chroot
 fi
@@ -103,10 +128,15 @@ else
     chroot "$DEST_" "$@"
     RC=$?
 fi
-umount_all
 
-if [ ! -z "$WROTE_DEBIAN_CHROOT" ]; then
-    rm "$DEST_"/etc/debian_chroot
+if [ -z "$STATEDIR" ] ; then
+  umount_all
+else
+  rm "$DEST_/$STATEDIR/$$"
+
+  if rmdir "$DEST_/$STATEDIR" 2>/dev/null; then
+    umount_all
+  fi
 fi
 
 exit $RC