Criando regras para o udev

A partir do Kernel 2.6, houve uma pequena revolução com relação à detecção de dispositivos no Linux. Graças ao sysfs, sempre que um novo dispositivo é plugado, como um pendrive, ou uma impressora, é adicionada uma entrada dentro da pasta “/sys”.

No caso de um pendrive, por exemplo, é criada a pasta “/sys/block/sda” e, dentro dela, uma subpasta para cada partição dentro do pendrive, como “/sys/block/sda/sda1”. A pasta é criada quanto o pendrive é detectado, e removida quando ele é desconectado do sistema. Graças a isso, um script de detecção precisa apenas monitorar o conteúdo da pasta /sys, não é mais preciso se preocupar em detectar quando o pendrive é conectado ou desconectado, pois o próprio Kernel (com a ajuda do hotplug ou udev) se encarrega disso.

Este é um exemplo de script que “detecta” pendrives e HDs externos ligados na porta USB, criando e removendo os ícones no desktop conforme eles são plugados e removidos. Ele funciona de forma “independente”, baseado apenas nas entradas do diretório /sys, sem utilizar o hotplug nem o udev:

#!/bin/sh
# Script para detectar pendrives e cartões, criando e removendo ícones no desktop
# Deve ser executado pelo root ou por um usuário com o sudo ativo
# Por Carlos E. Morimoto

detecta(){
cd /sys/block/
for i in `ls | grep sd`; do
cd $i; ls | grep $i; cd /sys/block/
done
}

while [ 1 = 1 ]; do

# Adiciona ícones
for i in `detecta`; do
jaexiste=`ls /home/$USER/Desktop/ | grep $i`
if [ -z “$jaexiste” ]; then
sudo mkdir /mnt/$i &>/dev/null
arg=”
FAT=`sudo fdisk -l | grep $i | grep FAT`
[ -n “$FAT” ] && arg=’-t vfat -o umask=000′
NTFS=`sudo fdisk -l | grep $i | grep NTFS`
[ -n “$NTFS” ] && arg=’-t ntfs -o umask=000,ro’
echo “[Desktop Entry]” >> /home/$USER/Desktop/$i.desktop
echo “Exec=sudo mount $arg /dev/$i /mnt/$i; konqueror /mnt/$i; sudo umount /mnt/$i; sync” >> /home/$USER/Desktop/$i.desktop
echo “Icon=usbpendrive_unmount” >> /home/$USER/Desktop/$i.desktop
echo “Name=$i” >> /home/$USER/Desktop/$i.desktop
echo “StartupNotify=true” >> /home/$USER/Desktop/$i.desktop
echo “Type=Application” >> /home/$USER/Desktop/$i.desktop
echo “X-KDE-SubstituteUID=false” >> /home/$USER/Desktop/$i.desktop
echo “X-KDE-Username=” >> /home/$USER/Desktop/$i.desktop
fi
done

# Remove ícones
for i in `ls /home/$USER/Desktop/ | grep sd | cut -d “.” -f 1`; do
presente=`ls /sys/block/sd* | grep $i`
[ -z “$presente” ] && rm -f /home/$USER/Desktop/$i.desktop
done

sleep 6
done

Note que este script é dividido em duas seções: a primeira adiciona ícones para dispositivos “detectados” e a segunda remove ícones de dispositivos que foram desplugados, assim que percebe que a entrada correspondente no “/sys” foi removida. Este script é executado diretamente com o seu login de usuário e fica em loop, atualizando os ícones freqüentemente. O “sleep 6” no final do script determina a periodicidade com que ele é executado.

Na verdade, incluí este script apenas como um exemplo de como, graças ao trabalho feito pelo próprio Kernel, é simples detectar novos periféricos e executar ações apropriadas. é justamente isso que faz o hotplug e, mais recentemente, o udev.

Embora o udev não substitua diretamente o hotplug, as regras para ele permitem fazer quase tudo que era antes possível através dos scripts do hotplug (entre muitas possibilidades novas), o que explica a migração de muitas distribuições. Vamos entender então como criá-las e o que é possível fazer com elas.

As regras do udev vão por padrão na pasta “/etc/udev/rules.d/“. Os arquivos dentro da pasta são executados em ordem numérico-alfabética, de uma forma similar aos arquivos de inicialização dentro da pasta “/etc/rcS.d”, em distribuições baseadas no Debian.

O arquivo “025_libgphoto2.rules” é executando antes do “050_linux-wlan-ng.rules”, por exemplo. Normalmente, os arquivos são links para arquivos dentro da pasta “/etc/udev/”, mas na prática tanto faz seguir o padrão, ou colocar os arquivos diretamente dentro da pasta “/etc/udev/rules.d/”.

Quando um novo dispositivo é plugado, o udev vasculha todos os arquivos, na ordem especificada, até encontrar alguma regra que se aplique a ele. Ele pára assim que encontra a primeira, de forma que, se o mesmo dispositivo for referenciado em duas ou mais regras diferentes, vale a primeira. É por isso que a ordem dos arquivos é importante.

Para adicionar suas regras, crie um novo arquivo dentro da pasta, como “/etc/udev/rules.d/01-regras.rules“. O importante é que o arquivo use a extensão “.rules” (caso contrário ele não será processado) e comece com um número mais baixo que os demais, para que seja processado primeiro. Isso garante que suas regras não sejam ignoradas em favor das regras padrão incluídas na distribuição.

Dentro do arquivo, cada regra forma uma única linha. Este é um exemplo de regra, que executa um script sempre que um pendrive ou HD USB é conectado:

BUS=”usb”, ACTION==”add”, KERNEL==”sd??”, NAME=”%k”, RUN+=”/usr/local/bin/detectar-usb”

Os parâmetros usados são os seguintes:

BUS=”usb”: Define que a regra se aplica apenas a dispositivos USB. Você pode usar também BUS=”scsi”, BUS=”ide” e BUS=”pci” para criar regras para outros tipos de dispositivos. Usando o BUS=”pci”, por exemplo, você pode criar devices personalizados para sua placa de som, rede ou modem PCI.

ACTION==”add”: A regra se aplica quando um dispositivo for conectado. Existe também o parâmetro “remove”, que se aplica quando o dispositivo é desconectado. Sem especificar o ACTION, a regra é executada apenas quando o dispositivo é conectado, ou assim que o udev o detecta durante o boot, mas nada é feito quando ele é desconectado.

KERNEL=”sd??”: Aqui estamos sendo mais específicos. A regra se aplica apenas a dispositivos cujo device comece com “sd”, seguido por duas letras/números, como “/dev/sda1” ou “/dev/sdb2”. Como especificamos que a regra só se aplica a dispositivos USB, ela não vai ser disparada caso seja conectado um HD serial ATA ou SCSI, por exemplo.

NAME=”%k”: O parâmetro “NAME” é responsável por criar o device dentro da pasta “/dev”, através do qual o dispositivo será acessado. O parâmetro “%k” corresponde ao nome dado pelo Kernel, e pode ser usado quando você não quiser mudar o nome do dispositivo. Se a partição no pendrive é vista como “sda1”, o dispositivo será simplesmente “/dev/sda1”, como de costume.
Se, por outro lado, você quiser que seu pendrive seja visto como “/dev/pendrive”, ou qualquer outro nome especial, indique isso na regra, como em: NAME=”pendrive”. Se quiser que o device seja “/dev/block/pendrive”, use: NAME=”block/pendrive”.

RUN+=: Aqui especificamos uma ação para a regra. Neste caso estou usando o parâmetro “RUN+=”, que executa um script externo, que pode se encarregar de montar o dispositivo, adicionar um ícone no desktop ou outra ação qualquer.

Você pode também usar o parâmetro “SYMLINK=” para criar “apelidos” para os dispositivos, fazendo com que além do device padrão, ele responda também por outro definido por você. Você pode usar, por exemplo os parâmetros NAME=”%k” SYMLINK=”pendrive” para fazer com que, além de ficar acessível pelo dispositivo “/dev/sda1”, seu pendrive possa ser acessado pelo “/dev/pendrive”.

Esta é a regra irmã da primeira, que executa outro script quando o dispositivo é desconectado. Ela pode ser usada para reverter os passos executados pelo primeiro script, como, por exemplo, remover o ícone no desktop criado pelo primeiro. A regra para remover deve vir sempre antes da regra para adicionar:

ACTION==”remove”, KERNEL==”sd*”, RUN+=”/usr/local/bin/remover-usb”

Note que a regra para remover tem menos parâmetros que a para adicionar. Aqui estou especificando que o script “/usr/local/bin/remover-usb” deve ser executado sempre que algum dispositivo cujo nome comece com “sd” (sda, sdb, etc., incluindo todas as partições) for removido.

Vamos então a um exemplo prático, com as regras e scripts que usei no Kurumin 6.0, para que detectasse pendrives e HDs USB, criando ícones no desktop quando eles são plugados e removendo os ícones automaticamente.

O primeiro passo é criar um arquivo com as regras. No meu caso, criei o arquivo “/etc/udev/usb-storage.rules” e o link “/etc/udev/rules.d/010-storage.rules” apontando para ele.

Estas são as duas regras adicionadas no arquivo:

ACTION==”remove”, KERNEL==”sd*”, RUN+=”/usr/local/bin/detectar-cartao3″

BUS=”usb”, ACTION==”add”, KERNEL==”sd??”, NAME{all_partitions}=”%k”, RUN+=”/usr/local/bin/detectar-cartao3″

Como pode ver, o script “/usr/local/bin/detectar-cartao3” é executado tanto quando um pendrive é inserido quanto removido, pois optei por colocar as duas funções dentro do mesmo script.

A opção NAME{all_partitions}=”%k” faz com que o udev crie os dispositivos “/dev/sdx1” até “/dev/sdx15”, incluindo o “/dev/sdx”, ao invés de criar apenas devices para as partições encontradas. Sem isso, o udev criará o device “dev/sdx1”, mas não o “/dev/sdx” (ou vice-versa, de acordo com a regra usada), fazendo com que você consiga acessar os dados na partição, mas não consiga usar o cfdisk ou outro particionador para reparticionar o dispositivo.

O script executado pela regra é bem similar ao que incluí no início do tópico, mas ele agora inclui uma função para detectar os usuários logados e criar um ícone para o pendrive no desktop de cada um. Ao ser clicado, o ícone executa um script separado, o “montar-pendrive” (que incluo a seguir) que detecta se o usuário está com o sudo ativado e monta o pendrive usando o sudo ou o kdesu.

#!/bin/sh
# Por Carlos E. Morimoto

# Esta é a função que lista os dispositivos e partições presentes, gerando uma lista como:
# sda1 sda2 sdb1 sdc1 (um por linha)

detecta(){
cd /sys/block/
for p in `ls | grep sd`; do
cd $p; ls | grep $p; cd /sys/block/
done
}

# Função que lista os usuários logados no KDE:
userfu(){
ls /tmp/ | grep “kde” | sed -s ‘/root/D’ | cut -f “2” -d “-“
}

# Cria o ícone no desktop de cada usuário logado no kde:
for u in `userfu`; do
usuario=”$u”

for i in `detecta`; do
jaexiste=`ls /home/$usuario/Desktop/ | grep $i`
mkdir /mnt/$i &>/dev/null
chmod 777 /mnt/$i &>/dev/null
myfs=”

# Detecta se a partição é FAT ou NTFS
de=`echo $i | cut -c 1-3`
FAT=`fdisk -l /dev/$de | grep $i | grep FAT`
[ -n “$FAT” ] && myfs=’fat’
NTFS=`fdisk -l /dev/$de | grep $i | grep NTFS`
[ -n “$NTFS” ] && myfs=’ntfs’

# Cria o ícone apenas se ele já não existir
if [ -z “$jaexiste” ]; then
echo “[Desktop Entry]” >> /home/$usuario/Desktop/$i.desktop
echo “Encoding=UTF-8” >> /home/$usuario/Desktop/$i.desktop
echo “Exec=montar-pendrive $i $myfs; sync; sync” >> /home/$usuario/Desktop/$i.desktop
echo “Icon=usbpendrive_unmount” >> /home/$usuario/Desktop/$i.desktop
echo “Name=$i” >> /home/$usuario/Desktop/$i.desktop
echo “StartupNotify=true” >> /home/$usuario/Desktop/$i.desktop
echo “Type=Application” >> /home/$usuario/Desktop/$i.desktop
echo “X-KDE-SubstituteUID=false” >> /home/$usuario/Desktop/$i.desktop
echo “X-KDE-Username=” >> /home/$usuario/Desktop/$i.desktop
fi
done

# Remove ícones de dispositivos removidos, do desktop de cada usuário:
for i in `ls /home/$usuario/Desktop/ | grep sd | cut -c 1-3`; do
presente=`ls /sys/block/ 2>/dev/null | grep $i`
[ -z “$presente” ] && rm -f /home/$usuario/Desktop/$i*.desktop
done

done

# O k-sync sincroniza os dados periodicamente, evitando perda de dados
# se o pendrive é removido sem desmontar.

killall k-sync
k-sync &
exit 0

Este é o código do script “montar-pendrive”

#!/bin/sh
# Usado pelo script detectar-cartao3
# Permite montar uma partição em um pendrive. Detecta se o sudo está ativo ou não.
# Usa o kdesu quando o sudo está desativado.
# Exemplo de uso: “montar pendrive sda1 fat”
# Por Carlos E. Morimoto

sudoativo=`sudo whoami`

if [ “$sudoativo” != “root” ]; then
arg=”
[ “$2” = “fat” ] && arg=’-t vfat -o umask=000′
[ “$2” = “ntfs” ] && arg=’-t ntfs -o umask=000,ro’
kdesu “mount $arg /dev/$1 /mnt/$1”; konqueror /mnt/$1; sync; kdesu “umount /mnt/$1”

else
arg=”
[ “$2” = “fat” ] && arg=’-t vfat -o umask=000′
[ “$2” = “ntfs” ] && arg=’-t ntfs -o umask=000,ro’
sudo mount $arg /dev/$1 /mnt/$1; konqueror /mnt/$1; sync; sudo umount /mnt/$1
fi

E este o do k-sync, que simplesmente executa o comando “sync” a cada 30 segundos:

#!/bin/sh
while [ “1” != “2” ]; do
sync; sleep 30
done

Você pode fazer também com que as regras ativem serviços de sistema, quando determinados dispositivos são conectados. Por exemplo, instalando os pacotes “bluez-utils”, “bluez-firmware” e “kdebluetooth” você passa a ter um suporte bastante completo a dispositivos bluetooth de uma maneira geral. Mas, para que tudo funcione, é necessário que o serviço “bluez-utils” esteja ativado. A regra abaixo faz com que o serviço seja automaticamente ativado quando algum dispositivo bluetooth é conectado:

ACTION==”add”, KERNEL==”bluetooth”, RUN+=”/etc/init.d/bluez-utils start”

Assim como no hotplug, você pode também criar regras que se aplicam a dispositivos específicos. Neste caso, ao invés de criar uma regra genérica, que se aplica a qualquer dispositivo USB, qualquer device que comece com “sd”, ou qualquer coisa do gênero, usamos alguma informação que seja específica do dispositivo em questão.

Por exemplo, sempre que conecto a gaveta de HD USB que usei no exemplo do hotplug, é criada a entrada “/sys/block/sda”, contendo várias informações sobre o dispositivo, incluindo as partições e capacidade de cada uma. No arquivo “/sys/block/sda/device/model” aparece o modelo “ATMR04-0”.

Você pode ver de uma vez todas as informações disponível sobre o dispositivo usando o comando “udevinfo -a -p”, seguido da pasta no diretório “/sys/block” ou “/sys/class” criada quando ele é conectado, como em:

$ udevinfo -a -p /sys/block/sda/

Se souber qual é o device usado por ele, você pode fazer a busca por ele, como em “udevinfo -q path -n /dev/sda”. Este comando devolve o caminho para ele dentro do diretório “/sys”.

O udevinfo retorna sempre uma grande quantidade de informações. Todas elas estão disponíveis dentro do diretório /sys, o udevinfo apenas as reúne num único lugar. No meu caso, algumas linhas interessantes são:

DRIVER==”usb-storage”
SYSFS{product}==”USB TO IDE”
SYSFS{size}==”117210240″
SYSFS{serial}==”0000:00:13.2″

Ao plugar um joystick USB, é criada a pasta “/sys/class/input/js0”. Ao rodar o comando “udevinfo -a -p /sys/class/input/js0” para ver as propriedades, aparecem (entre várias outras coisas), as linhas:

SYSFS{manufacturer}==”Logitech”
SYSFS{product}==”WingMan Precision USB”

Como pode ver, é sempre fácil encontrar alguma informação que se aplica apenas ao dispositivo em particular, como o model, product ou serial. Qualquer uma delas pode ser usada para compor a regra.

Esta é uma regra simples para o joystick, que cria o link “/dev/joypad”, apontando para o dispositivo real do joystick.

BUS="usb", SYSFS{product}=="WingMan Precision USB", NAME="%k", SYMLINK="joypad"

No caso da gaveta, poderíamos criar uma regra um pouco mais complexa, que além de criar um link, executaria um script de backup sempre que ela é conectada:

BUS="usb", SYSFS{product}=="USB TO IDE", KERNEL="sd?1", NAME="%k", SYMLINK="gavetausb" RUN+="/usr/local/bin/backup"

Note que aqui, além do “SYSFS{product}”, que identifica a gaveta USB, estou usando também o parâmetro KERNEL=”sd?1″. Isso faz com que o link aponte para a primeira partição do HD (como “/dev/sda1”), ao invés de simplesmente apontar para o dispositivo (“/dev/sda”). No caso de HDs e pendrives, isso é importante, pois você sempre monta a partição, não o dispositivo em si.

Sempre que a gaveta é plugada, a regra executa o script “/usr/local/bin/backup”. Poderíamos usar o mesmo script de backup que vimos no tópico sobre o hotplug, modificando apenas o device. Ao invés de acessar o HD na gaveta através do “/dev/sda1”, passaríamos a usar o link “/dev/gavetausb”, que é o endereço personalizado.

A principal diferença aqui é que, mesmo que existam outros pendrives e outros HD USB plugados no micro, o device da gaveta será sempre “/dev/gavetausb”, de forma que o script de backup poderá ser executado com bem mais segurança. Esta possibilidade de ter links personalizados e fixos é justamente a maior vantagem do udev.

No caso de dispositivos com várias partições, você pode usar o parâmetro “NAME{all_partitions}=”%k” que comentei anteriormente. Ele faz com que o udev crie devices para todas as partições possíveis dentro do dispositivo, permitindo que você monte-as manualmente, à moda antiga.

Mais uma dica é que, normalmente, novos dispositivos são criados com permissão de escrita somente para o root, até que você as modifique manualmente, usando o comando “chmod”. O udev permite ajustar as permissões, modificando o dono, grupo e permissões de acesso do dispositivo. Isso pode ser usado para que scanners e outros dispositivos fiquem automaticamente acessíveis para todos os usuários do sistema, evitando dores de cabeça em geral, ou fazer com que um pendrive ou outro dispositivo em particular fique disponível apenas para um determinado usuário, mas não para os demais.

Para fazer com que um palm fique disponível apenas para o “joao”, você poderia usar a regra:

SYSFS{idProduct}==”00da”, SYSFS{manufacturer}==”Palm_Inc.”, NAME=”%k”, SYMLINK=”pendrive”, OWNER=”joao”, MODE=”0600″

Você pode encontrar mais alguns documentos sobre o tema, incluindo mais exemplos no:
http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html.

Sobre o Autor

Redes Sociais:

Deixe seu comentário

X