我们正在将服务器从一个非常过时的发行版更新到一个现代的基于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(可能不是必需的,但是不能伤害…)。 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
的自动挂载驱动器列表 /var/log/messages
的操作logging到/var/log/messages
/media/sdd2_usbtest
, /media/sdd2_
由于@迈克·布莱克威尔已经完成了大部分的重任,我决定不重写它; 只是做了必要的改变。 我已经承认他的作品看到了他原来的答案的名字和URI。