web-dev-qa-db-fra.com

Lecteurs USB à montage automatique avec systemd

Nous mettons à jour nos serveurs d'une distribution très obsolète à un système moderne basé sur Debian Jessie, y compris lightdm/xfce, et bien sûr systemd (et udisks2). Un point de blocage est le montage automatique de clés USB. Nous avions l'habitude d'accomplir cela avec quelques règles udev. Les anciennes règles fonctionnent presque toujours - le point de montage est créé et le lecteur est monté correctement, mais après quelques secondes, systemd fait quelque chose qui rompt le montage, de sorte que les tentatives d'accès suivantes entraînent des erreurs "Le point d'extrémité de transport n'est pas connecté".

Le montage manuel du lecteur via la ligne de commande fonctionne correctement. Il en va de même pour un gestionnaire de fichiers (thunar et thunar-volman, qui à son tour utilise udisks2). Mais ce ne sont pas des options viables - ces systèmes fonctionnent généralement sans tête, donc thunar ne fonctionne pas normalement. Nous devons être capables de brancher des unités de disque pour des sauvegardes cron sans assistance.

Je pensais que la modification du script udev pour générer un travail détaché qui attend quelques secondes avant d'effectuer le montage pourrait faire l'affaire, mais systemd semble se mettre en quatre pour éviter cela - il attend toujours que le travail détaché se termine avant continue.

Peut-être que le script udev chatouille udisks2 est en quelque sorte la bonne approche? Je suis perdu, donc tout conseil est grandement apprécié.

28
Mike Blackwell

Après plusieurs faux départs, j'ai compris cela. La clé est d'ajouter un service d'unité systemd entre udev et un script de montage.

(Pour mémoire, je n'ai pas pu faire fonctionner cela en utilisant udisks2 (via quelque chose comme udisksctl mount -b /dev/sdb1) appelé soit directement à partir d'une règle udev, soit à partir d'un fichier d'unité systemd. Il semble y avoir une condition de concurrence critique et le nœud de périphérique n'est pas tout à fait prêt, ce qui entraîne Error looking up object for device /dev/sdb1. Malheureusement, car udisks2 pourrait s'occuper de tout le désordre du point de montage ...)

Le gros du travail est effectué par un script Shell, qui prend en charge la création et la suppression de points de montage, ainsi que le montage et le démontage des disques.

/ 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

Le script, à son tour, est appelé par un fichier d'unité systemd. Nous utilisons la syntaxe de nom de fichier "@" afin de pouvoir passer le nom du périphérique en argument.

/ etc/systemd/system/usb-mount @ .service

[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

Enfin, certaines règles udev démarrent et arrêtent le service d'unité systemd sur 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"

Cela semble faire l'affaire! Quelques commandes utiles pour déboguer des trucs comme celui-ci:

  • udevadm control -l debug active la journalisation détaillée sur /var/log/syslog pour voir ce qui se passe.
  • udevadm control --reload-rules après avoir modifié des fichiers dans le répertoire rules.d (peut ne pas être nécessaire, mais ne peut pas faire de mal ...).
  • systemctl daemon-reload après avoir modifié les fichiers d'unité systemd.
30
Mike Blackwell

il y a une nouvelle option de montage automatique systemd succincte qui peut être utilisée avec fstab qui vous permet d'utiliser toutes les options de permission de montage normalisées, et cela ressemble à ceci:

  x-systemd.automount

un exemple de celui-ci dans une ligne fstab:

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

l'option noauto signifie qu'elle ne tentera pas d'être montée au démarrage, comme avec les anciens logiciels autofs.

après avoir ajouté un nouveau x-systemd.automount ligne vers fstab vous devez ensuite exécuter:

  Sudo systemctl daemon-reload

puis les deux ou l'un des éléments suivants:

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

pour plus d'informations à ce sujet:

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

13
infinite-etcetera

J'irais avec la réponse de Warren Young J'ai quelques modifications que j'ai apportées à

J'ai ajouté une protection d'espace car cela donnait des erreurs de l'évaluation de l'environnement pour le lecteur.

J'ai ajouté une section pour chmoder un disque USB afin que tous les utilisateurs aient un accès complet aux disques non ntfs ou 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
2
penguinjeff

En utilisant pmount, systemd et l'approche de Mike Blackwell, vous pouvez simplifier le tout:

/ etc/systemd/system/usb-mount @ .service

[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 et merci Mike.

2
Eric V.

J'ai modifié le script de @MikeBlackwell en:

  • reconnaître les noms de périphériques qui s'étendent sur plusieurs caractères, pas seulement /dev/sd[a-z] mais /dev/sd[a-z]*; souvent le cas avec des serveurs qui ont un plus grand nombre de broches.
  • suivre la liste des lecteurs montés automatiquement sur /var/log/usb-mount.track
  • consigner les actions dans /var/log/messages avec tag usb-mount.sh
  • préfixe le nom du périphérique avec l'étiquette du périphérique pour que le point de montage ne s'exécute pas en cas de problème avec les lecteurs auxquels aucune étiquette n'a été attribuée (vide?): /media/sdd2_usbtest, /media/sdd2_
  • scripts d'encapsulation inclus pour placer les fichiers de manière appropriée et annuler si nécessaire

Étant donné que @MikeBlackwell a déjà effectué la plupart des travaux lourds, j'ai choisi de ne pas le réécrire; vient de faire les changements nécessaires. J'ai reconnu son travail en voyant son nom et l'URI de la réponse originale.

Trouvez-le sur https://github.com/raamsri/automount-usb

2
six-k