答案1
- Ubuntu Server 11.10 注意事项:由于
vol_id
命令已过时,此脚本在 Ubuntu Server 11.10 上失败。vol_id
已被取代blkid
。要修复脚本,请将udev-auto-mount.sh
脚本中的“vol_id”替换为“blkid -o udev”。
我已经为此苦思冥想了一段时间,我想我已经找到了一个可行的解决方案。它是在基于 Debian 的系统上开发和测试的,因此它应该可以在 Ubuntu 上运行。我将指出它所做的假设,以便它也可以适用于其他系统。
- 它将自动在插件上安装 USB 驱动器,并且不需要花费太多精力来适应 Firewire。
- 它使用 UDEV,因此不能与 HAL/DeviceKit/GNOME-Anything 进行任何交互。
- 它会自动创建一个
/media/LABEL
目录来挂载该设备。 - 然而,它可能干扰其他自动挂载程序;我无法对此进行测试。我预计,在 Gnome-VFS 处于活动状态时,两者都可能尝试挂载...如果 Gnome-VFS 挂载失败,则可能无法配置桌面图标。应该可以从 Gnome 卸载,但可能需要
gksudo
或类似操作。
我没有在系统启动时测试过这一点,但我认为它可能无法工作的唯一原因是,如果它在系统准备好挂载之前尝试挂载 USB 驱动器。如果是这种情况,您可能需要对挂载脚本进行额外的调整。(我使用 ServerFault 检查看看是否有任何建议,但那边对此不太感兴趣。)
那么,开始吧。
UDEV 参考:
- 编写 udev 规则(这udev 规则参考)
- 人udev(请查看你的系统以获取最新版本)
- 人 udevadm(udev 管理工具;再次查看你的系统以获取最新版本)
- 安装时备份到 USB 驱动器(完全不同的问题,但有助于理解解决方案)
背景(UDEV?怎么了?)
UDEV 是内核的热插拔系统。它可以自动配置正确的设备和设备符号链接(例如/dev/disk/by-label/<LABEL>
),无论是在启动时还是在系统运行时添加的设备。
D-Bus 和 HAL 用于将硬件事件发送到桌面环境等监听器。因此,当您登录 GNOME 并插入 CD 或插入 USB 驱动器时,该事件将遵循以下链:
kernel -> udev -> dbus -> hal -> gnome-vfs/nautilus (mount)
瞧,您的驱动器就挂载了。但在无头系统中,我们不想必须登录才能获得自动挂载的好处。
Udev 规则
由于 UDEV 允许我们在设备插入时编写规则并运行程序,因此这是一个理想的选择。我们将利用 Debian/Ubuntu 的现有规则,让它们/dev/disk/by-label/<LABEL>
为我们设置符号链接,并添加另一条规则来为我们安装设备。
UDEV 的规则保存在 Karmic 中/etc/udev/rules.d
(和/lib/udev/rules.d
Karmic 上),并按数字顺序进行处理。任何不以数字开头的文件都会在编号文件之后进行处理。在我的系统上,HAL 规则位于一个名为的文件中90-hal.rules
,因此我将规则放入其中,89-local.rules
以便它们在到达 HAL 之前得到处理。首先,您需要确保这些规则发生在 之后60-persistent-storage.rules
。 local.rules
这可能就足够了。
将其放入您的新规则文件中:
# /etc/udev/rules.d/local.rules
# /etc/udev/rules.d/89-local.rules
# ADD rule: if we have a valid ID_FS_LABEL_ENC, and it's USB, mkdir and mount
ENV{ID_FS_LABEL_ENC}=="?*", ACTION=="add", SUBSYSTEMS=="usb", \
RUN+="/usr/local/sbin/udev-automounter.sh %k"
确保 后面没有空格
\
,只有一个newline
(\n
)。更改
SUBSYSTEMS=="usb"
为SUBSYSTEMS=="usb|ieee1394"
以获得 Firewire 支持。如果您希望设备始终由特定用户拥有,请添加一个
OWNER="username"
子句。如果您只需要特定用户拥有的文件,请调整安装脚本。
阅读规则
这会将要运行的程序添加到设备的要运行的程序列表中。它通过 识别 USB 分区设备<LABEL>
,然后将此信息传递给执行安装的脚本。具体来说,此规则匹配:
ENV{ID_FS_LABEL_ENC}=="?*"
-- 由先前的系统规则设置的环境变量。对于非文件系统不存在,因此我们要检查它。我们实际上想将其用作ID_FS_LABEL
挂载点,但我还没有说服 UDEV 帮我转义它,所以我们将让挂载脚本来处理它。此环境变量和其他环境变量可通过 udev 使用以下
vol_id
命令获取 (已弃用)。这是一个方便的工具,可以快速查看分区的详细信息:$ sudo vol_id /dev/sdc1 ID_FS_TYPE=ext2 ID_FS_UUID=a40d282a-4a24-4593-a0ab-6f2600f920dd ID_FS_LABEL=Travel Dawgs ID_FS_LABEL_ENC=Travel\x20Dawgs ID_FS_LABEL_SAFE=Travel_Dawgs
ACTION=="add"
-- 仅匹配add
事件...SUBSYSTEMS=="usb"
-- 仅匹配 USB 总线上的设备。我们SUBSYSTEMS
在这里使用,因为这与我们设备的父设备匹配;我们感兴趣的设备实际上是 SUBSYSTEM=="scsi"。与父 USB 设备匹配可避免将我们的程序添加到内部驱动器。RUN+="..."
-- 不是匹配,而是操作:将此程序添加到要运行的程序列表中。在程序的参数中,%k
展开为设备名称(例如sdc1
,不是/dev/sdc1
)并$env{FOO}
获取环境变量 FOO 的内容。
测试规则
第一个参考链接(上文)是一篇优秀的 UDEV 教程,但略显过时。它运行的用于测试规则的程序(udevtest
特别是)已被 catch-alludevadm
实用程序取代。
添加规则后,插入设备。等待几秒钟,然后使用以下命令检查已将其分配给哪个设备:
$ ls -l /dev/disk/by-label/*
lrwxrwxrwx 1 root root 10 2009-10-25 07:27 label_Foo -> ../../sda1
lrwxrwxrwx 1 root root 10 2009-10-25 07:27 label_Bar -> ../../sdb1
lrwxrwxrwx 1 root root 10 2009-10-25 07:27 label_Baz -> ../../sdc1
如果您的可移动驱动器包含label_Baz
,则它位于设备 上sdc1
。运行此命令并查看最后的输出:
$ sudo udevadm test /sys/block/sdc/sdc1
parse_file: reading (...) (many lines about files it reads)
import_uevent_var: import into environment: (...) (many lines about env variables)
(...) (many lines tracing rule matches & programs run)
update_link: found 1 devices with name 'disk/by-label/LABEL_BAZ'
update_link: found '/block/sdc/sdc1' for 'disk/by-label/LABEL_BAZ'
update_link: compare (our own) priority of '/block/sdc/sdc1' 0 >= 0
update_link: 'disk/by-label/LABEL_BAZ' with target 'sdc1' has the highest priority 0, create it
udevtest: run: '/usr/local/sbin/udev-automounter.sh sdc1 LABEL_BAZ'
udevtest: run: 'socket:/org/freedesktop/hal/udev_event'
udevtest: run: 'socket:@/org/kernel/udev/monitor'
从我们的RUN+=
规则的最后几行(本例中从底部开始第三行)。您可以看到将用于此设备的参数。您现在可以运行该命令来检查参数是否合理;如果它在您的命令行上运行,则在插入设备时它应该会自动运行。
您还可以实时监控 UDEV 事件:运行sudo udevadm monitor
(man udevadm
有关开关的详细信息,请参阅)。然后只需插入新设备并观察事件滚动。(除非您对非常低级的细节感兴趣,否则可能有点过头了……)
重新加载规则
验证规则已正确读取后,您需要告诉 UDEV 重新加载其规则,以便新规则生效。使用以下任何一种方法(如果第一种方法不起作用,第二种方法应该有效……但请先尝试第一种方法):
跑步
sudo udevadm control --reload-rules
跑步
sudo /etc/init.d/udev reload
重启
脚本!实际上,是 2 个脚本……
这是第一个脚本。 由于我们运行的程序需要快速完成,因此这只是在后台运行第二个脚本。将其放入/usr/local/sbin/udev-automounter.sh
:
#!/bin/sh
#
# USAGE: usb-automounter.sh DEVICE
# DEVICE is the actual device node at /dev/DEVICE
/usr/local/sbin/udev-auto-mount.sh ${1} &
这是第二个脚本。 这会进行更多的输入检查。将其放入/usr/local/sbin/udev-auto-mount.sh
。您可能想要调整下面的安装选项。此脚本现在自行处理查找分区 LABEL;UDEV 仅发送 DEVICE 名称。
如果在启动时安装驱动器时出现问题,您可以在此脚本中放入一个较长的时间sleep 60
,以便在脚本尝试安装驱动器之前给系统时间来完成所有操作。
我在评论中给出了如何检查(运行ps
以查看 Web 服务器是否正在运行)的建议,但您需要根据自己的系统进行调整。我认为您可能使用的大部分网络服务器都足以满足此目的 - nfsd、smbd、apache 等。当然,风险在于如果服务未运行,则挂载脚本将失败,因此测试特定文件的存在可能是一个更好的解决方案。
#!/bin/sh
#
# USAGE: udev-auto-mount.sh DEVICE
# DEVICE is the actual device node at /dev/DEVICE
#
# This script takes a device name, looks up the partition label and
# type, creates /media/LABEL and mounts the partition. Mount options
# are hard-coded below.
DEVICE=$1
# check input
if [ -z "$DEVICE" ]; then
exit 1
fi
# test that this device isn't already mounted
device_is_mounted=`grep ${DEVICE} /etc/mtab`
if [ -n "$device_is_mounted" ]; then
echo "error: seems /dev/${DEVICE} is already mounted"
exit 1
fi
# If there's a problem at boot-time, this is where we'd put
# some test to check that we're booting, and then run
# sleep 60
# so the system is ready for the mount below.
#
# An example to experiment with:
# Assume the system is "booted enough" if the HTTPD server is running.
# If it isn't, sleep for half a minute before checking again.
#
# The risk: if the server fails for some reason, this mount script
# will just keep waiting for it to show up. A better solution would
# be to check for some file that exists after the boot process is complete.
#
# HTTPD_UP=`ps -ax | grep httpd | grep -v grep`
# while [ -z "$HTTPD_UP" ]; do
# sleep 30
# HTTPD_UP=`ps -ax | grep httpd | grep -v grep`
# done
# pull in useful variables from vol_id, quote everything Just In Case
eval `/sbin/vol_id /dev/${DEVICE} | sed 's/^/export /; s/=/="/; s/$/"/'`
if [ -z "$ID_FS_LABEL" ] || [ -z "$ID_FS_TYPE" ]; then
echo "error: ID_FS_LABEL is empty! did vol_id break? tried /dev/${DEVICE}"
exit 1
fi
# test mountpoint - it shouldn't exist
if [ ! -e "/media/${ID_FS_LABEL}" ]; then
# make the mountpoint
mkdir "/media/${ID_FS_LABEL}"
# mount the device
#
# If expecting thumbdrives, you probably want
# mount -t auto -o sync,noatime [...]
#
# If drive is VFAT/NFTS, this mounts the filesystem such that all files
# are owned by a std user instead of by root. Change to your user's UID
# (listed in /etc/passwd). You may also want "gid=1000" and/or "umask=022", eg:
# mount -t auto -o uid=1000,gid=1000 [...]
#
#
case "$ID_FS_TYPE" in
vfat) mount -t vfat -o sync,noatime,uid=1000 /dev/${DEVICE} "/media/${ID_FS_LABEL}"
;;
# I like the locale setting for ntfs
ntfs) mount -t auto -o sync,noatime,uid=1000,locale=en_US.UTF-8 /dev/${DEVICE} "/media/${ID_FS_LABEL}"
;;
# ext2/3/4 don't like uid option
ext*) mount -t auto -o sync,noatime /dev/${DEVICE} "/media/${ID_FS_LABEL}"
;;
esac
# all done here, return successful
exit 0
fi
exit 1
超级奖励清理脚本!
再来一个脚本。这个脚本的作用是卸载设备并删除挂载点目录。它假定它有权限执行此操作,因此您需要使用 来运行它sudo
。此脚本现在在命令行上获取完整的挂载点,例如:
$ /usr/local/sbin/udev-unmounter.sh "/media/My Random Disk"
将其放入/usr/local/sbin/udev-unmounter.sh
:
#!/bin/sh
#
# USAGE: udev-unmounter.sh MOUNTPT
# MOUNTPT is a mountpoint we want to unmount and delete.
MOUNTPT="$1"
if [ -z "$MOUNTPT" ]; then
exit 1
fi
# test mountpoint - it should exist
if [ -e "${MOUNTPT}" ]; then
# very naive; just run and pray
umount -l "${MOUNTPT}" && rmdir "${MOUNTPT}" && exit 0
echo "error: ${MOUNTPT} failed to unmount."
exit 1
fi
echo "error: ${MOUNTPT} does not exist"
exit 1
答案2
网络上其他人建议的最后一个选项是ivman
,但这似乎取决于pmount
,而您已经声明它不起作用。 pmount
已被放弃并且ivman
几乎相同。
的替代品ivman
是halevt
,它在 Karmic 中可用。它是 的重新实现ivman
(读作:“维护”和“不依赖于 pmount”)。该软件包在 Jaunty 上不可用,但如果您不打算升级,您可以自己构建它。
这两个工具都位于 DBus 和 HAL 层之上,并响应来自这两个层的事件。显然,它们都可以作为系统守护进程或用户会话挂载管理器(类似 Gnome-VFS)运行 - 这些/etc/defaults/{ivman,halevt}
文件负责系统设置。
以下是一些说明进行调整ivman
以使用/media/<LABEL>
挂载点。可能halevt
有更简单的方法来实现这一点,但也许他们会帮你找到答案。
与 HALEVT 合作
更新:为了获得自动 CD 挂载,而我的 UDEV 答案没有提供,我更深入地研究了halevt
。我发现了这个博客文章这有助于解释这个过程。我确实必须halevt
为 Debian Lenny 编译自己的包(幸运的是,所有依赖项都在 lenny-backports 部分中)。安装后,这个过程大部分并不可怕:
- 确保系统 halevt-daemon 已启用
/etc/default/halevt
- 允许系统 halevt 用户挂载设备
/etc/PolicyKit/PolicyKit.conf
(见下文;来源) - 修改 HAL 策略以将卷标复制到首选挂载点
/etc/hal/fdi/policy/preferences.fdi
(见下文) - 如果您需要 CD/DVD 支持,请获取
eject.hal
脚本来自上述博文,修改并保存/usr/local/bin
。 - 修改 halevt 系统配置以启用挂载
/etc/halevt/halevt.xml
- 将代码添加到登录管理器的会话前和会话后脚本中,以在有人登录时停止系统 halevt 守护程序,并在有人注销时重新启动它。
如果您需要重新启动 HAL 和 HALEVT 守护进程来检查新配置,请使用以下方法按正确的顺序获取它们:
sudo sh -c "/etc/init.d/halevt stop ; /etc/init.d/hal restart ; /etc/init.d/halevt start"
步骤1
请START_DAEMON=yes
检查/etc/default/halevt
。
第2步
在/etc/PolicyKit/PolicyKit.conf
,添加这个里面这<config></config>
部分:
<match action="org.freedesktop.hal.storage.mount-removable">
<match user="halevt">
<return result="yes"/>
</match>
</match>
步骤3
在/etc/hal/fdi/policy/preferences.fdi
,添加这个里面` 部分:
<match key="volume.label" empty="false">
<match key="volume.label" is_absolute_path="false">
<merge key="volume.policy.desired_mount_point" type="copy_property">volume.label</merge>
</match>
</match>
步骤4
脚本很好,但需要运行/bin/bash
;某些系统可能实际使用调用/bin/dash
时/bin/sh
。因此,请更改脚本中的顶行,以确保获得正确的内容:
#!/bin/sh <------ old first line
#!/bin/bash <------ new first line
步骤5
这是最有趣的部分。您的系统可能已经提供了基本功能/etc/halevt/halevt.xml
,因此您必须根据自己的用途进行调整。就我而言,我的系统已经提供了基本的可移动设备安装功能,但我必须添加对 CDROM 安装和弹出按钮的支持。
我提到的博客文章有一个XML 配置的好例子以供您自己进行调整。它主要是关于为作者的fluxbox
环境设置 gnome-mount 替代品,因此他的示例 XML 比您想要的要多,但它是了解您可以做什么的好方法。中也有一些很好的例子/usr/share/doc/halevt/examples
。
我还必须先跑步,sudo sh -c "mkdir /var/halevt ; chown halevt:plugdev /var/halevt"
然后一切才能正常进行。
以下是我为使自动安装 CD/DVD 正常工作而添加的内容:
<!-- CD/DVD mount -->
<halevt:Device match="hal.block.device & hal.block.is_volume = true & hal.volume.is_disc = true & hal.volume.disc.has_data = true">
<halevt:Property name="hal.volume.is_mounted">
<halevt:Action value="true" exec="halevt-mount -u $hal.udi$ -p $hal.volume.policy.desired_mount_point$ -m 002"/>
</halevt:Property>
</halevt:Device>
<!-- CD/DVD eject button support -->
<halevt:Device match="hal.storage.drive_type = cdrom">
<halevt:Condition name="EjectPressed" exec='/usr/local/bin/eject.hal $hal.block.device$'/>
</halevt:Device>
第 6 步
一旦系统 halevt-daemon 开始运行,您需要在登录 GNOME 时禁用它,并在注销时重新启动它。(参见我对这个问题的回答对于非 GDM 登录管理器。)由于我没有使用它,所以这个东西只是理论上的,但它应该可以工作。
在 中/etc/gdm/PreSession/Default
添加此项以停止系统 halevt-daemon:
/etc/init.d/halevt stop
在 中/etc/gdm/PostSession/Default
添加此项以重新启动系统 halevt-daemon:
/etc/init.d/halevt start
答案3
随着时间的推移,更简单的解决方案出现了。
此解决方案依赖于为此目的而编写的 udevil 软件包,不需要修改 udev 规则。对于新老用户来说,这可能是一种更可取的直接解决方案。
udevil 的脚本devmon
仅依赖 udev 和 glib 即可完成所有神奇的功能。几乎开箱即用,无需初始配置。
我在工作站上所做的一切就是rc.local
像这样调用 devmon:
devmon 2>&1 >> /var/log/devmon &
为了您的方便,您可能希望将其嵌入到初始化脚本中,而不是rc.local
使用自动化工具来pleaserun
创建它:https://unix.stackexchange.com/a/124609/42673
运行后,我插入的存储将被检查(它会查找分区,如果找到,则查看其文件系统标签),然后挂载到/media/FILESYSTEM_LABEL
。
无法想象有什么比这更简单的事情,除非(臭名昭著的)systemd 在未来某个时候整合此功能。
udevil 概览 (github.io/udevil)
脚本:devmon(igurublog/脚本-devmon)