commit:     d39798015f4f34f48b7787934aafed4209ddebc2
Author:     Matt Turner <mattst88 <AT> gentoo <DOT> org>
AuthorDate: Sat May 16 21:44:37 2020 +0000
Commit:     Matt Turner <mattst88 <AT> gentoo <DOT> org>
CommitDate: Thu Oct 22 18:02:16 2020 +0000
URL:        https://gitweb.gentoo.org/proj/catalyst.git/commit/?id=d3979801

catalyst: Use libmount for handling mounts

Handle the mounts/unmounts in all in process rather than shelling out
(pun intended!) to an external program.

At the same time, remove kill_chroot_pids() since with mount namespaces
(which we've been using since e5a53e42 "catalyst: create namespaces for
building") this is unnecessary. mount_namespaces(7) says

       A mount ceases to be a member of a peer group when either the
       mount is explicitly unmounted, or when the mount is implicitly
       unmounted because a mount namespace is removed (because it  has
       no  more member processes).

So the mounts are implicitly unmounted when the last process in the
namespace dies.

Signed-off-by: Matt Turner <mattst88 <AT> gentoo.org>

 catalyst/base/stagebase.py          | 76 ++++++++++++++++---------------------
 targets/support/kill-chroot-pids.sh | 62 ------------------------------
 2 files changed, 33 insertions(+), 105 deletions(-)

diff --git a/catalyst/base/stagebase.py b/catalyst/base/stagebase.py
index be7b96c8..838fd2f1 100644
--- a/catalyst/base/stagebase.py
+++ b/catalyst/base/stagebase.py
@@ -6,6 +6,7 @@ import sys
 
 from pathlib import Path
 
+import libmount
 import toml
 
 from snakeoil import fileutils
@@ -629,17 +630,6 @@ class StageBase(TargetBase, ClearBase, GenBase):
             assert self.settings[verify] == "blake2"
             self.settings.setdefault("gk_mainargs", []).append("--b2sum")
 
-    def kill_chroot_pids(self):
-        log.info('Checking for processes running in chroot and killing them.')
-
-        # Force environment variables to be exported so script can see them
-        self.setup_environment()
-
-        killcmd = normpath(self.settings["sharedir"] +
-                           self.settings["shdir"] + 
"/support/kill-chroot-pids.sh")
-        if os.path.exists(killcmd):
-            cmd([killcmd], env=self.env)
-
     def mount_safety_check(self):
         """
         Check and verify that none of our paths in mypath are mounted. We don't
@@ -853,7 +843,8 @@ class StageBase(TargetBase, ClearBase, GenBase):
 
             source = str(self.mount[x]['source'])
             target = self.settings['chroot_path'] + 
str(self.mount[x]['target'])
-            mount = ['mount']
+            fstype = ''
+            options = ''
 
             log.debug('bind %s: "%s" -> "%s"', x, source, target)
 
@@ -861,18 +852,19 @@ class StageBase(TargetBase, ClearBase, GenBase):
                 if 'var_tmpfs_portage' not in self.settings:
                     continue
 
-                mount += ['-t', 'tmpfs', '-o',
-                          f"size={self.settings['var_tmpfs_portage']}G"]
+                fstype = 'tmpfs'
+                options = f"size={self.settings['var_tmpfs_portage']}G"
             elif source == 'tmpfs':
-                mount += ['-t', 'tmpfs']
+                fstype = 'tmpfs'
             elif source == 'shm':
-                mount += ['-t', 'tmpfs', '-o', 'noexec,nosuid,nodev']
+                fstype = 'tmpfs'
+                options = "noexec,nosuid,nodev"
             else:
                 source_path = Path(self.mount[x]['source'])
                 if source_path.suffix == '.sqfs':
-                    mount += ['-o', 'ro']
+                    options = "ro"
                 else:
-                    mount.append('--bind')
+                    options = "bind"
 
                     # We may need to create the source of the bind mount. 
E.g., in the
                     # case of an empty package cache we must create the 
directory that
@@ -881,38 +873,39 @@ class StageBase(TargetBase, ClearBase, GenBase):
 
             Path(target).mkdir(mode=0o755, parents=True, exist_ok=True)
 
-            cmd(mount + [source, target], env=self.env, fail_func=self.unbind)
+            try:
+                cxt = libmount.Context(source=source, target=target,
+                                       fstype=fstype, options=options)
+                cxt.mount()
+            except OSError as e:
+                self.unbind()
+                raise CatalystError(f"Counldn't mount: {source}, {e.strerror}")
 
     def unbind(self):
-        ouch = 0
-        mypath = self.settings["chroot_path"]
+        chroot_path = self.settings["chroot_path"]
+        umount_failed = False
 
         # Unmount in reverse order
-        for x in [x for x in reversed(self.mount) if self.mount[x]['enable']]:
-            target = normpath(mypath + self.mount[x]['target'])
-            if not os.path.exists(target):
-                log.notice('%s does not exist. Skipping', target)
+        for target in [Path(chroot_path + self.mount[x]['target'])
+                       for x in reversed(self.mount)
+                       if self.mount[x]['enable']]:
+            if not target.exists():
+                log.debug('%s does not exist. Skipping', target)
                 continue
 
             if not ismount(target):
-                log.notice('%s is not a mount point. Skipping', target)
+                log.debug('%s is not a mount point. Skipping', target)
                 continue
 
             try:
-                cmd(['umount', target], env=self.env)
-            except CatalystError:
-                log.warning('First attempt to unmount failed: %s', target)
-                log.warning('Killing any pids still running in the chroot')
-
-                self.kill_chroot_pids()
-
-                try:
-                    cmd(['umount', target], env=self.env)
-                except CatalystError:
-                    ouch = 1
-                    log.warning("Couldn't umount bind mount: %s", target)
-
-        if ouch:
+                cxt = libmount.Context(target=target)
+                cxt.umount(target)
+            except OSError as e:
+                log.warning("Couldn't umount: %s, %s", target,
+                            e.strerror)
+                umount_failed = True
+
+        if umount_failed:
             # if any bind mounts really failed, then we need to raise
             # this to potentially prevent an upcoming bash stage cleanup script
             # from wiping our bind mounts.
@@ -1345,9 +1338,6 @@ class StageBase(TargetBase, ClearBase, GenBase):
     def run(self):
         self.chroot_lock.write_lock()
 
-        # Kill any pids in the chroot
-        self.kill_chroot_pids()
-
         # Check for mounts right away and abort if we cannot unmount them
         self.mount_safety_check()
 

diff --git a/targets/support/kill-chroot-pids.sh 
b/targets/support/kill-chroot-pids.sh
deleted file mode 100755
index ea8ee402..00000000
--- a/targets/support/kill-chroot-pids.sh
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/bin/bash
-# Script to kill processes found running in the chroot.
-
-if [ "${clst_chroot_path}" == "/" ]
-then
-       echo "Aborting .... clst_chroot_path is set to /"
-       echo "This is very dangerous"
-       exit 1
-fi
-
-if [ "${clst_chroot_path}" == "" ]
-then
-       echo "Aborting .... clst_chroot_path is NOT set"
-       echo "This is very dangerous"
-       exit 1
-fi
-
-j=0
-declare -a pids
-# Get files and dirs in /proc
-for i in `ls /proc`
-do
-       # Test for directories
-       if [ -d /proc/$i ]
-       then
-       # Search for exe containing string inside ${clst_chroot_path}
-       ls -la --color=never /proc/$i 2>&1 |grep exe|grep ${clst_chroot_path} > 
/dev/null
-
-       # If found
-       if [ $? == 0 ]
-       then
-               # Assign the pid into the pids array
-               pids[$j]=$i
-               j=$(($j+1))
-       fi
-       fi
-done
-
-if [ ${j} -gt 0 ]
-then
-       echo
-       echo "Killing process(es)"
-       echo "pid: process name"
-       for pid in ${pids[@]}
-       do
-               P_NAME=$(ls -la --color=never /proc/${pid} 2>&1 |grep exe|grep 
${clst_chroot_path}|awk '{print $11}')
-               echo ${pid}: ${P_NAME}
-       done
-       echo
-       echo "Press Ctrl-C within 10 seconds to abort"
-
-       sleep 10
-
-       for pid in ${pids[@]}
-       do
-               kill -9 ${pid}
-       done
-
-       # Small sleep here to give the process(es) a chance to die before 
running unbind again.
-       sleep 5
-
-fi

Reply via email to