在没有用户登录的情况下在启动时自动将外部驱动器挂载到 /media/LABEL?

end*_*ith 74 linux mount ubuntu usb-flash-drive external-hard-drive

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

我不想将所有数据都输入到 fstab 中,部分原因是它既乏味又烦人,但主要是因为我无法预测我将插入的内容或分区将来会如何变化。

我希望驱动器可以被MPD 之类的东西访问,并且在我使用 SSH 登录时可用。 gnome-mount似乎只在您本地登录到 Gnome 图形会话时才挂载东西。

qua*_*ote 74

  • 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)
Run Code Online (Sandbox Code Playgroud)

而且很快,您的驱动器已安装。但是在无头系统中,我们不想必须登录才能获得自动挂载的好处。

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"
Run Code Online (Sandbox Code Playgroud)
  • 确保 之后没有空格\,只有一个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
    
    Run Code Online (Sandbox Code Playgroud)
  2. ACTION=="add"-- 只匹配add事件...

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

  4. RUN+="..."-- 不是匹配,而是操作:将此程序添加到要运行的程序列表中。在程序的参数中,%k扩展为设备名称(例如sdc1,not /dev/sdc1)并$env{FOO}获取环境变量 FOO 的内容。

测试规则

第一个参考链接(上面)是一个优秀的 UDEV 教程,但它有点过时了。它运行以测试您的规则(udevtest特别是)的程序已被包罗万象的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
Run Code Online (Sandbox Code Playgroud)

如果您的可移动驱动器包含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 Code Online (Sandbox Code Playgroud)

从我们RUN+=规则的最后几行(在本例中从底部算起第 3 行)中查找脚本名称。您可以看到将用于此设备的参数。您现在可以运行该命令来检查参数是否合理;如果它适用于您的命令行,则在插入设备时它应该会自动工作。

您还可以实时监控 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} &
Run Code Online (Sandbox Code Playgroud)

这是第二个脚本。 这会做更多的输入检查。把这个放在/usr/local/sbin/udev-auto-mount.sh. 您可能需要调整下面的安装选项。该脚本现在自行处理查找分区 LABEL;UDEV 只发送设备名称。

如果在启动时挂载驱动器出现问题,您可以sleep 60在此脚本中放置一个很长的时间,以便在脚本尝试挂载驱动器之前让系统有时间完全启动。

我在评论中给出了关于如何检查(运行ps以查看网络服务器是否正在运行)的建议,但您需要为您的系统进行调整。我认为您可能使用的大多数网络服务器都足以满足此目的——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
Run Code Online (Sandbox Code Playgroud)

超级奖金清理脚本!

再来一个剧本。所有这些都是卸载设备并删除挂载点目录。它假定它具有执行此操作的权限,因此您需要使用sudo. 这个脚本现在在命令行上获取完整的挂载点,例如:

$ /usr/local/sbin/udev-unmounter.sh "/media/My Random Disk"
Run Code Online (Sandbox Code Playgroud)

把这个放在/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
Run Code Online (Sandbox Code Playgroud)

  • 你很棒!:) (3认同)
  • 我已经在 github 上组装了所有脚本:https://github.com/fatso83/Code-Snippets/tree/master/system-utils/ubuntu/automount 它们在 Ubuntu 10.10 上运行良好,还包括自动卸载 (3认同)

qua*_*ote 9

其他人在网络上建议的最后一个选项是ivman,但这似乎取决于pmount,而您已经声明该选项不起作用。 pmount被遗弃,ivman几乎相同。

的替代品ivmanhalevt,它在 Karmic 中可用。它是ivman(阅读:“维护”和“不依赖于pmount”)的重新实现。该软件包在 Jaunty 上不可用,但如果您不打算升级,您可以自己构建它。

这两个工具都位于 DBus 和 HAL 层之上,并响应来自它们的事件。显然,两者都可以作为系统守护程序或用户会话挂载管理器(a la 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-daemon,并在他们注销时重新启动它。

如果您需要重新启动 HAL 和 HALEVT 守护进程来检查您的新配置,请使用此命令以正确的顺序获取它们:

sudo sh -c "/etc/init.d/halevt stop ; /etc/init.d/hal restart ; /etc/init.d/halevt start"
Run Code Online (Sandbox Code Playgroud)

第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>
Run Code Online (Sandbox Code Playgroud)

第 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>
Run Code Online (Sandbox Code Playgroud)

第四步

脚本很好,但需要运行/bin/bash;某些系统实际上可能会使用/bin/dashwhen/bin/sh被调用。因此,更改脚本中的第一行以确保您得到正确的一行:

#!/bin/sh         <------ old first line

#!/bin/bash       <------ new first line
Run Code Online (Sandbox Code Playgroud)

第 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>
Run Code Online (Sandbox Code Playgroud)

第 6 步

一旦让系统 halevt-daemon 工作,您需要在登录 GNOME 时禁用它,并在注销时重新启动它。(请参阅对非 GDM 登录管理器的这个问题的回答。)这些东西是理论上的,因为我不使用它,但它应该可以工作。

在 中/etc/gdm/PreSession/Default,添加以下内容以停止系统 halevt-daemon:

/etc/init.d/halevt stop
Run Code Online (Sandbox Code Playgroud)

在 中/etc/gdm/PostSession/Default,添加以下内容以重新启动系统 halevt-daemon:

/etc/init.d/halevt start
Run Code Online (Sandbox Code Playgroud)

  • 对于在 2013 年阅读本文的人,他们现在应该知道 HAL 已被弃用,他们应该求助于基于 udev 的解决方案,例如上面给出的 quack quixote。 (3认同)

Cos*_*ușă 7

随着时间的推移,出现了更简单的解决方案。

此解决方案依赖于为此目的编写的 udevil 软件包,无需修改 udev 规则。作为一种直接的解决方案,它可能更可取(对于新老用户)。

devmon来自 udevil的脚本完成了所有的魔法,而只依赖于 udev 和 glib。几乎开箱即用,无需初始配置。

我在我的工作站上所做的只是rc.local像这样调用 devmon :
devmon 2>&1 >> /var/log/devmon &
为了您的舒适,您可能希望将其嵌入到 init 脚本中,而不是rc.local使用诸如pleaserun创建它的自动化工具:https : //unix.stackexchange.com/ a/124609/42673

让它运行后,我插入的存储被检查(它查找分区,如果找到则查看它们的文件系统标签)然后挂载到/media/FILESYSTEM_LABEL.
无法想象比这更简单的事情,除非(臭名昭著的)systemd 在未来的某个时候合并此功能。

udevil 概览 ( github.io/udevil )
脚本:devmon ( igurublog/script-devmon )