- Примечание для Ubuntu Server 11.10: этот сценарий не выполняется на Ubuntu Server 11.10 из-за устаревшей команды
vol_id
. 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 используются для отправки аппаратных событий слушателям, таким как Desktop Environments. Поэтому, когда вы входите в GNOME и вставляете компакт-диск или подключаете USB-накопитель, это событие следует следующей цепочке:
kernel -> udev -> dbus -> hal -> gnome-vfs/nautilus (mount)
И до того, ваш диск будет установлен. Но в безголовой системе нам не нужно входить в систему, чтобы воспользоваться преимуществами автоматического монтирования.
Правила Удев
Поскольку 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
, а не /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
Если ваш съемный диск содержит 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 перезагрузить его правила, чтобы новое вступило в силу. Используйте любой из этих методов (если первый не работает, второй должен ... но попробуйте первый первый):
Сценарий! На самом деле, 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 только отправляет имя УСТРОЙСТВА.
Если при загрузке возникает проблема с монтированием дисков, вы можете поместить в этот скрипт хороший длинный 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
Супер бонус сценарий очистки!
Еще один скрипт. Все это делает размонтирование устройства и удаление каталогов точки монтирования. Предполагается, что для этого у него есть привилегии, поэтому вам нужно запустить его с помощью 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