Unidades USB de montaje automático con systemd

27

Estamos actualizando nuestros servidores desde una distribución muy desactualizada a un sistema moderno basado en Debian Jessie, que incluye lightdm / xfce y, por supuesto, systemd (y udisks2). Un punto de fricción es el montaje automático de unidades USB. Solíamos lograr esto con algunas reglas de udev. Las viejas reglas casi siguen funcionando: el punto de montaje se crea y la unidad se monta bien, pero después de unos segundos systemd está haciendo algo que rompe el montaje, por lo que los intentos de acceso posteriores producen errores de "El punto final de transporte no está conectado".

Montar manualmente la unidad a través de la línea de comando funciona bien. Al igual que dejar un administrador de archivos (thunar y thunar-volman, que a su vez usa udisks2). Pero esas no son opciones viables: estos sistemas se ejecutan principalmente sin cabeza, por lo que normalmente no se ejecuta thunar. Necesitamos poder conectar unidades de disco para copias de seguridad desatendidas basadas en cron.

Pensé que modificar el script udev para generar un trabajo separado que espere unos segundos antes de realizar el montaje podría hacer el truco, pero systemd parece hacer todo lo posible para evitar esto; de alguna manera todavía espera que el trabajo separado termine antes continuo.

¿Quizás el guión udev le hace cosquillas a udisks2 de alguna manera es el enfoque correcto? Estoy perdido, por lo que cualquier consejo es muy apreciado.

Mike Blackwell
fuente
1
Solo relacionado tangencialmente, pero ... ¿Estás poniendo xfce en un servidor?
Parthian Shot
Ah, usé el término "servidor" de forma bastante flexible ... Toda la interacción del usuario con el sistema es a través de una aplicación web, a la que generalmente se accede a través de un navegador a través de la red. Pero algunos clientes prefieren una solución que no sea de red, por lo que ejecutamos Chrome en la consola en una especie de modo quiosco. (Esto también es útil para depurar problemas de configuración de red, puede conectar un monitor / mouse / teclado y acceder a las herramientas de diagnóstico básicas en la aplicación web sin necesidad de credenciales de inicio de sesión de Linux). Probablemente haya una solución más ligera que lightdm / xfce, pero esta fue la más sencilla de configurar ...
Mike Blackwell
Para cualquiera que quiera las reglas systemd-udevd ejecutando directamente un script: tuve esto; funcionó durante un tiempo, pero en algún momento dejó de ejecutar el script si udevd se había iniciado automáticamente. Deténgase y reinicie desde la línea de comando, y estaría bien. Además de eso, nunca funcionó bien con NTFS + FUSE porque udev detectó que tenía un proceso hijo de larga duración (ntfs-3g) y lo mató después de los años 60. En pocas palabras: las reglas de udev que ejecutan directamente un script son una pérdida de tiempo. Vaya con las reglas de udev y un servicio systemd en su lugar, como se señala en las respuestas. Entonces tampoco tiene que lidiar con espacios de nombres (MountFlags = slave).
Mark
Tuve un problema similar con un script iniciado por udev que no podía hacer conexiones de red. La solución a continuación de usar systemd también funcionó para esto, ¡gracias!
Quentin Stafford-Fraser

Respuestas:

28

Después de varios comienzos falsos, descubrí esto. La clave es agregar un servicio de unidad systemd entre udev y un script de montaje.

(Para el registro, no pude hacer que esto funcionara utilizando udisks2 (a través de algo así udisksctl mount -b /dev/sdb1) llamado directamente desde una regla udev o desde un archivo de unidad systemd. Parece que hay una condición de carrera y el nodo del dispositivo no está listo , lo que resulta en Error looking up object for device /dev/sdb1. Desafortunado, ya que udisks2 podría encargarse de todo el desorden del punto de montaje ...)

El trabajo pesado se realiza mediante un script de shell, que se encarga de crear y eliminar puntos de montaje, y de montar y desmontar las unidades.

/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

El script, a su vez, es llamado por un archivo de unidad systemd. Utilizamos la sintaxis del nombre de archivo "@" para poder pasar el nombre del dispositivo como argumento.

/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

Finalmente, algunas reglas de udev inician y detienen el servicio de la unidad systemd en 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"

Esto parece hacer el truco! Un par de comandos útiles para depurar cosas como esta:

  • udevadm control -l debugactiva el registro detallado para /var/log/syslogque pueda ver lo que está sucediendo.
  • udevadm control --reload-rules después de modificar archivos en el directorio rules.d (puede que no sea necesario, pero no puede dañar ...).
  • systemctl daemon-reload después de modificar los archivos de la unidad systemd.
Mike Blackwell
fuente
44
Guau. Esto es asombroso ¡Ojalá pudiera dar múltiples votos a favor! Lo único que tuve que modificar fue que, en mi sistema, blkidno parece extraer un ID_FS_LABEL, así que simplemente utilicé el en DEVBASElugar de LABELconstruirlo MOUNT_POINT.
Travis Griggs
¿Se puede modificar esta configuración para que funcione con dispositivos ATA / SCSI? Ver: serverfault.com/q/825779/297059
user339676
@Travis: puede usar en udevadmlugar de blkid. Ofrece muchos más detalles, así como información adicional. (p. ej. udevadm info --query=property --name=sda1)
usuario339676
esto no funciona bien en el arranque, si un dispositivo usb ya está conectado. ¿Algunas ideas?
Michal Artazov
Cuando no se establecen nullglobs, al desmontar, la limpieza puede generar un error como /usr/bin/find: '/media/*': No such file or directory. La limpieza puede usar una verificación adicional como if [ "$f" != "/media/*" ]; thenantes de ejecutarse find.
Pro Backup
12

hay una nueva y sucinta systemdopción de montaje automático que se puede usar con la fstabque le permite usar todas las opciones de permisos de montaje estandarizados, y se ve así:

  x-systemd.automount

Un ejemplo de ello en una fstablínea:

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

la noautoopción significará que no intentará montarse en el arranque, como ocurre con el software anterior autofs.

después de agregarle una nueva x-systemd.automountlínea fstab, debe ejecutar:

  sudo systemctl daemon-reload

y luego ambos, o uno, de los siguientes:

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

para más información al respecto:

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

infinito-etcétera
fuente
sudo systemctl restart local-fs.targethizo el truco para mí
Philippe Gachoud
2

He modificado el script de @MikeBlackwell a:

  • reconocer nombres de dispositivos que abarcan varios caracteres, no solo /dev/sd[a-z]sino /dev/sd[a-z]*; a menudo, el caso con servidores que tienen un mayor número de cabezales.
  • rastree la lista de unidades montadas automáticamente en /var/log/usb-mount.track
  • registrar las acciones con /var/log/messagescon la etiqueta usb-mount.sh
  • prefijo de nombre de dispositivo con la etiqueta del dispositivo para el punto de que no se ejecute en el que hubo problemas con los que no se han asignado una etiqueta de montaje (vacío?): /media/sdd2_usbtest,/media/sdd2_
  • Incluye scripts de contenedor para colocar los archivos adecuadamente y deshacer si es necesario

Como @MikeBlackwell ya ha realizado la mayor parte del trabajo pesado, decidí no reescribirlo; Acabo de hacer los cambios necesarios. He reconocido su trabajo avistando su nombre y URI de la respuesta original.

Encuéntralo en https://github.com/raamsri/automount-usb

seis-k
fuente
2

Usando pmount , systemd y el enfoque de Mike Blackwell, puede simplificar todo:

/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 y gracias Mike.

Eric V.
fuente
0

Iría con la respuesta de Warren Young. Tengo algunos cambios que hice en

Agregué algo de protección de espacio ya que estaba dando errores de la evaluación del entorno para la unidad.

Agregué una sección para chmod un disco usb para que todos los usuarios tengan acceso completo a discos que no sean ntfs o 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
penguinjeff
fuente
Es posible que desee describir lo que es diferente entre la respuesta original y la suya en pocas palabras, para que sea más útil. PD: no hubo respuesta de Warren Young; ¿Quizás quisiste decir la respuesta de Mike Blackwell que fue editada?
Amir