自动安装USB驱动器与systemd

我们正在将服务器从一个非常过时的发行版更新到一个现代的基于Debian Jessie的系统,包括lightdm / xfce,当然还有systemd(和udisks2)。 一个棘手的问题是自动挂载USB驱动器。 我们曾经用一些udev规则来完成这个。 旧的规则几乎仍然有效 – 挂载点被创build,驱动器挂载正常,但几秒钟后systemd正在做一些破坏挂载的事情,所以后续的访问尝试导致“传输端点未连接”错误。

通过命令行手动安装驱动器可以正常工作。 因此,让一个文件pipe理器(thunar和thunar-volman,反过来使用udisk2)。 但这些都不是可行的select – 这些系统大多是无头的,所以通常不会正常运行。 我们需要能够插入磁盘驱动器,实现无人值守的基于cron的备份。

我认为修改udev脚本生成一个等待几秒钟的分离作业,然后再执行mount,可能会有所斩获,但是systemd似乎不愿意这样做 – 它仍然等待分离的作业完成之前持续。

也许有udev脚本挠痒痒udisks2不知何故是正确的做法? 我输了,所以任何build议非常赞赏。

经过几次错误的开始,我明白了这一点。 关键是在udev和安装脚本之间添加systemd单元服务。

(为了logging,我无法使用udisks2(通过类似于udisksctl mount -b /dev/sdb1 )直接从udev规则或systemd单元文件调用这个工作。似乎有一个竞争条件,设备节点没有完全准备好,导致Error looking up object for device /dev/sdb1 。不幸的是,因为udisks2可以照顾所有的挂载点杂乱…)

繁重的工作由一个shell脚本完成,该脚本负责创build和删除挂载点,以及挂载和卸载驱动器。

/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 (eg 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 /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 

该脚本又被系统单元文件调用。 我们使用“@”文件名语法,所以我们可以传递设备名称作为参数。

/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 

最后,一些udev规则在hotplug / unplug上启动和停止systemd单元服务:

/etc/udev/rules.d/99-local.rules

 KERNEL=="sd[az][0-9]", SUBSYSTEMS=="usb", ACTION=="add", RUN+="/bin/systemctl start usb-mount@%k.service" KERNEL=="sd[az][0-9]", SUBSYSTEMS=="usb", ACTION=="remove", RUN+="/bin/systemctl stop usb-mount@%k.service" 

这似乎是诀窍! 几个有用的命令来debugging这样的东西:

  • udevadm control -l debug将详细logging打开到/var/log/syslog以便您可以看到发生了什么。
  • udevadm control --reload-rules在修改udevadm control --reload-rules文件后dir(可能不是必需的,但是不能伤害…)。
  • 在修改systemd单元文件后, systemctl daemon-reload

有一个新的,简洁的systemd自动安装选项,可以使用fstab ,它允许您使用所有的标准化安装许可选项,它看起来像这样:

  x-systemd.automount 

它在fstab行中的一个例子:

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

noauto选项将意味着它不会尝试在启动autofs ,就像使用较早的软件autofs

在添加新的x-systemd.automount行到fstab您需要运行:

  sudo systemctl daemon-reload 

然后是以下两个或一个:

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

欲了解更多信息:

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

我已经从@MikeBlackwell将脚本修改为:

  • 识别跨越多个字符的设备名称,不仅仅是/dev/sd[ab]而是/dev/sd[ab]* ; 经常是主轴数量较多的服务器的情况。
  • 跟踪/var/log/usb-mount.track的自动挂载驱动器列表
  • 使用标记usb-mount.sh将 /var/log/messages的操作logging到/var/log/messages
  • 带有装载点设备标签的前缀设备名称不能运行到尚未分配标签的驱动器问题(空?): /media/sdd2_usbtest/media/sdd2_
  • 包括包装脚本以适当放置文件并在需要时撤消

由于@迈克·布莱克威尔已经完成了大部分的重任,我决定不重写它; 只是做了必要的改变。 我已经承认他的作品看到了他原来的答案的名字和URI。

find它在https://github.com/six-k/automount-usb