Lecteurs USB à montage automatique avec systemd

27

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é.

Mike Blackwell
la source
1
Seulement lié tangentiellement, mais ... Vous mettez xfce sur un serveur?
Parthian Shot
Ah, j'ai utilisé le terme "serveur" de façon assez vague ... Toutes les interactions des utilisateurs avec le système se font via une application Web, généralement accessible via un navigateur sur le réseau. Mais certains clients préfèrent une solution non réseau, nous exécutons donc Chrome sur la console dans une sorte de mode kiosque. (Ceci est également pratique pour déboguer les problèmes de configuration réseau, vous pouvez brancher un moniteur / souris / clavier et accéder aux outils de diagnostic de base dans l'application Web sans avoir besoin des informations de connexion Linux). Il y a probablement une solution plus légère que lightdm / xfce, mais c'était la plus simple à installer ...
Mike Blackwell
Pour tous ceux qui veulent des règles systemd-udevd exécutant directement un script: j'avais ceci; cela a fonctionné pendant un certain temps, mais à un moment donné, il a cessé d'exécuter le script si udevd avait été démarré automatiquement. Arrêtez et redémarrez à partir de la ligne de commande, et ce serait bien. De plus, cela n'a jamais bien fonctionné avec NTFS + FUSE car udev a détecté qu'il avait un processus enfant de longue durée (ntfs-3g) et l'a tué après 60s. Conclusion: les règles udev exécutant directement un script sont une perte de temps. Utilisez plutôt les règles udev et un service systemd, comme indiqué dans les réponses. Ensuite, vous n'avez pas non plus à gérer les espaces de noms (MountFlags = slave).
Mark
J'ai eu un problème similaire d'un script démarré par udev ne pouvant pas établir de connexions réseau. La solution ci-dessous d'utiliser systemd a également fonctionné - merci!
Quentin Stafford-Fraser

Réponses:

28

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é directement à partir d'une règle udev ou 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 , résultant en Error looking up object for device /dev/sdb1. Malheureusement, car udisks2 pourrait prendre soin de tout le désordre de point de montage ...)

Le gros du travail est effectué par un script shell, qui prend en charge la création et la suppression des 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/[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

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 debugactive la journalisation détaillée pour /var/log/syslogque vous puissiez voir ce qui se passe.
  • udevadm control --reload-rules après avoir modifié des fichiers dans le dir 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.
Mike Blackwell
la source
4
Sensationnel. C'est génial. J'aimerais pouvoir donner plusieurs votes positifs! La seule chose que j'ai dû modifier, c'est que sur mon système, blkidil ne semble pas en extraire un ID_FS_LABEL, j'ai donc simplement utilisé le DEVBASEplutôt que LABELde le construire à la MOUNT_POINTplace.
Travis Griggs
Cette configuration peut-elle être modifiée pour fonctionner avec des périphériques ATA / SCSI? Voir: serverfault.com/q/825779/297059
user339676
@Travis - Vous pouvez utiliser à la udevadmplace de blkid. Il donne beaucoup plus de détails ainsi que des informations supplémentaires. (par exemple, udevadm info --query=property --name=sda1)
user339676
cela ne fonctionne pas bien au démarrage, si un périphérique USB est déjà connecté. Des idées?
Michal Artazov
Lorsque nullglobs n'est pas défini, lors du démontage, le nettoyage peut générer une erreur comme /usr/bin/find: '/media/*': No such file or directory. Le nettoyage peut utiliser une vérification supplémentaire comme if [ "$f" != "/media/*" ]; thenavant l'exécution find.
Pro Backup
12

il existe une nouvelle systemdoption de montage automatique succinct qui peut être utilisée avec fstablaquelle vous pouvez utiliser toutes les options d'autorisation de montage standardisées, et cela ressemble à ceci:

  x-systemd.automount

un exemple de celui-ci dans une fstabligne:

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

l' noautooption signifie qu'il ne tentera pas d'être monté au démarrage, comme avec les logiciels plus anciens autofs.

après avoir ajouté une nouvelle x-systemd.automountligne, fstabvous devez 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

infinite-etcetera
la source
sudo systemctl restart local-fs.targeta fait l'affaire pour moi
Philippe Gachoud
2

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
  • enregistrer les actions /var/log/messagesavec la balise usb-mount.sh
  • nom du périphérique préfixe avec l'étiquette de dispositif pour le point de montage de ne pas fonctionner pour des problèmes avec les lecteurs qui ne sont pas attribués une étiquette (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

six-k
la source
2

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

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

Eric V.
la source
0

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
pingouinjeff
la source
Vous voudrez peut-être décrire ce qui est différent entre la réponse originale et la vôtre en quelques mots, pour la rendre plus utile. PS: il n'y a pas eu de réponse de Warren Young; peut-être que vous vouliez dire la réponse de Mike Blackwell qui a été modifiée?
Amir