Pour cette clef USB, il a fallu que je comprenne le mécanisme du hotplug. Extrèmement efficace, hal+dbus+udev m’a l’air moins simple…
Le principe: Si le noyau est compilé avec l’option CONFIG_HOTPLUG=y, il a apparait /proc/sys/kernel/hotplug.
Si on fait echo “blop” > /proc/sys/kernel/hotplug, le programme blop sera appelé à chaque évènement hotplug.
Le paquet hotplug est entièrement en bash (et pas en Perl, hein Matt ) et s’avère très puissant. [malheureusement, il s’occupe du chargement des modules et du coup est incompatible avec udev, il faudrait virer la gestion des modules du script]. Un exemple sera beaucoup plus parlant. Supposons qu’on veuille faire un système PNP lors de l’insertion des clefs USB, à savoir
- Entrée dans /etc/fstab
- Création d’un répertoire dans /auto
Bien évidemment cela disparait au retrait.
hotplug appelle les scripts automatiquement de la façon suivante:
Dans /etc/hotplug/usb (car ce sont des périphériques USB), on il cherche les fichier nom.usermap. Si la signature du périphérique si trouve, il appelle le script indiqué sur la ligne. Ce fichier usermap est une suite de ligne de la forme
Pour faire ce fichier, il faut s’inspirer du fichier /etc/hotplug/usb.distmap du paquet hotplug(même format mais avec les noms du modules au lieu du script) pour trouver les lignes correspondantes, par exemple ici, on cherche les clefs USB donc il suffit de récupérer les lignes commençant par usb-storage et remplacer usb-storage par le nom de notre script. Ça donnerait par exemple
$ grep usb-storage /etc/hotplug/usb.distmap | sed -e '1,$s/usb-storage/clef/' > /etc/hotplug/usb/clef.usermap
avec cela, à chaque introduction d’une clef USB, le script /etc/hotplug/usb/clef sera appelé. Les arguments sont passés sous forme de variables d’environnement. On distingue
- DEVICE qui correspond à l’adresse du périphérique USB par exemple
/proc/bus/usb/001/001 - ACTION qui correspond à l’opération faite, essentiellement “add” et “remove”
[Rq: udev aurait un dispositif hotplug avec DEVNAME au lieu de DEVICE, à vérifier] - REMOVER voir plus bas
A partir de ce moment là, on peut faire le travail. Par exemple pour nous, récupérer à partir de dmesg les partitions de la clef USB, changer le groupe de $DEVICE, modifier fstab, etc…
Cela donne le script suivant:
#!/bin/sh
GROUP=disk
if [ ! -f /var/run/hotplug.clef ] ; then
exit 0
fi
if [ "$ACTION" = "add" ] && [ -f "$DEVICE" ]
then
# 1) On change le groupe de périphérique en disk
if getent group $GROUP > /dev/null; then
chmod 660 "$DEVICE"
chown root:$GROUP "$DEVICE"
fi
# 2) On récupère la sortie de dmesg
sleep 4
while ! dmesg | tail -n 1 | grep -q "scan complete" ; do sleep $[2/10]; done
# est ce que c'est du DEVFS ou pas?
if [ `dmesg | tail -n 5 | grep "^ sd.:"`AA = "AA" ] ; then
# merdouille, c'est du devfs avec des /dev/scsi/host et...
# on récupère le périphérique (sdqque chose=
LDEV=`dmesg | tail -n 9 | grep -E "SCSI " | tail -n 1 | sed -e 's/.* \(sd.\):.*$/\1/'`
# on récupère les partitions
LISTE_PARTITION=`dmesg | tail -n 5 | grep -E "^ /dev/scsi/hos.*:" | sed -e 's/.*: //' | sed -e 's/p//g'`
# Si il n'y a pas de partition, c'est une clef montable directement
if echo $LISTE_PARTITION | grep -q -E "^unknow" ; then
LISTE_PARTITION=$LDEV
else
# sinon on récupère les partitons
LI=""
for g in $LISTE_PARTITION ; do
f=$LDEV$g
LI=$LI$f" "
done
LISTE_PARTITION=$LI
fi
# sinon, cas normal non DEVFS
else
LISTE_PARTITION=`dmesg | tail -n 5 | grep "^ sd.:" | sed -e 's/.*: //'`
if ! echo $LISTE_PARTITION | grep -q -E "^sd" ; then
LISTE_PARTITION=`dmesg | tail -n 5 | grep "^ sd.:" | sed -e 's/ *\(sd.*\):.*/\1/'`
fi
fi
# LISTE_PARTITIONS contient les partitions (avec éventuellement sda: au lieu de sda)
LISTE=""
for g in $LISTE_PARTITION ; do
f=`echo $g | sed -e 's/://'`
if echo $f | grep -q -E "^sd" ; then
# on crée le répertoire
mkdir -p /auto/$f
# on crée l'entrée dans /etc/fstab
echo "#$DEVICE" >> /etc/fstab
# si l'entrée existe déjà, la nouvelle entrée est commentée
if grep -q /dev/$f /etc/fstab > /dev/null ; then
echo -n "#" >> /etc/fstab
fi
echo /dev/$f /auto/$f auto user,defaults 0 0 >> /etc/fstab
LISTE2=$LISTE" "$f
LISTE=$LISTE2
fi
done
# voilà, à ce stade c'est fini pour l'insertion
mais il faut s’occuper de la suppression du répertoire, de l’entrée dans /etc/fstab lors du retrait de la clef. C’est là qu’intervient REMOVER. REMOVER contient le nom du script qui sera appelé lors du retrait de la clef par hotplug. En génral, il suffit donc de terminer le script par un ln -s de $REMOVER vers le script. Mais ici, les opérations sont plus compliquées et il faut passer la liste des repertoires à supprimer comme argument. On va donc fabriquer le script REMOVER en lui faisant faire un appel au script clef lui même avec les bonnes variables d’environnement:
echo "#!/bin/sh" >> $REMOVER
echo "DEVICE="$DEVICE" ACTION=\"remove\" DIR=\"""$LISTE"\"" /etc/hotplug/usb/clef" >> $REMOVER
chmod +x $REMOVER
# fin de l'insertion
fi
# fi de ACTION="add"
Il suffit de rajouter maintenant ce que l’on fait à la suppression:
if [ "$ACTION" = "remove" ]
then
# on démonte en force le répertoire (même si c'est trop tard)
umount -f /auto/$DIR 2> /dev/null
# on supprime le répertoire
pushd /auto
rmdir $DIR
popd
# On vire les lignes correspondantes dans /etc/fstab
AVIRER=`echo $DEVICE | sed -e 's|/|\\\\/|g'`
echo "sed -e '/^#"$AVIRER"/{N;d}' /etc/fstab > /tmp/fstab"> /tmp/edite
sh /tmp/edite
mv /tmp/fstab /etc/fstab
# on dégage les modules éventuellement
rmmod sd_mod 2> /dev/null
rmmod usb_storage 2> /dev/null
fi
Voilà, avec tout ça on se retrouve avec un Plug and Play sur les clefs USB très simple d’emploi et efficace.