- 对于Ubuntu Server 11.10的注意事项:由于过时的
vol_id
命令,此脚本在Ubuntu Server 11.10上失败。 vol_id
已被取代blkid
。要修复脚本,请在脚本中将“ vol_id”替换为“ blkid -o udev” udev-auto-mount.sh
。
我已经为此努力了一段时间,我想我已经找到了可行的解决方案。它是在基于Debian的系统上开发和测试的,因此应该可以在Ubuntu上运行。我将指出它所做的假设,以便它也可以适用于其他系统。
- 它将自动在插件上安装USB驱动器,并且不需要花太多时间就可以适应Firewire。
- 它使用UDEV,因此不会胡闹HAL / DeviceKit / GNOME-Anything。
- 它会自动创建一个
/media/LABEL
目录以将设备挂载到该目录。
- 但是,它可能会干扰其他自动安装器。我无法测试。我希望在Gnome-VFS处于活动状态的情况下,两者都可以尝试执行挂载...如果Gnome-VFS挂载失败,则可能未配置桌面图标。应该可以从Gnome卸载,但是可能需要执行
gksudo
类似操作。
我尚未在系统启动时对此进行测试,但是我看到它可能不起作用的唯一原因是,如果它在系统准备好挂载之前尝试挂载USB驱动器。如果是这样,您可能需要对安装脚本进行其他一些调整。(我正在与ServerFault进行检查,以查看是否有任何建议,但是在那里没有太多兴趣。)
然后,继续。
UDEV参考:
背景(UDEV?Whuzzat?)
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的规则将保存在/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
not /dev/sdc1
),并$env{FOO}
获取环境变量FOO的内容。
测试规则
第一个参考链接(上面)是一个出色的UDEV教程,但是它已经过时了。它运行的用于测试您的规则的程序(udevtest
尤其是)已被catch-all udevadm
实用程序取代。
添加规则后,插入设备。给它几秒钟,然后检查分配给它的设备:
$ 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+=
在最后几行中从我们的规则中查找脚本名称(在此示例中,脚本名称从底部第3行)。您可以看到将用于此设备的参数。您现在可以运行该命令以检查参数是否正确。如果它可以在您的命令行上运行,则在插入设备后应该会自动运行。
您还可以实时监视UDEV事件:运行sudo udevadm monitor
(man udevadm
有关开关的详细信息,请参阅)。然后,只需插入新设备,然后观看事件滚动。(除非您真正了解低级的细节,否则可能会造成过度杀伤力。)
重新加载规则
验证规则正确读取后,您需要告诉UDEV重新加载其规则,以便新规则生效。使用以下任何一种方法(如果第一种方法不起作用,则第二种方法应...但尝试第一种方法):
脚本!实际上,有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
超级奖金清理脚本!
还有一个脚本。所有这些操作就是卸载设备并删除安装点目录。它假定它具有privs来执行此操作,因此您需要使用来运行它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