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