- Nota para o Ubuntu Server 11.10: Este script falha no Ubuntu Server 11.10 devido ao
vol_id
comando obsoleto . vol_id
foi substituído por blkid
. Para corrigir o script, substitua "vol_id" por "blkid -o udev" no udev-auto-mount.sh
script.
Eu já estou pensando nisso há um tempo e acho que encontrei uma solução funcional. Ele foi desenvolvido e testado em um sistema baseado no Debian, portanto, deve funcionar no Ubuntu. Apontarei as suposições que ele faz para que também possa ser adaptado a outros sistemas.
- Ele montará automaticamente unidades USB no plug-in e não deve demorar muito para se adaptar ao Firewire.
- Ele usa o UDEV, portanto, não se preocupe com o HAL / DeviceKit / GNOME-Anything.
- Ele cria automaticamente um
/media/LABEL
diretório para montar o dispositivo.
- No entanto, pode interferir com outros montadores automáticos; Não posso testar isso. Espero que, com o Gnome-VFS ativo, ambos possam tentar fazer a montagem ... se o Gnome-VFS falhar na montagem, ele poderá não configurar um ícone na área de trabalho. A desmontagem do Gnome deve ser possível, mas pode exigir
gksudo
ou semelhante.
Não testei isso na inicialização do sistema, mas a única razão pela qual vejo que ele pode não funcionar é se ele tentar montar a unidade USB antes que o sistema esteja pronto para montagem. Se for esse o caso, você provavelmente precisará de um ajuste adicional no script de montagem. (Estou verificando com ServerFault para ver se há algum conselho, mas não há muito interesse nele por lá.)
Vamos lá, então.
Referências UDEV:
Antecedentes (UDEV? Whuzzat?)
UDEV é o sistema de hotplug do kernel. É o que configura automaticamente os dispositivos e links simbólicos adequados (por exemplo /dev/disk/by-label/<LABEL>
), tanto no momento da inicialização quanto para os dispositivos adicionados enquanto o sistema está em execução.
D-Bus e HAL são usados para enviar eventos de hardware para ouvintes como ambientes de desktop. Portanto, quando você faz login no GNOME e insere um CD ou conecta uma unidade USB, esse evento segue esta cadeia:
kernel -> udev -> dbus -> hal -> gnome-vfs/nautilus (mount)
E pronto, sua unidade é montada. Mas em um sistema sem cabeça, não queremos fazer login para obter os benefícios da montagem automática.
Regras do Udev
Como o UDEV nos permite escrever regras e executar programas na inserção de dispositivos, essa é a escolha ideal. Vamos tirar proveito das regras existentes do Debian / Ubuntu, deixá-las configurar o /dev/disk/by-label/<LABEL>
link simbólico para nós e adicionar outra regra que montará o dispositivo para nós.
As regras da UDEV são mantidas em /etc/udev/rules.d
(e /lib/udev/rules.d
no Karmic) e são processadas em ordem numérica. Qualquer arquivo que não comece com um número é processado após os arquivos numerados. No meu sistema, as regras HAL estão em um arquivo chamado 90-hal.rules
, então eu as incluo 89-local.rules
para que elas sejam processadas antes de chegarem ao HAL. Principalmente, você precisa garantir que essas regras aconteçam após o 60-persistent-storage.rules
. local.rules
pode ser bom o suficiente.
Coloque isso no seu novo arquivo de regras:
# /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"
Verifique se não há espaços após o \
, apenas um newline
( \n
).
Mude SUBSYSTEMS=="usb"
para SUBSYSTEMS=="usb|ieee1394"
para suporte ao Firewire.
Se você deseja que o dispositivo sempre seja de propriedade de um usuário específico, adicione uma OWNER="username"
cláusula. Se você precisar apenas dos arquivos pertencentes a um usuário específico, ajuste o script de montagem.
Lendo a regra
Isso adiciona um programa a ser executado na lista de programas do dispositivo a ser executado. Ele identifica os dispositivos de partição USB <LABEL>
e passa essas informações para um script que executa a montagem. Especificamente, esta regra é correspondente:
ENV{ID_FS_LABEL_ENC}=="?*"
- uma variável de ambiente definida por uma regra anterior do sistema. Não existe para sistemas que não são arquivos, é por isso que procuramos por ele. Na verdade, queremos usar ID_FS_LABEL
o ponto de montagem, mas eu não convenci o UDEV a escapar dele para mim, então vamos deixar o script de montagem lidar com isso.
Esta e outras variáveis de ambiente são obtidas pelo udev usando o vol_id
comando ( descontinuado ). É uma ferramenta útil para ver detalhes rápidos e agradáveis em uma partição:
$ 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"
- corresponder apenas a add
eventos ...
SUBSYSTEMS=="usb"
- corresponda apenas aos dispositivos que estão no barramento USB. Usamos SUBSYSTEMS
aqui porque isso corresponde aos pais do nosso dispositivo; o dispositivo em que estamos interessados será realmente SUBSYSTEM == "scsi". A correspondência com um dispositivo USB pai evita adicionar nosso programa às unidades internas.
RUN+="..."
- não uma correspondência, mas uma ação: adicione este programa à lista de programas a serem executados. Nos argumentos do programa, %k
é expandido para o nome do dispositivo (por exemplo sdc1
, não /dev/sdc1
) e $env{FOO}
obtém o conteúdo da variável de ambiente FOO.
Testando a regra
O primeiro link de referência (acima) é um excelente tutorial UDEV, mas está um pouco desatualizado. Os programas executados para testar suas regras ( udevtest
em particular) foram substituídos pelo udevadm
utilitário catch-all .
Depois de adicionar a regra, conecte seu dispositivo. Aguarde alguns segundos e verifique para qual dispositivo ele foi atribuído:
$ 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
Se a sua unidade removível contiver label_Baz
, ela estará no dispositivo sdc1
. Execute isso e observe a saída no final:
$ 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'
Procure o nome do script da nossa RUN+=
regra nas últimas linhas (terceira da parte inferior neste exemplo). Você pode ver os argumentos que seriam usados para este dispositivo. Você pode executar esse comando agora para verificar se os argumentos estão corretos; se funcionar em sua linha de comando, funcionará automaticamente quando um dispositivo for inserido.
Você também pode monitorar eventos UDEV em tempo real: execute sudo udevadm monitor
(consulte man udevadm
para obter detalhes sobre os comutadores). Em seguida, basta conectar um novo dispositivo e assistir a rolagem de eventos. (Provavelmente um exagero, a menos que você goste de detalhes realmente de baixo nível ...)
Recarregando as regras
Depois de verificar se a regra está sendo lida corretamente, você precisa dizer ao UDEV para recarregar suas regras para que a nova entre em vigor. Use qualquer um desses métodos (se o primeiro não funcionar, o segundo deve ... mas tente o primeiro primeiro):
Roteiro! Na verdade, 2 scripts ...
Aqui está o primeiro script. Como o programa que executamos precisa ser concluído rapidamente, isso apenas gera o segundo script em segundo plano. Coloque isso em /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} &
Aqui está o segundo script. Isso faz um pouco mais de verificação de entrada. Coloque isso /usr/local/sbin/udev-auto-mount.sh
. Você pode ajustar as opções de montagem abaixo. Este script agora lida com a localização da partição LABEL por conta própria; O UDEV envia apenas o nome do DISPOSITIVO.
Se houver um problema ao montar unidades no momento da inicialização , você pode colocar um bom tempo sleep 60
nesse script, para dar tempo ao sistema para que ele avance antes que o script tente montar a unidade.
Dei uma sugestão nos comentários para saber como verificar (execute ps
para verificar se um servidor da web está sendo executado), mas você precisará ajustá-la para o seu sistema. Eu acho que a maioria dos servidores de rede que você pode estar usando seria suficiente para esse fim - nfsd, smbd, apache, etc. O risco, é claro, é que o script mount falhe se o serviço não estiver sendo executado, então talvez testando um a existência de um arquivo específico seria uma solução melhor.
#!/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
Script de limpeza de super bônus!
Mais um script. Tudo isso é desmontar o dispositivo e remover os diretórios do ponto de montagem. Ele assume que ele tem privs para fazer isso, então você precisará executá-lo sudo
. Este script agora pega o ponto de montagem completo na linha de comando, por exemplo:
$ /usr/local/sbin/udev-unmounter.sh "/media/My Random Disk"
Coloque isso em /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