Automount USB-Laufwerke mit Systemd

27

Wir aktualisieren unsere Server von einer sehr veralteten Distribution auf ein modernes Debian Jessie-basiertes System, einschließlich lightdm / xfce und natürlich systemd (und udisks2). Ein Knackpunkt ist die automatische Bereitstellung von USB-Laufwerken. Früher haben wir dies mit einigen udev-Regeln erreicht. Die alten Regeln funktionieren fast immer noch - der Bereitstellungspunkt wird erstellt und das Laufwerk ist ordnungsgemäß bereitgestellt, aber nach einigen Sekunden führt systemd etwas aus, das die Bereitstellung unterbricht, sodass nachfolgende Zugriffsversuche zu Fehlern führen, dass der Transportendpunkt nicht verbunden ist.

Das manuelle Mounten des Laufwerks über die Befehlszeile funktioniert einwandfrei. Das gleiche gilt für den Dateimanager (thunar und thunar-volman, die wiederum udisks2 verwenden). Aber das sind keine praktikablen Optionen - diese Systeme laufen meistens ohne Kopf, so dass Thunar normalerweise nicht läuft. Wir müssen in der Lage sein, Festplattenlaufwerke für unbeaufsichtigte Cron-basierte Sicherungen anzuschließen.

Ich dachte, dass das Ändern des udev-Skripts, um einen getrennten Job zu erzeugen, der einige Sekunden vor dem Ausführen des Ladevorgangs wartet, den Trick machen könnte, aber systemd scheint alles zu tun, um dies zu verhindern - es wartet irgendwie immer noch, bis der getrennte Job beendet ist auch weiterhin.

Vielleicht ist es der richtige Ansatz, wenn das udev-Skript udisks2 kitzelt? Ich bin ratlos, daher wird jeder Rat sehr geschätzt.

Mike Blackwell
quelle
1
Nur tangential verwandt, aber ... Sie platzieren xfce auf einem Server?
Parthian Shot
Ah, ich habe den Begriff "Server" eher locker verwendet ... Die gesamte Benutzerinteraktion mit dem System erfolgt über eine Web-App, auf die normalerweise über einen Browser über das Netzwerk zugegriffen wird. Einige Kunden bevorzugen jedoch eine Nicht-Netzwerk-Lösung. Daher führen wir Chrome auf der Konsole in einer Art Kiosk-Modus aus. (Dies ist auch praktisch zum Debuggen von Netzwerkkonfigurationsproblemen. Sie können einen Monitor / eine Maus / eine Tastatur anschließen und auf grundlegende Diagnosetools in der Web-App zugreifen, ohne Linux-Anmeldeinformationen zu benötigen.) Es gibt wahrscheinlich eine leichtere Lösung als lightdm / xfce, aber dies war am einfachsten einzurichten ...
Mike Blackwell
Für alle, die systemd-udevd-Regeln möchten, die direkt ein Skript ausführen: Ich hatte dies; Es hat eine Weile funktioniert, aber irgendwann wurde das Skript nicht mehr ausgeführt, wenn udevd automatisch gestartet wurde. Stoppen Sie und starten Sie von der Kommandozeile, und es wäre in Ordnung. Darüber hinaus funktionierte es mit NTFS + FUSE nie gut, da udev feststellte, dass es einen lang andauernden untergeordneten Prozess (ntfs-3g) gab, und ihn nach 60s abbrach. Fazit: udev-Regeln, mit denen ein Skript direkt ausgeführt wird, sind Zeitverschwendung. Verwenden Sie stattdessen udev-Regeln und einen systemd-Dienst, wie in den Antworten angegeben. Dann müssen Sie sich auch nicht mit Namespaces (MountFlags = Slave) auseinandersetzen.
Mark
Ich hatte ein ähnliches Problem mit einem Skript, das gestartet wurde, weil udev keine Netzwerkverbindungen herstellen konnte. Die folgende Lösung für die Verwendung von systemd hat auch dafür funktioniert - danke!
Quentin Stafford-Fraser

Antworten:

28

Nach mehreren Fehlstarts habe ich das herausgefunden. Der Schlüssel besteht darin, einen System-Unit-Service zwischen udev und einem Mount-Skript hinzuzufügen.

(Ich verstehe, ich war nicht in der Lage, dies mit udisks2 (über so etwas wie udisksctl mount -b /dev/sdb1), das entweder direkt aus einer udev-Regel oder aus einer systemd-Unit-Datei aufgerufen wurde , zum Laufen zu bringen . Es scheint eine Race-Bedingung zu geben, und der Geräteknoten ist nicht ganz bereit , was zu Error looking up object for device /dev/sdb1Unglück führt, da udisks2 sich um die ganze Unordnung auf dem Mount Point kümmern könnte ...)

Das schwere Heben wird durch ein Shell-Skript erledigt, das das Erstellen und Entfernen von Bereitstellungspunkten sowie das Bereitstellen und Entfernen der Laufwerke übernimmt.

/usr/local/bin/usb-mount.sh

#!/bin/bash

# This script is called from our systemd unit file to mount or unmount
# a USB drive.

usage()
{
    echo "Usage: $0 {add|remove} device_name (e.g. sdb1)"
    exit 1
}

if [[ $# -ne 2 ]]; then
    usage
fi

ACTION=$1
DEVBASE=$2
DEVICE="/dev/${DEVBASE}"

# See if this drive is already mounted, and if so where
MOUNT_POINT=$(/bin/mount | /bin/grep ${DEVICE} | /usr/bin/awk '{ print $3 }')

do_mount()
{
    if [[ -n ${MOUNT_POINT} ]]; then
        echo "Warning: ${DEVICE} is already mounted at ${MOUNT_POINT}"
        exit 1
    fi

    # Get info for this drive: $ID_FS_LABEL, $ID_FS_UUID, and $ID_FS_TYPE
    eval $(/sbin/blkid -o udev ${DEVICE})

    # Figure out a mount point to use
    LABEL=${ID_FS_LABEL}
    if [[ -z "${LABEL}" ]]; then
        LABEL=${DEVBASE}
    elif /bin/grep -q " /media/${LABEL} " /etc/mtab; then
        # Already in use, make a unique one
        LABEL+="-${DEVBASE}"
    fi
    MOUNT_POINT="/media/${LABEL}"

    echo "Mount point: ${MOUNT_POINT}"

    /bin/mkdir -p ${MOUNT_POINT}

    # Global mount options
    OPTS="rw,relatime"

    # File system type specific mount options
    if [[ ${ID_FS_TYPE} == "vfat" ]]; then
        OPTS+=",users,gid=100,umask=000,shortname=mixed,utf8=1,flush"
    fi

    if ! /bin/mount -o ${OPTS} ${DEVICE} ${MOUNT_POINT}; then
        echo "Error mounting ${DEVICE} (status = $?)"
        /bin/rmdir ${MOUNT_POINT}
        exit 1
    fi

    echo "**** Mounted ${DEVICE} at ${MOUNT_POINT} ****"
}

do_unmount()
{
    if [[ -z ${MOUNT_POINT} ]]; then
        echo "Warning: ${DEVICE} is not mounted"
    else
        /bin/umount -l ${DEVICE}
        echo "**** Unmounted ${DEVICE}"
    fi

    # Delete all empty dirs in /media that aren't being used as mount
    # points. This is kind of overkill, but if the drive was unmounted
    # prior to removal we no longer know its mount point, and we don't
    # want to leave it orphaned...
    for f in /media/* ; do
        if [[ -n $(/usr/bin/find "$f" -maxdepth 0 -type d -empty) ]]; then
            if ! /bin/grep -q " $f " /etc/mtab; then
                echo "**** Removing mount point $f"
                /bin/rmdir "$f"
            fi
        fi
    done
}

case "${ACTION}" in
    add)
        do_mount
        ;;
    remove)
        do_unmount
        ;;
    *)
        usage
        ;;
esac

Das Skript wird wiederum von einer systemd-Unit-Datei aufgerufen. Wir verwenden die Dateinamensyntax "@", um den Gerätenamen als Argument zu übergeben.

/etc/systemd/system/[email protected]

[Unit]
Description=Mount USB Drive on %i
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/local/bin/usb-mount.sh add %i
ExecStop=/usr/local/bin/usb-mount.sh remove %i

Schließlich starten und stoppen einige udev-Regeln den System-Unit-Service beim Hotplug / Unplug:

/etc/udev/rules.d/99-local.rules

KERNEL=="sd[a-z][0-9]", SUBSYSTEMS=="usb", ACTION=="add", RUN+="/bin/systemctl start usb-mount@%k.service"

KERNEL=="sd[a-z][0-9]", SUBSYSTEMS=="usb", ACTION=="remove", RUN+="/bin/systemctl stop usb-mount@%k.service"

Dies scheint den Trick zu tun! Ein paar nützliche Befehle zum Debuggen von Dingen wie folgt:

  • udevadm control -l debugAktiviert die ausführliche Protokollierung, /var/log/syslogdamit Sie sehen können, was gerade passiert.
  • udevadm control --reload-rules nachdem Sie Dateien im Verzeichnis rules.d geändert haben (möglicherweise nicht erforderlich, kann aber nicht schaden ...).
  • systemctl daemon-reload Nachdem Sie die System-Unit-Dateien geändert haben.
Mike Blackwell
quelle
4
Wow. Das ist fantastisch. Ich wünschte, ich könnte mehrere Upvotes geben! Das einzige, was ich ändern musste, war, dass auf meinem System blkidanscheinend kein Extrakt vorhanden ist ID_FS_LABEL, also habe ich stattdessen nur das verwendet, DEVBASEanstatt das LABELzu konstruieren MOUNT_POINT.
Travis Griggs
Kann dieses Setup für ATA / SCSI-Geräte geändert werden? Siehe: serverfault.com/q/825779/297059
user339676
@ Travis - Sie können udevadmanstelle von verwenden blkid. Es enthält viel mehr Details sowie zusätzliche Informationen. (zB udevadm info --query=property --name=sda1)
user339676
Dies funktioniert nicht gut beim Booten, wenn ein USB-Gerät bereits angeschlossen ist. Irgendwelche Ideen?
Michal Artazov
Wenn keine Null-Globs festgelegt sind, kann die Bereinigung beim Aufheben der Bereitstellung einen Fehler wie den folgenden erzeugen /usr/bin/find: '/media/*': No such file or directory. Bei der Bereinigung kann wie if [ "$f" != "/media/*" ]; thenvor dem Ausführen eine zusätzliche Überprüfung durchgeführt werden find.
Pro Backup
12

Es gibt eine neue, prägnante systemdAuto-Mount-Option, mit fstabder Sie alle standardisierten Mount-Berechtigungsoptionen verwenden können. Sie sieht folgendermaßen aus:

  x-systemd.automount

Ein Beispiel in einer fstabZeile:

  /dev/sdd1   /mnt/hitachi-one     auto     noauto,x-systemd.automount     0 2

Die noautoOption bedeutet, dass nicht versucht wird, beim Booten gemountet zu werden, wie bei älterer Software autofs.

Nachdem Sie eine neue x-systemd.automountZeile hinzugefügt haben , müssen fstabSie Folgendes ausführen:

  sudo systemctl daemon-reload

und dann beide oder eine der folgenden:

  sudo systemctl restart remote-fs.target
  sudo systemctl restart local-fs.target

Für weitere Informationen dazu:

https://wiki.archlinux.org/index.php/Fstab#Automount_with_systemd

unendlich-etcetera
quelle
sudo systemctl restart local-fs.targethat den Trick für mich getan
Philippe Gachoud
2

Ich habe das Skript von @MikeBlackwell geändert zu:

  • Gerätenamen erkennen, die mehrere Zeichen umfassen, nicht nur, /dev/sd[a-z]sondern /dev/sd[a-z]*; Dies ist häufig bei Servern mit einer größeren Anzahl von Spindeln der Fall.
  • Verfolgen Sie die Liste der automatisch aktivierten Laufwerke unter /var/log/usb-mount.track
  • Protokollieren Sie die Aktionen /var/log/messagesmit dem Tag usb-mount.sh
  • Präfix Gerätename mit dem Geräteetikett für den Mount - Punkt nicht mit Antrieben in zu Problemen führen , die ein Etikett nicht vergeben haben (leer?): /media/sdd2_usbtest,/media/sdd2_
  • Wrapper-Skripte enthalten, um die Dateien entsprechend zu platzieren und bei Bedarf rückgängig zu machen

Da @MikeBlackwell bereits die meiste Zeit mit dem Heben von Lasten verbracht hat, habe ich beschlossen, es nicht umzuschreiben. habe gerade die notwendigen Änderungen vorgenommen. Ich habe seine Arbeit bestätigt, indem ich seinen Namen und die URI der ursprünglichen Antwort gesehen habe.

Finden Sie es unter https://github.com/raamsri/automount-usb

six-k
quelle
2

Mit pmount , systemd und Mike Blackwells Ansatz können Sie das Ganze vereinfachen:

/etc/systemd/system/[email protected]

[Unit]
Description=Mount USB Drive on %i
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/bin/pmount --umask 000 /dev/%i /media/%i
ExecStop=/usr/bin/pumount /dev/%i

/etc/udev/rules.d/99-usb-mount.rules

ACTION=="add",KERNEL=="sd[a-z][0-9]*",SUBSYSTEMS=="usb",RUN+="/bin/systemctl start usb-mount@%k.service"
ACTION=="remove",KERNEL=="sd[a-z][0-9]*",SUBSYSTEMS=="usb",RUN+="/bin/systemctl stop usb-mount@%k.service"

HTH und danke Mike.

Eric V.
quelle
0

Ich würde Warren Youngs Antwort folgen, an der ich einige Änderungen vorgenommen habe

Ich fügte einen Platzschutz hinzu, da er Fehler aus der Umgebungsbewertung für das Laufwerk ergab.

Ich habe einen Abschnitt hinzugefügt, um eine USB-Festplatte zu chmodieren, damit alle Benutzer uneingeschränkten Zugriff auf Nicht-NTFS- oder VFAT-Festplatten haben.

/usr/local/bin/usb-mount.sh

#!/bin/bash

# This script is called from our systemd unit file to mount or unmount
# a USB drive.

usage()
{
    echo "Usage: $0 {add|remove} device_name (e.g. sdb1)"
    exit 1
}

if [[ $# -ne 2 ]]; then
    usage
fi

ACTION="$1"
DEVBASE="$2"
DEVICE="/dev/${DEVBASE}"

# See if this drive is already mounted, and if so where
MOUNT_POINT=$(/bin/mount | /bin/grep ${DEVICE} | /usr/bin/awk '{ print $3 }')

do_mount()
{
    if [[ -n "${MOUNT_POINT}" ]]; then
        echo "Warning: ${DEVICE} is already mounted at ${MOUNT_POINT}"
        exit 1
    fi

    # Get info for this drive: $ID_FS_LABEL, $ID_FS_UUID, and $ID_FS_TYPE
    # added some sed's to avoid space issues
    eval $(/sbin/blkid -o udev ${DEVICE}|sed 's/=/="/'|sed 's/$/"/')

    # Figure out a mount point to use
    LABEL="${ID_FS_LABEL}"
    if [[ -z "${LABEL}" ]]; then
        LABEL="${DEVBASE}"
    elif /bin/grep -q " /media/${LABEL} " /etc/mtab; then
        # Already in use, make a unique one
        LABEL+="-${DEVBASE}"
    fi
    MOUNT_POINT="/media/${LABEL}"

    echo "Mount point: ${MOUNT_POINT}"

    /bin/mkdir -p "${MOUNT_POINT}"

    # Global mount options
    OPTS="rw,relatime"
    #added a chmod checker for file systems that don't 
    #understand allow all to read write
    CHMOD=no
    # File system type specific mount options
    if [[ ${ID_FS_TYPE} == "vfat" ]]; then
        OPTS+=",users,gid=100,umask=000,shortname=mixed,utf8=1,flush"
    #added options I wanted on ntfs
    elif [[ ${ID_FS_TYPE} == "ntfs" ]]; then
        OPTS+=",user,users,umask=000,allow_other"
    else
       CHMOD=yes
    fi

    if ! /bin/mount -o "${OPTS}" ${DEVICE} "${MOUNT_POINT}"; then
        echo "Error mounting ${DEVICE} (status = $?)"
        /bin/rmdir "${MOUNT_POINT}"
        exit 1
    fi


    echo "**** Mounted ${DEVICE} at ${MOUNT_POINT} ****"
    if [ "${CHMOD}" = "yes" ];then
        /usr/bin/find "${MOUNT_POINT}" -type f -exec chmod 0666 {} \;
        /usr/bin/find "${MOUNT_POINT}" -type d -exec chmod 0777 {} \;
    fi
}

do_unmount()
{
    if [[ -z ${MOUNT_POINT} ]]; then
        echo "Warning: ${DEVICE} is not mounted"
    else
        /bin/umount -l ${DEVICE}
        echo "**** Unmounted ${DEVICE}"
    fi

    # Delete all empty dirs in /media that aren't being used as mount
    # points. This is kind of overkill, but if the drive was unmounted
    # prior to removal we no longer know its mount point, and we don't
    # want to leave it orphaned...
    for f in /media/* ; do
        if [[ -n $(/usr/bin/find "$f" -maxdepth 0 -type d -empty) ]]; then
            if ! /bin/grep -q " $f " /etc/mtab; then
                echo "**** Removing mount point $f"
                /bin/rmdir "$f"
            fi
        fi
    done
}

case "${ACTION}" in
    add)
        do_mount
        ;;
    remove)
        do_unmount
        ;;
    *)
        usage
        ;;
 esac
Pinguineff
quelle
Vielleicht möchten Sie in wenigen Worten beschreiben, was sich zwischen der ursprünglichen und Ihrer Antwort unterscheidet, um sie nützlicher zu machen. PS: Es gab keine Antwort von Warren Young; Vielleicht meintest du damit Mike Blackwells Antwort, die bearbeitet wurde?
Amir