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/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 pour
/var/log/syslog
que 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.