Automount napędy USB z systemd

27

Aktualizujemy nasze serwery z bardzo nieaktualnej dystrybucji do nowoczesnego systemu opartego na Debian Jessie, w tym lightdm / xfce i oczywiście systemd (i udisks2). Jednym z problemów jest automatyczne podłączanie napędów USB. Robiliśmy to z pewnymi zasadami udev. Stare reguły prawie nadal działają - punkt montowania jest tworzony, a dysk jest montowany w porządku, ale po kilku sekundach systemd robi coś, co przerywa montowanie, więc kolejne próby dostępu powodują błędy „Punkt końcowy transportu nie jest podłączony”.

Ręczny montaż napędu za pomocą wiersza polecenia działa dobrze. Tak samo pozwala menedżerowi plików (thunar i thunar-volman, który z kolei używa udisks2). Ale to nie są realne opcje - systemy te w większości działają bez głowy, więc Thunar zwykle nie działa. Musimy być w stanie podłączyć dyski w celu wykonywania nienadzorowanych kopii zapasowych opartych na cronie.

Pomyślałem, że modyfikacja skryptu udev w celu odradzania odłączonego zadania, które czeka kilka sekund przed wykonaniem montowania, może załatwić sprawę, ale systemd wydaje się, że robi to, aby temu zapobiec - nadal jakoś czeka na zakończenie odłączonego zadania przed zakończeniem kontynuując.

Być może posiadanie skryptu udev łaskotanie udisks2 jest jakoś właściwym podejściem? Jestem zagubiony, więc wszelkie rady są mile widziane.

Mike Blackwell
źródło
1
Tylko stycznie spokrewnione, ale ... Umieszczasz xfce na serwerze?
Parthian Shot
Ach, dość luźno użyłem terminu „serwer” ... Cała interakcja użytkownika z systemem odbywa się za pośrednictwem aplikacji internetowej, zwykle dostępnej za pośrednictwem przeglądarki przez sieć. Ale niektórzy klienci wolą rozwiązanie niesieciowe, dlatego Chrome uruchamiamy na konsoli w trybie kiosku. (Przydaje się to również przy debugowaniu problemów z konfiguracją sieci, możesz podłączyć monitor / mysz / klawiaturę i uzyskać dostęp do podstawowych narzędzi diagnostycznych w aplikacji internetowej bez konieczności logowania się do systemu Linux). Prawdopodobnie istnieje rozwiązanie lżejsze niż lightdm / xfce, ale najłatwiej było je skonfigurować ...
Mike Blackwell
Dla każdego, kto chce reguł systemd-udevd bezpośrednio uruchamiających skrypt: miałem to; działało przez jakiś czas, ale w pewnym momencie przestało działać skrypt, jeśli udevd został uruchomiony automatycznie. Zatrzymaj się i uruchom ponownie z wiersza poleceń, i będzie dobrze. Co więcej, nigdy nie działał dobrze z NTFS + FUSE, ponieważ udev wykrył, że ma długo działający proces potomny (ntfs-3g) i zabił go po 60s. Konkluzja: reguły udev bezpośrednio uruchamiające skrypt to strata czasu. Zamiast tego stosuj reguły udev i usługę systemową, jak zaznaczono w odpowiedziach. Wtedy nie musisz również zajmować się przestrzeniami nazw (MountFlags = slave).
Mark
Miałem podobny problem ze skryptem uruchomionym przez udev, który nie był w stanie nawiązywać połączeń sieciowych. Poniższe rozwiązanie użycia systemd również działało w tym przypadku - dzięki!
Quentin Stafford-Fraser

Odpowiedzi:

28

Po kilku fałszywych startach wymyśliłem to. Kluczem jest dodanie usystematyzowanej usługi jednostki między udev a skryptem instalacyjnym.

(Dla przypomnienia, nie byłem w stanie uzyskać tego działania przy użyciu udisks2 (przez coś podobnego udisksctl mount -b /dev/sdb1) wywoływanego albo bezpośrednio z reguły udev, albo z pliku jednostki systemowej. Wygląda na to, że istnieje wyścig, a węzeł urządzenia nie jest jeszcze gotowy , co powoduje Error looking up object for device /dev/sdb1. Niefortunne, ponieważ udisks2 może zająć się całym bałaganem punktu montowania ...)

Ciężkie podnoszenie odbywa się za pomocą skryptu powłoki, który dba o tworzenie i usuwanie punktów montowania oraz montowanie i odmontowywanie dysków.

/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

Skrypt jest z kolei wywoływany przez systemowy plik jednostki. Używamy składni nazwy pliku „@”, aby przekazać nazwę urządzenia jako argument.

/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

Wreszcie, niektóre reguły udev uruchamiają i zatrzymują usystematyzowaną obsługę jednostek podczas 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"

To wydaje się załatwić sprawę! Kilka przydatnych poleceń do debugowania takich rzeczy:

  • udevadm control -l debugwłącza pełne rejestrowanie /var/log/syslog, abyś mógł zobaczyć, co się dzieje.
  • udevadm control --reload-rules po zmodyfikowaniu plików w rules.d katalog (może nie być konieczne, ale nie może zranić ...).
  • systemctl daemon-reload po zmodyfikowaniu plików jednostek systemowych.
Mike Blackwell
źródło
4
Łał. To jest niesamowite. Chciałbym dać wiele pozytywnych opinii! Jedyną rzeczą, którą musiałem zmodyfikować było to, że w moim systemie, blkidnie wydaje się wyodrębnić ID_FS_LABEL, więc po prostu używany DEVBASEzamiast LABELdo konstrukcji MOUNT_POINTzamiast.
Travis Griggs,
Czy tę konfigurację można zmodyfikować, aby działała z urządzeniami ATA / SCSI? Zobacz: serverfault.com/q/825779/297059
user339676
@Travis - Możesz użyć udevadmzamiast blkid. Daje znacznie więcej szczegółów, a także dodatkowe informacje. (np. udevadm info --query=property --name=sda1)
user339676
to nie działa dobrze podczas rozruchu, jeśli urządzenie USB jest już podłączone. Jakieś pomysły?
Michał Artazow
Jeśli nullglob nie są ustawione, po odmontowaniu czyszczenie może wygenerować błąd podobny do tego /usr/bin/find: '/media/*': No such file or directory. Oczyszczanie może użyć dodatkowego sprawdzenia, tak jak if [ "$f" != "/media/*" ]; thenprzed uruchomieniem find.
Pro Backup
12

dostępna jest nowa, zwięzła systemdopcja automatycznego montowania, z którą można korzystać, fstabktóra pozwala korzystać ze wszystkich standardowych opcji uprawnień montowania, i wygląda to tak:

  x-systemd.automount

przykład tego w fstabwierszu:

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

noautoopcja to znaczy nie będzie próbował zostać zamontowany w bagażniku, jak ze starszym oprogramowaniem autofs.

po dodaniu nowego x-systemd.automountwiersza fstabmusisz uruchomić:

  sudo systemctl daemon-reload

a następnie oba lub jedno z poniższych:

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

więcej informacji na ten temat:

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

infinite-etcetera
źródło
sudo systemctl restart local-fs.targetwykonał dla mnie lewę
Philippe Gachoud
2

Zmodyfikowałem skrypt z @MikeBlackwell do:

  • rozpoznać nazwy urządzeń obejmujące wiele znaków, nie tylko, /dev/sd[a-z]ale /dev/sd[a-z]*; często ma to miejsce w przypadku serwerów, które mają większą liczbę wrzecion.
  • śledź listę napędów automatycznych w /var/log/usb-mount.track
  • zaloguj akcje do za /var/log/messagespomocą tagu usb-mount.sh
  • Nazwa urządzenia prefiks z etykietą urządzenia do punktu montowania, aby nie uruchomić do problemów z dysków, które nie zostały przypisane etykiety (pusty): /media/sdd2_usbtest,/media/sdd2_
  • zawiera skrypty opakowujące, aby odpowiednio umieszczać pliki i cofać je w razie potrzeby

Ponieważ @MikeBlackwell wykonał już większość ciężkich operacji podnoszenia, postanowiłem go nie przepisywać; właśnie dokonał niezbędnych zmian. Potwierdziłem jego pracę, widząc jego imię i URI oryginalnej odpowiedzi.

Znajdź go na https://github.com/raamsri/automount-usb

sześć-k
źródło
2

Stosując podejście pmount , systemd i Mike'a Blackwella, możesz uprościć całość:

/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 i dziękuję Mike.

Eric V.
źródło
0

Wybrałbym odpowiedź Warrena Younga, w której wprowadziłem kilka zmian

Dodałem trochę ochrony przestrzeni, ponieważ dawała błędy z ewaluacji środowiska dla dysku.

Dodałem sekcję do chmod dysku USB, aby wszyscy użytkownicy mieli pełny dostęp do dysków innych niż NTFS lub VFAT.

/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
pingwin
źródło
Możesz w kilku słowach opisać różnicę między oryginalną odpowiedzią a twoją, aby była bardziej użyteczna. PS: Warren Young nie odpowiedział; może miałeś na myśli odpowiedź Mike'a Blackwella, która została zredagowana?
Amir