How do I stop all processes in a chroot?
Solution 1:
I don't trust anything but the kernel to keep a sane state here, so I don't (ab)use init to get this job done, nor do I count on myself actually knowing what is or isn't mounted (some packages can mount extra filesystems, like binfmt_misc). So, for process slaughter, I use:
PREFIX=/mnt/chroot-0
FOUND=0
for ROOT in /proc/*/root; do
LINK=$(readlink $ROOT)
if [ "x$LINK" != "x" ]; then
if [ "x${LINK:0:${#PREFIX}}" = "x$PREFIX" ]; then
# this process is in the chroot...
PID=$(basename $(dirname "$ROOT"))
kill -9 "$PID"
FOUND=1
fi
fi
done
if [ "x$FOUND" = "x1" ]; then
# repeat the above, the script I'm cargo-culting this from just re-execs itself
fi
And for umounting chroots, I use:
PREFIX=/mnt/chroot-0
COUNT=0
while grep -q "$PREFIX" /proc/mounts; do
COUNT=$(($COUNT+1))
if [ $COUNT -ge 20 ]; then
echo "failed to umount $PREFIX"
if [ -x /usr/bin/lsof ]; then
/usr/bin/lsof "$PREFIX"
fi
exit 1
fi
grep "$PREFIX" /proc/mounts | \
cut -d\ -f2 | LANG=C sort -r | xargs -r -n 1 umount || sleep 1
done
As an addendum, I'd point out that approaching this as an init problem is probably the wrong way to look at it, unless you actually have an init in the chroot and a separate process space (ie: in the case of LXC containers). With a single init (outside the chroot), and a shared process space, this is no longer "init's problem", but rather just up to you to find the processes that happen to have the offending path, hence the above proc walk.
It's not clear from your initial post if these are fully-bootable systems that you're just upgrading externally (which is how I read it), or if they're chroots that you use for things like package builds. If it's the latter, you might also want a policy-rc.d in place (like the one dropped in by mk-sbuild) that just forbids init jobs starting in the first place. Obviously, that's not a sane solution if these are meant to be bootable systems as well.