it-swarm.com.de

Wie stoppe ich alle Prozesse in einer Chroot?

Ich habe eine Reihe von LVM-Partitionen, die jeweils eine Ubuntu-Installation enthalten. Gelegentlich möchte ich einen apt-get dist-upgrade ausführen, um eine Installation auf die neuesten Pakete zu aktualisieren. Ich mache das mit chroot - der Prozess ist normalerweise ungefähr so:

$ Sudo mount /dev/local/chroot-0 /mnt/chroot-0
$ Sudo chroot /mnt/chroot-0 sh -c 'apt-get update && apt-get dist-upgrade'
$ Sudo umount /mnt/chroot-0

[nicht gezeigt: Ich mounte und entmountete /mnt/chroot-0/{dev,sys,proc} auch als Bindmounts zu den realen /dev, /sys und /proc, wie das Dist-Upgrade dies zu erwarten scheint vorhanden ]

Nach dem Upgrade auf Precise funktioniert dieser Prozess jedoch nicht mehr - die endgültige Umount-Datei schlägt fehl, da noch Dateien im /mnt/chroot-0 -Dateisystem geöffnet sind. lsof bestätigt, dass sich Prozesse mit offenen Dateien in der Chroot befinden. Diese Prozesse wurden während des dist-Upgrades gestartet. Dies liegt vermutlich daran, dass bestimmte Dienste in der Chroot neu gestartet werden müssen (z. B. durch service postgresql restart), nachdem das Paket aktualisiert wurde.

Ich denke also, ich muss upstart mitteilen, dass alle Dienste, die in dieser Chroot ausgeführt werden, gestoppt werden sollen. Gibt es eine Möglichkeit, dies zuverlässig zu tun?

Ich habe es versucht:

cat <<EOF | Sudo chroot /mnt/chroot-0 /bin/sh
# stop 'initctl' services 
initctl list | awk '/start\/running/ {print \$1}' | xargs -n1 -r initctl stop
EOF

Wobei initctl list anscheinend das Richtige tut und nur Prozesse auflistet, die in diesem bestimmten Stammverzeichnis gestartet wurden. Ich habe versucht, dies auch hinzuzufügen, wie von Tuminoid vorgeschlagen:

cat <<EOF | Sudo chroot /mnt/chroot-0 /bin/sh
# stop 'service' services
service --status-all 2>/dev/null |
    awk '/^ \[ \+ \]/ { print \$4}' |
    while read s; do service \$s stop; done
EOF

Diese scheinen jedoch nicht alles zu erfassen; Prozesse, die auf PID 1 zurückgesetzt wurden, werden nicht gestoppt. Ich habe auch versucht:

Sudo chroot /mnt/chroot-0 telinit 0

Aber in diesem Fall unterscheidet init doesnt zwischen den einzelnen Roots und fährt die gesamte Maschine herunter.

Gibt es eine Möglichkeit, init anzuweisen, alle Prozesse in einer bestimmten Chroot zu stoppen, damit ich das Dateisystem sicher trennen kann? Verfügt upstart über eine Möglichkeit, alle untergeordneten Prozesse (wie beim regulären Herunterfahren) innerhalb einer Chroot SIGTERM/SIGKILL zuzuweisen?

16
Jeremy Kerr

Ich traue nichts außer dem Kernel, um hier einen vernünftigen Zustand beizubehalten, also verwende ich init nicht (ab), um diesen Job zu erledigen, noch zähle ich darauf, dass ich wirklich weiß, was gemountet ist oder nicht (einige Pakete) kann zusätzliche Dateisysteme wie binfmt_misc mounten). Also verwende ich zum Prozessschlachten:

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

Und zum Ummounten von Chroots verwende ich:

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

Als Ergänzung möchte ich darauf hinweisen, dass es wahrscheinlich falsch ist, dies als Init-Problem zu betrachten, es sei denn, Sie haben tatsächlich ein Init in der Chroot und einen separaten Prozessraum (dh im Fall von LXC-Containern). . Mit einem einzelnen Init (außerhalb der Chroot) und einem gemeinsam genutzten Prozessbereich ist dies nicht mehr das Problem von "init", sondern es liegt ganz bei Ihnen, die Prozesse zu finden, die den fehlerhaften Pfad haben, daher der obige Prozesspfad.

Aus Ihrem ersten Beitrag geht nicht hervor, ob es sich um vollständig bootfähige Systeme handelt, die Sie nur extern aktualisieren (so lese ich es), oder ob es sich um Chroots handelt, die Sie für Dinge wie die Erstellung von Paketen verwenden. In letzterem Fall möchten Sie möglicherweise auch eine Policy-rc.d (wie die, die von mk-sbuild abgelegt wurde), die nur das Starten von Jobs von vornherein verbietet. Das ist natürlich keine vernünftige Lösung, wenn dies auch bootfähige Systeme sein sollen.

16
infinity

Sie haben das Problem bereits selbst identifiziert: Einige Dinge führen service ... während des dist-Upgrades aus und service ist nicht Teil von Upstart, sondern Teil von sysvinit. Fügen Sie service --status-all eine ähnliche awk-Magie hinzu, um sysvinit-Dienste zu stoppen, wie Sie sie für Upstart-Dienste verwendet haben.

0
Tuminoid

Ich weiß, dass diese Frage ziemlich alt ist, aber ich denke, sie ist heute genauso aktuell wie 2012, und hoffentlich findet jemand diesen Code nützlich. Ich schrieb den Code für etwas, was ich tat, dachte aber, ich würde ihn teilen.

Mein Code ist anders, aber die Ideen sind @infinity sehr ähnlich (der einzige Grund, den ich jetzt über/proc/*/root weiß, ist seine Antwort - danke @infinity!). Ich habe auch einige coole zusätzliche Funktionen hinzugefügt

#Kills any PID passed to it
#At first it tries nicely with SIGTERM
#After a timeout, it uses SIGKILL
KILL_PID()
{
        PROC_TO_KILL=$1

        #Make sure we have an arg to work with
        if [[ "$PROC_TO_KILL" == "" ]]
        then
                echo "KILL_PID: \$1 cannot be empty"
                return 1
        fi

        #Try to kill it nicely
        kill -0 $PROC_TO_KILL &>/dev/null && kill -15 $PROC_TO_KILL

        #Check every second for 5 seconds to see if $PROC_TO_KILL is dead
        WAIT_TIME=5

        #Do a quick check to see if it's still running
        #It usually takes a second, so this often doesn't help
        kill -0 $PROC_TO_KILL &>/dev/null &&
        for SEC in $(seq 1 $WAIT_TIME)
        do
                sleep 1

                if [[ "$SEC" != $WAIT_TIME ]]
                then
                        #If it's dead, exit
                        kill -0 $PROC_TO_KILL &>/dev/null || break
                else
                        #If time's up, kill it
                        kill -0 $PROC_TO_KILL &>/dev/null && kill -9 $PROC_TO_KILL
                fi
        done
}

Jetzt würden Sie zwei Dinge tun, um sicherzustellen, dass die Chroot entfernt werden kann:

Beenden Sie alle Prozesse, die möglicherweise in der Chroot ausgeführt werden:

CHROOT=/mnt/chroot/

#Find processes who's root folder is actually the chroot
for ROOT in $(find /proc/*/root)
do
        #Check where the symlink is pointing to
        LINK=$(readlink -f $ROOT)

        #If it's pointing to the $CHROOT you set above, kill the process
        if echo $LINK | grep -q ${CHROOT%/}
        then
                PID=$(basename $(dirname "$ROOT"))
                KILL_PID $PID
        fi
done

Beenden Sie alle Prozesse, die möglicherweise außerhalb der Chroot ausgeführt werden, diese jedoch beeinträchtigen (Beispiel: Wenn Ihre Chroot/mnt/chroot ist und dd in/mnt/chroot/testfile schreibt, schlägt das Aufheben der Bereitstellung von/mnt/chroot fehl).

CHROOT=/mnt/chroot/

#Get a list of PIDs that are using $CHROOT for anything
PID_LIST=$(Sudo lsof +D $CHROOT 2>/dev/null | tail -n+2 | tr -s ' ' | cut -d ' ' -f 2 | sort -nu)

#Kill all PIDs holding up unmounting $CHROOT
for PID in $PID_LIST
do
        KILL_PID $PID
done

Hinweis: Führen Sie den gesamten Code als root aus

Ersetzen Sie für eine weniger komplexe Version KILL_PID entweder durch kill -SIGTERM oder kill -SIGKILL.

0
Tal

jchroot : eine Chroot mit mehr Isolation.

Nachdem Ihr Befehl ausgeführt wurde, wird jeder Prozess, der durch die Ausführung dieses Befehls gestartet wurde, abgebrochen, alle IPC werden freigegeben, alle Bereitstellungspunkte werden deaktiviert. Alles sauber!

schroot kann das noch nicht, aber das ist geplant

Ich habe es erfolgreich in OpenVZ VPS getestet, das Docker oder LXC nicht verwenden kann.

Bitte lesen Sie den Blog des Autors für die Details:

https://vincent.bernat.im/en/blog/2011-jchroot-isolation.html

0
Like