UDEV 参考:

UDEV 参考:

这个问题类似,但与我想要的正好相反。我希望在启动时自动将外部 USB 驱动器安装到类似 的位置,而无需任何人登录/media/<label>

我不想将所有数据输入到 fstab 中,部分原因是它很繁琐和烦人,但主要原因是我无法预测我将插入什么内容或分区将来会如何变化。

我希望驱动器可以访问多普勒,当我使用 SSH 登录时可用。 gnome-mount似乎只有当您本地登录到 Gnome 图形会话时才会挂载东西。

答案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 是内核的热插拔系统。它可以自动配置正确的设备和设备符号链接(例如/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.dKarmic 上),并按数字顺序进行处理。任何不以数字开头的文件都会在编号文件之后进行处理。在我的系统上,HAL 规则位于一个名为的文件中90-hal.rules,因此我将规则放入其中,89-local.rules以便它们在到达 HAL 之前得到处理。首先,您需要确保这些规则发生在 之后60-persistent-storage.ruleslocal.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>,然后将此信息传递给执行安装的脚本。具体来说,此规则匹配:

  1. 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
    
  2. ACTION=="add"-- 仅匹配add事件...

  3. SUBSYSTEMS=="usb"-- 仅匹配 USB 总线上的设备。我们SUBSYSTEMS在这里使用,因为这与我们设备的父设备匹配;我们感兴趣的设备实际上是 SUBSYSTEM=="scsi"。与父 USB 设备匹配可避免将我们的程序添加到内部驱动器。

  4. 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 monitorman 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几乎相同。

的替代品ivmanhalevt,它在 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 部分中)。安装后,这个过程大部分并不可怕:

  1. 确保系统 halevt-daemon 已启用/etc/default/halevt
  2. 允许系统 halevt 用户挂载设备/etc/PolicyKit/PolicyKit.conf(见下文;来源
  3. 修改 HAL 策略以将卷标复制到首选挂载点/etc/hal/fdi/policy/preferences.fdi(见下文)
  4. 如果您需要 CD/DVD 支持,请获取eject.hal脚本来自上述博文,修改并保存/usr/local/bin
  5. 修改 halevt 系统配置以启用挂载/etc/halevt/halevt.xml
  6. 将代码添加到登录管理器的会话前和会话后脚本中,以在有人登录时停止系统 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 &amp; hal.block.is_volume = true  &amp; hal.volume.is_disc = true &amp; 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

答案4

对于基于 Debian 的系统(例如 Ubuntu 等),有usb安装自动为您安装 USB 驱动器的软件包。它基本上使用基于 udev 的方法,正如已经概述的那样 - 只是它只是一个简单的软件包安装。似乎原来的该软件包的作者已经精疲力竭,但 Ubuntu/Debian 似乎仍在维护它(我猜它没有那么复杂) - 所以它在最新版本中仍然可用。

可以配置已安装的脚本(/etc/usbmount/usbmount.conf)以提供适当的挂载点。

相关内容