I Introduction
L’objectif est de pouvoir lancer un script à l’insertion d’une clef USB. Script pouvant effectuer une sauvegarde de données, une copie ou lancer un processus particulier.
II Présentation rapide de UDEV
UDEV est mis en œuvre depuis les versions Linux supérieures à 2.6 (cat /proc/version). C’est un gestionnaire de périphériques qui permet la détection voire l’installation lorsqu’un nouveau est inséré.
UDEV utilise Sysfs également mis en œuvre à partir du noyau 2.6. Ce dernier inscrit dans le répertoire /sys tous les périphériques détectés par le noyau linux.
Lorsqu’un disque est détecté, UDEV fixe plusieurs noms. Ils sont placés dans /dev/disk et pour chaque référence un lien symbolique est fait vers le fichier périphérique :
xavior@mon_pc:~/scripts/udev$ tree /dev/disk/
/dev/disk/
├── by-id
│ ├── nvme-CT1000P5PSSD8_2135313D2BC8 -> ../../nvme0n1
│ ├── nvme-CT1000P5PSSD8_2135313D2BC8-part1 -> ../../nvme0n1p1
│ ├── nvme-CT1000P5PSSD8_2135313D2BC8-part2 -> ../../nvme0n1p2
│ ├── nvme-CT1000P5PSSD8_2135313D2BC8-part3 -> ../../nvme0n1p3
│ ├── nvme-CT1000P5PSSD8_2135313D2BC8-part4 -> ../../nvme0n1p4
│ ├── nvme-CT1000P5PSSD8_2135313D2BC8-part5 -> ../../nvme0n1p5
│ ├── nvme-eui.000000000000000100a07521313d2bc8 -> ../../nvme0n1
│ ├── nvme-eui.000000000000000100a07521313d2bc8-part1 -> ../../nvme0n1p1
│ ├── nvme-eui.000000000000000100a07521313d2bc8-part2 -> ../../nvme0n1p2
│ ├── nvme-eui.000000000000000100a07521313d2bc8-part3 -> ../../nvme0n1p3
│ ├── nvme-eui.000000000000000100a07521313d2bc8-part4 -> ../../nvme0n1p4
│ ├── nvme-eui.000000000000000100a07521313d2bc8-part5 -> ../../nvme0n1p5
│ ├── usb-General_USB_Flash_Disk_AA00000000013411-0:0 -> ../../sda
│ ├── usb-General_USB_Flash_Disk_AA00000000013411-0:0-part1 -> ../../sda1
│ └── usb-General_USB_Flash_Disk_AA00000000013411-0:0-part2 -> ../../sda2
├── by-label
│ ├── EFI-USB -> ../../sda1
│ └── GRUB-SOS -> ../../sda2
├── by-partlabel
│ ├── Basic\x20data\x20partition -> ../../nvme0n1p3
│ ├── EFI-USB -> ../../sda1
│ ├── EFI\x20system\x20partition -> ../../nvme0n1p1
│ ├── GRUB-SOS -> ../../sda2
│ └── Microsoft\x20reserved\x20partition -> ../../nvme0n1p2
├── by-partuuid
│ ├── 3eb7f155-14ef-47c5-9b9c-431a2a1288a0 -> ../../nvme0n1p2
│ ├── 3f6e7377-15e6-4d8e-8b78-5a3e60c7fc66 -> ../../sda1
│ ├── 7618d45d-2513-46c4-8c4e-85159a05db05 -> ../../nvme0n1p5
│ ├── 98a46934-67d7-4d04-8255-3672e33994c4 -> ../../nvme0n1p3
│ ├── a6881757-9e91-4f77-a28f-75c63e88e381 -> ../../nvme0n1p4
│ ├── ab7d9f47-7c39-4956-86d8-546bcd7db63c -> ../../nvme0n1p1
│ └── f6c7c1ae-1fd3-4819-8a8e-f3a16f9def3c -> ../../sda2
├── by-path
│ ├── pci-0000:00:14.0-usb-0:3:1.0-scsi-0:0:0:0 -> ../../sda
│ ├── pci-0000:00:14.0-usb-0:3:1.0-scsi-0:0:0:0-part1 -> ../../sda1
│ ├── pci-0000:00:14.0-usb-0:3:1.0-scsi-0:0:0:0-part2 -> ../../sda2
│ ├── pci-0000:03:00.0-nvme-1 -> ../../nvme0n1
│ ├── pci-0000:03:00.0-nvme-1-part1 -> ../../nvme0n1p1
│ ├── pci-0000:03:00.0-nvme-1-part2 -> ../../nvme0n1p2
│ ├── pci-0000:03:00.0-nvme-1-part3 -> ../../nvme0n1p3
│ ├── pci-0000:03:00.0-nvme-1-part4 -> ../../nvme0n1p4
│ └── pci-0000:03:00.0-nvme-1-part5 -> ../../nvme0n1p5
└── by-uuid
├── 06F242C3F242B729 -> ../../nvme0n1p4
├── 1e6402aa-22a8-4774-ba35-ea349e77b9a0 -> ../../nvme0n1p2
├── 3b9c95e0-5e8c-4f2b-a71d-3d247c311e9b -> ../../nvme0n1p5
├── 46484D6A5D2880DC -> ../../sda2
├── 4CB8-B0FE -> ../../nvme0n1p1
├── 5055-11ED -> ../../sda1
└── 7466C75E66C7202A -> ../../nvme0n1p3
Pour cela UDEV utilise des règles déjà existantes à l’installation du système. Elles se trouvent dans le répertoire /lib/udev/rules.d :
xavior@mon_pc:/lib/udev/rules.d$ ls
39-usbmuxd.rules 60-serial.rules 75-net-description.rules 80-iio-sensor-proxy.rules
40-usb-media-players.rules 60-tpm-udev.rules 75-probe_mtd.rules 80-libinput-device-groups.rules
40-usb_modeswitch.rules 60-virtualbox-dkms.rules 77-mm-broadmobi-port-types.rules 80-mm-candidate.rules
40-vm-hotadd.rules 60-virtualbox-guest-utils.rules 77-mm-cinterion-port-types.rules 80-net-setup-link.rules
50-apport.rules 60-virtualbox.rules 77-mm-dell-port-types.rules 80-udisks2.rules
50-firmware.rules 61-autosuspend-manual.rules 77-mm-dlink-port-types.rules 81-net-dhcp.rules
50-udev-default.rules 61-gdm.rules 77-mm-ericsson-mbm.rules 84-nm-drivers.rules
55-dm.rules 61-gnome-settings-daemon-rfkill.rules 77-mm-fibocom-port-types.rules 85-brltty.rules
55-ippusbxd.rules 61-persistent-storage-android.rules 77-mm-foxconn-port-types.rules 85-hdparm.rules
56-hpmud.rules 64-btrfs.rules 77-mm-gosuncn-port-types.rules 85-hplj10xx.rules
56-lvm.rules 64-xorg-xkb.rules 77-mm-haier-port-types.rules 85-nm-unmanaged.rules
60-autosuspend-chromiumos.rules 65-libwacom.rules 77-mm-huawei-net-port-types.rules 85-regulatory.rules
60-block.rules 66-snapd-autoimport.rules 77-mm-longcheer-port-types.rules 90-alsa-restore.rules
60-cdrom_id.rules 69-cd-sensors.rules 77-mm-mtk-port-types.rules 90-bolt.rules
60-crda.rules 69-libmtp.rules 77-mm-nokia-port-types.rules 90-console-setup.rules
60-drm.rules 69-lvm-metad.rules 77-mm-pcmcia-device-blacklist.rules 90-fwupd-devices.rules
60-evdev.rules 69-wacom.rules 77-mm-qdl-device-blacklist.rules 90-libgpod.rules
60-fido-id.rules 70-joystick.rules 77-mm-quectel-port-types.rules 90-libinput-fuzz-override.rules
60-inputattach.rules 70-mouse.rules 77-mm-sierra.rules 90-nm-thunderbolt.rules
60-input-id.rules 70-nvmf-autoconnect.rules 77-mm-simtech-port-types.rules 90-pulseaudio.rules
60-libfprint-2.rules 70-power-switch.rules 77-mm-telit-port-types.rules 95-cd-devices.rules
60-libgphoto2-6.rules 70-printers.rules 77-mm-tplink-port-types.rules 95-dm-notify.rules
60-libsane.rules 70-spice-vdagentd.rules 77-mm-ublox-port-types.rules 95-upower-csr.rules
60-pcmcia.rules 70-touchpad.rules 77-mm-usb-device-blacklist.rules 95-upower-hidpp.rules
60-persistent-alsa.rules 70-u2f.rules 77-mm-usb-serial-adapters-greylist.rules 95-upower-hid.rules
60-persistent-input.rules 70-uaccess.rules 77-mm-x22x-port-types.rules 95-upower-wup.rules
60-persistent-storage-dm.rules 71-power-switch-proliant.rules 77-mm-zte-port-types.rules 96-e2scrub.rules
60-persistent-storage.rules 71-seat.rules 78-graphics-card.rules 97-hid2hci.rules
60-persistent-storage-tape.rules 71-u-d-c-gpu-detection.rules 78-sound-card.rules 99-systemd.rules
60-persistent-v4l.rules 73-seat-late.rules 80-debian-compat.rules
60-sensor.rules 73-special-net-names.rules 80-drivers.rules
Pour intégrer ses propres règles, le répertoire suivant est à utiliser : /etc/udev/rules.d
Le nom de fichier doit avoir l’extension ».rules ». Les fichiers sont pris en compte par ordre alphabétique. Ainsi le nom suivant pouvant être utilisé « 10-local.rules
« .
III Ecriture de règles UDEV personnalisées
III.1 Une règle
Un règle comporte des clefs qui permettent :
- de rechercher spécifiquement un périphérique :
- « KERNEL »
- « SUBSYSTEM »
- « DRIVER »
- « ENV{key} » –> clé/valeur de UDEV
- « ATTR {key} » –> clé/valeur de Sysfs
- « ATTR{filename} »
- « ACTION »
- « DEVPATH »
- « SYMLINK »
- « SYSCTL{kernel parameter} »
- « TAG »
- …
- de définir des actions :
- « NAME »
- « SYMLINK »
- « OWNER, GROUP, MODE »
- « SECLABEL »
- « ATTR{Key} »
- « SYSCTL{Key} »
- « ENV{key} »
- « RUN{type} »
- …
III.2 Les opérateurs
Les opérateurs permettent de définir soit une recherche soit une affectation et qui sont :
Opérateur | Signification |
---|---|
== | égalité |
!= | non égalité |
= | affecte valeur |
-= | enlève valeur |
:= | affecte valeur puis non modifiable ensuite |
+= | ajoute une nouvelle valeur |
III.3 Exemple de règles simples permettant l’exécution d’un script
III.3.1 Exemple 1 : détection d’ajout d’un périphérique
Première règle pour voir ce qui se passe :
ACTION=="add", RUN+="/bin/sh -c '/bin/echo %k : %p >> /home/xavior/events.log'"
Cette règle va loguer tout les ajouts de périphériques :
- filtre : « ACTION== »add »
- action : RUN+= »/bin/sh -c ‘/bin/echo %k : %p >> /home/xavior/events.log' »
Pour être sûr que la règle soit prise en compte :
sudo udevadm control --reload-rules
ce qui donne le résultat suivant en branchant une clef USB :
xavior@mon_pc:~$ cat events.log
scsi_tmf_0 : /devices/virtual/workqueue/scsi_tmf_0
2-3 : /devices/pci0000:00/0000:00:14.0/usb2/2-3
2-3:1.0 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0
host0 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0
host0 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/scsi_host/host0
target0:0:0 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0
0:0:0:0 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0
0:0:0:0 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/bsg/0:0:0:0
sg0 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/scsi_generic/sg0
0:0:0:0 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/scsi_disk/0:0:0:0
0:0:0:0 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/scsi_device/0:0:0:0
8:0 : /devices/virtual/bdi/8:0
sda : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda
sda1 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda1
sda2 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda2
8:2-fuseblk : /devices/virtual/bdi/8:2-fuseblk
Comme on peut le voir, des paramètres ont été récupérés de UDEV et affiché par le script shell:
- « %k » –> le nom donnée par le Kernel pour le périphérique trouvé
- « %p » –> le chemin du périphérique
Il existe une vingtaine de variables utilisables, toutes renseignées dans le « man udev«
III.3.2 Exemple 2 : détection d’ajout d’une clef USB
Nous allons maintenant voir comment filtrer que sur l’insertion de clef USB. Pour cela nous allons ajouter d’autres filtres pour affiner la recherche.
ACTION=="add", KERNEL=="sd*" ,RUN+="/bin/sh -c '/bin/echo %N : $name : %P : %k : %p >> /home/xavior/events2.log'"
Après avoir branché 2 clefs USB nous obtenons le résultat suivant :
xavior@mon_pc~$ cat events2.log
/dev/sda : sda : : sda : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda
/dev/sda1 : sda1 : sda : sda1 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda1
/dev/sda2 : sda2 : sda : sda2 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda2
/dev/sdb : sdb : : sdb : /devices/pci0000:00/0000:00:14.0/usb2/2-6/2-6:1.0/host1/target1:0:0/1:0:0:0/block/sdb
/dev/sdb1 : sdb1 : sdb : sdb1 : /devices/pci0000:00/0000:00:14.0/usb2/2-6/2-6:1.0/host1/target1:0:0/1:0:0:0/block/sdb/sdb1
Nous obtenons ici les clefs USB (sda et sdb) ainsi que les partitions associées…
III.4 Des outils pour cibler plus précisément un périphérique
UDEV fonctionne via les messages envoyés par le noyau. Des outils sont disponibles pour les visualiser ou connaître les propriétés des périphériques installés.
III.4.1 « udevman monitor » : visualiser les messages envoyé par noyau pour les périphériques
Pour visualiser en temps réel ces messages il peut être utilisé la commande « udevadm monitor« . Ainsi pour voir ces messages :
udevadm monitor -k
ce qui donne :
monitor will print the received events for:
KERNEL - the kernel uevent
KERNEL[75339.500524] add /devices/pci0000:00/0000:00:14.0/usb2/2-3 (usb)
KERNEL[75339.503965] add /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0 (usb)
KERNEL[75339.504052] add /devices/virtual/workqueue/scsi_tmf_0 (workqueue)
KERNEL[75339.504137] add /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0 (scsi)
KERNEL[75339.504157] add /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/scsi_host/host0 (scsi_host)
KERNEL[75339.504196] bind /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0 (usb)
KERNEL[75339.504238] bind /devices/pci0000:00/0000:00:14.0/usb2/2-3 (usb)
KERNEL[75340.783708] add /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0 (scsi)
KERNEL[75340.783739] add /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0 (scsi)
KERNEL[75340.783763] add /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/scsi_device/0:0:0:0 (scsi_device)
KERNEL[75340.783798] add /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/scsi_disk/0:0:0:0 (scsi_disk)
KERNEL[75340.783823] add /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/scsi_generic/sg0 (scsi_generic)
KERNEL[75340.783856] add /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/bsg/0:0:0:0 (bsg)
KERNEL[75340.802225] add /devices/virtual/bdi/8:0 (bdi)
KERNEL[75340.811131] add /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda (block)
KERNEL[75340.811170] add /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda1 (block)
KERNEL[75340.811203] add /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda2 (block)
KERNEL[75340.840671] bind /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0 (scsi)
KERNEL[75341.052677] add /devices/virtual/bdi/8:2-fuseblk (bdi)
Pour connaître les propriétés associées à chaque signal :
udevadm monitor -k -p
Ce qui donne :
monitor will print the received events for:
KERNEL - the kernel uevent
KERNEL[75441.408389] add /devices/pci0000:00/0000:00:14.0/usb2/2-3 (usb)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb2/2-3
SUBSYSTEM=usb
DEVNAME=/dev/bus/usb/002/084
DEVTYPE=usb_device
PRODUCT=90c/1000/1100
TYPE=0/0/0
BUSNUM=002
DEVNUM=084
SEQNUM=9090
MAJOR=189
MINOR=211
KERNEL[75441.411726] add /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0 (usb)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0
SUBSYSTEM=usb
DEVTYPE=usb_interface
PRODUCT=90c/1000/1100
TYPE=0/0/0
INTERFACE=8/6/80
MODALIAS=usb:v090Cp1000d1100dc00dsc00dp00ic08isc06ip50in00
SEQNUM=9091
...
III.4.2 « udevman info » : retrouver les propriétés et valeurs d’un périphérique
Il est également possible de retrouver toutes les clefs et valeurs d’un périphérique déjà connecté. Pour cela on utilise toujours la commande « udevadm » avec le paramètre ‘info‘ et le périphérique recherché
udevadm info /dev/sda
Ce qui donne :
P: /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda
N: sda
L: 0
S: disk/by-path/pci-0000:00:14.0-usb-0:3:1.0-scsi-0:0:0:0
S: disk/by-id/usb-General_USB_Flash_Disk_AA00000000013411-0:0
E: DEVPATH=/devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda
E: DEVNAME=/dev/sda
E: DEVTYPE=disk
E: MAJOR=8
E: MINOR=0
E: SUBSYSTEM=block
E: USEC_INITIALIZED=75442785275
E: ID_VENDOR=General
E: ID_VENDOR_ENC=General\x20
E: ID_VENDOR_ID=090c
E: ID_MODEL=USB_Flash_Disk
E: ID_MODEL_ENC=USB\x20Flash\x20Disk\x20\x20
E: ID_MODEL_ID=1000
E: ID_REVISION=1100
E: ID_SERIAL=General_USB_Flash_Disk_AA00000000013411-0:0
E: ID_SERIAL_SHORT=AA00000000013411
E: ID_TYPE=disk
E: ID_INSTANCE=0:0
E: ID_BUS=usb
E: ID_USB_INTERFACES=:080650:
E: ID_USB_INTERFACE_NUM=00
E: ID_USB_DRIVER=usb-storage
E: ID_PATH=pci-0000:00:14.0-usb-0:3:1.0-scsi-0:0:0:0
E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_3_1_0-scsi-0_0_0_0
E: ID_PART_TABLE_UUID=ae60942d-dbbf-4514-9585-9ee78cf6b983
E: ID_PART_TABLE_TYPE=gpt
E: DEVLINKS=/dev/disk/by-path/pci-0000:00:14.0-usb-0:3:1.0-scsi-0:0:0:0 /dev/disk/by-id/usb-General_USB_Flash_Disk_AA00000000013411-0:0
E: TAGS=:systemd:
Pour la colonne de gauche :
- E : une propriété –> il faudra utiliser ENV{} pour filtrer sur ce type de clef ;
- à savoir que les propriétés commençant par ID_xxxx sont ajoutées par des règles UDEV
- N : nom du périphérique proposé par le noyau ;
- S : lien symbolique créé vers le périphérique ;
- P: chemin vers sysfs (/sys/…).
Maintenant pour avoir plus d’informations comprenant l’ensemble des propriétés du device :
udevadm info -a /dev/sda
Ce qui donne :
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda':
KERNEL=="sda"
SUBSYSTEM=="block"
DRIVER==""
ATTR{removable}=="1"
ATTR{range}=="16"
ATTR{ext_range}=="256"
ATTR{stat}==" 260 7 9088 228 0 0 0 0 0 192 228 0 0 0 0 0 0"
ATTR{hidden}=="0"
ATTR{events_async}==""
ATTR{events}=="media_change"
ATTR{inflight}==" 0 0"
ATTR{ro}=="0"
ATTR{capability}=="51"
ATTR{alignment_offset}=="0"
ATTR{size}=="15720448"
ATTR{events_poll_msecs}=="-1"
ATTR{discard_alignment}=="0"
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0':
KERNELS=="0:0:0:0"
SUBSYSTEMS=="scsi"
DRIVERS=="sd"
...
Les valeurs ATTR{xxx} sont issues des proriétés fournies par Sysfs
III.5 Exemple de sélection d’un périphérique particulier
Après avoir vu comment obtenir l’ensemble des propriétés d’un périphérique nous allons voir comment définir nos règles Udev pour sélectionner précisément un périphérique, ici une clef USB. Une fois la clef trouvée, cela actionnera un script.
III.5.1 Cibler précisément une clef USB par son numéro de série
Nous allons voir comment cibler précisément une clef USB unique.
ACTION=="add",KERNEL=="sd[a-z]" ATTRS{serial}=="AA00000000015023", RUN+="/bin/sh -c '/bin/echo %N : $name : %P : %k : %p >> /home/xavior/events3.log'"
Explications :
- ACTION== »add » –> ne sélectionne que les évènements d’ajout de périphérique
- KERNEL== »sd[a-z] » –> ne sélectionne que la clef physique (et non les partitions)
- ATTRS{serial}== »AA00000000015023″ –> filtre sur le numéro de série de la clef (via les propriétés Sysfs)
Ce qui nous donne :
/dev/sdb : sdb : : sdb : /devices/pci0000:00/0000:00:14.0/usb2/2-6/2-6:1.0/host1/target1:0:0/1:0:0:0/block/sdb
III.5.2 Cibler tout type de clef USB insérée
Il suffit de reprendre la règle d’avant et d’enlever un filtre comme suivant :
ACTION=="add",KERNEL=="sd[a-z]", RUN+="/bin/sh -c '/bin/echo %N : $name : %P : %k : %p >> /home/xavior/events3.log'"
Ce qui donne après insertion de 2 clefs USB différentes :
/dev/sda : sda : : sda : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda
/dev/sdb : sdb : : sdb : /devices/pci0000:00/0000:00:14.0/usb2/2-6/2-6:1.0/host1/target1:0:0/1:0:0:0/block/sdb
III.5.3 Cibler toutes les partitions d’une clef USB insérée
Maintenant on souhaite avoir seulement le nom des partitions d’une clef usb :
ACTION=="add",KERNEL=="sd*[0-9]", RUN+="/bin/sh -c '/bin/echo %N : $name : %P : %k : %p >> /home/xavior/events3.log'"
Ce qui donne après l’insertion d’une clef :
/dev/sda1 : sda1 : sda : sda1 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda1
/dev/sda2 : sda2 : sda : sda2 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda2
III.5.4 Cibler la partition d’une clef USB qui porte un nom logique précis
Cette règle s’activera que si la clef USB possède un nom logique de partition précis.
ACTION=="add",KERNEL=="sd*[0-9]", ENV{PARTNAME}=="GRUB-SOS", RUN+="/bin/sh -c '/bin/echo %N : $name : %P : %k : %p >> /home/xavior/events3.log'"
Ce qui donne après l’insertion de plusieurs clefs USB différentes :
/dev/sda2 : sda2 : sda : sda2 : /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda2
IV Propriété UDEV des devices non visibles dans une règle (dans certains cas)
Lorsque on utilise la règle suivante qui effectue un filtre sur des propriétés qui ont été ajoutées par UDEV (propriété commençant par ID_xxxx) il se peut qu’elle ne fonctionne pas car vide… alors que tout semble correct.
ACTION=="add", SUBSYSTEM=="block", ENV{ID_FS_USAGE}=="filesystem", RUN+="/bin/sh -c '/bin/echo ID_FS_USAGE : $env{ID_FS_USAGE} : PARTNAME : $env{PARTNAME} : %N : $name : %P >> /home/xavior/events4.log'"
Il faut bien faire attention au nom de fichier qui porte cette règle. Dans notre cas le nom de fichier utilisé est « 10-local.rules
« . Il est pris en compte avant que les valeurs ajoutées par « UDEV » soient renseignées. Sous Ubuntu 20.04 par exemple, les règles qui vont renseigner ces valeurs sont les fichiers commençant à partir du numéro 60xxx (dans /liv/udev/rules.d).
Donc pour filtrer sur les propriétés des devices de type disque, il faut changer l’ordre de priorité de lecture de notre règle par une priorité plus tardive comme par exemple en utilisant le nom de fichier « 61-local.rules
« . Et ce n’est qu’à ce moment que le règle pourra complètement agir et avoir un résultat comme celui-ci :
ID_FS_USAGE : filesystem : PARTNAME : EFI-USB : /dev/sda1 : sda1 : sda
ID_FS_USAGE : filesystem : PARTNAME : GRUB-SOS : /dev/sda2 : sda2 : sda
V Exemples finaux
Nous trouverons 2 exemples pratiques d’UDEV :
- une sauvegarde pour une clef usb bien précise
- une copie systèmatique des données lors d’un périphérique inséré USB
V.1 Sauvegarde de données pour une clef USB bien précise
V.1.1 Définition d’une règle UDEV
Nous allons cibler un matériel (clef USB) bien précis. Pour voir les propriétés nous allons dans un premier temps brancher la clef.
Puis nous allons voir les propriétés : udevadm info /dev/sda1 :
xavior@mon_pc:/etc/udev/rules.d$ udevadm info /dev/sda1
P: /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda1
N: sda1
L: 0
S: disk/by-id/usb-SMI_USB_DISK_AA00000000015023-0:0-part1
...
E: DEVPATH=/devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda1
E: DEVNAME=/dev/sda1
E: DEVTYPE=partition
E: PARTN=1
E: PARTNAME=Sauvegarde
E: MAJOR=8
E: MINOR=1
E: SUBSYSTEM=block
...
E: ID_REVISION=1100
E: ID_SERIAL=SMI_USB_DISK_AA00000000015023-0:0
E: ID_SERIAL_SHORT=AA00000000015023
E: ID_TYPE=disk
...
E: ID_FS_UUID=C025-EDD1
E: ID_FS_UUID_ENC=C025-EDD1
E: ID_FS_VERSION=FAT32
E: ID_FS_TYPE=vfat
E: ID_FS_USAGE=filesystem
E: ID_PART_ENTRY_SCHEME=gpt
E: ID_PART_ENTRY_NAME=Sauvegarde
...
Nos critères seront :
- PARTNAME : nom de la partition,
- ID_SERIAL_SHORT : numéro de série de la clef USB.
Ce qui fera notre règle UDEV suivante : sudo vim /etc/udev/rules.d/61-local.rules
##########################
# Règles UDEV Perso
#########################
# Ajout clef USB dédiée à la sauvegarde
ACTION=="add",KERNEL=="sd[a-z][1-9]", ATTRS{serial}=="AA00000000015023", RUN+="/home/xavior/scripts/udev/udev_test.sh $name $env{ID_SERIAL_SHORT} $env{ID_PART_ENTRY_NAME} "
Cette règle se déclenchera si :
- la clef USB est insérée ;
- le nom du périphérique est de type disque USB (sda1, sdb2, autres…) ;
- le numéro de série est celui de la clef.
On aurait pu ajouter le nom de la partition. Dans cet exemple il a été décidé de vérifier ce point dans le script.
Enfin, si les conditions sont remplies alors le script udev_test.sh sera executé. Les paramètres donnés au script sont :
- $name : le nom du périphérique attribué par le noyau ;
- $env{ID_SERIAL_SHORT} : le numéro de série de la clef usb ;
- $env{IP_PART_ENTRY_NAME} : le nom de la partition.
V.1.2 Script bash associé
Et maintenant le script bash « udev_test » qui va effectuer la sauvegarde de données du PC vers la clef :
##########################################################
# Script permettant la sauvegarde des fichiers importants
# par simple insertion de la clef
# --> lancé par règle UDEV
# $1 -> nom partition
# $2 -> numéro de série de la clef USB
# $3 -> libellé de la partition
##########################################################
##########################################################
# Function permettant de notifier
# $1 -> texte à afficher
##########################################################
function notifier() {
# récupérer les users Xorg et num display
userX=`who | cut -f 1 -d ' '`
displayX=`who | cut -f 4 -d ' '`
# si chaine non vide
if [ "$userX" = "" ]
then
echo "userX vide ...."
else
export DISPLAY=$displayX
export HOME=/home/$userX
# envoie de la notification
su $userX -c "zenity --notification --text=\"$1\""
fi
}
#######################################################################
# Main
#######################################################################
clear
# Récupération des arguments
nom_partition=$1
num_serial=$2
libelle_partition=$3
# Répertoire de montage cible
nom_repertoire="/mnt/tmp_$nom_partition"
# Tests finaux pour confirmer ciblage clef USB
continuer=0
if [ "$num_serial" = "AA00000000015023" ] && [ "$libelle_partition" = "Sauvegarde" ]; then
continuer=1
fi
if [ $continuer -eq 1 ]; then
notifier "Insertion clef USB $1 -- $2\n Début SAUVEGARDE..."
##################
# Montage partition
mkdir $nom_repertoire
mount /dev/$nom_partition $nom_repertoire
################
# Opérations à faire : sauvegarde des données...
# Chemin répertoire final sur lequel sera monté la clef USB et effectué la sauvegarde
nom_rep_sauvegarde="$nom_repertoire/Sauvegarde"
# teste si répertoire sauvegarde existe --> si non on créé
if [ ! -d $nom_rep_sauvegarde ]; then
mkdir $nom_rep_sauvegarde
mkdir $nom_rep_sauvegarde/scripts
mkdir $nom_rep_sauvegarde/0_DOC_Vitaux
fi
# Copie des fichiers sur support amovible USB
rsync -az --delete-after /home/xavior/scripts/ $nom_rep_sauvegarde/scripts/
rsync -az --delete-after /home/xavior/Documents/0_DOC_Vitaux/ $nom_rep_sauvegarde/0_DOC_Vitaux/
cp -R -f '/home/xavior/Bureau/Mots de passe.kdbx' $nom_rep_sauvegarde/
######################
# Demontage partition
umount $nom_repertoire
rmdir $nom_repertoire
notifier "FIN SAUVEGARDE : $1 -- $2"
fi
Ce qui donne le résultat ci-dessous une fois la clef insérée :

V.2 Copie systématique du contenu d’une clef USB vers le PC
Dans cet exemple nous allons jouer les mini-pirates : dès qu’une clef USB est insérée sur le PC, on copie systématiquement le contenu de la clef …
V.2.1 Définition d’une règle UDEV
Notre règle sera simple :
# copie systématique d'une clef USB
ACTION=="add",KERNEL=="sd[a-z][1-9]", RUN+="/home/xavior/scripts/udev/udev_test2.sh $name $env{ID_SERIAL_SHORT} $env{ID_PART_ENTRY_NAME}"
Les conditions d’execution du script sont :
- l’ajout d’un périphérique,
- un périphérique de type sda1, sdc2 ou autres …
V.2.2 Script bash associé
Le script ci-dessous va copier le contenu de la clef usb dans le home :
#!/bin/bash
##########################################################
# Script permettant la sauvegarde des fichiers importants
# par simple insertion de la clef
# --> lancé par règle UDEV
# $1 -> nom partition
# $2 -> numéro de série de la clef USB
# $3 -> libellé de la partition
##########################################################
#######################################################################
# Main
#######################################################################
clear
# Récupération des arguments
nom_partition=$1
num_serial=$2
libelle_partition=$3
# Répertoire de montage cible
nom_repertoire="/mnt/tmp_$nom_partition"
##################
# Montage partition
mkdir $nom_repertoire
mount /dev/$nom_partition $nom_repertoire
################
# Opérations ...
# création répertoire final
repertoire_final="/home/xavior/usb/$num_serial/$nom_partition"
if [ ! -d $repertoire_final ]; then
mkdir -p $repertoire_final
fi
# copie des données
cp -R -f -u $nom_repertoire/* $repertoire_final
######################
# Demontage partition
umount $nom_repertoire
rmdir $nom_repertoire
VI Conclusion et ressources
Nous avons vu comment implémenter des scripts avec UDEV lors d’ajout ou de retrait de périphériques USB. UDEV est particulièrment utile dans le cadre de l’automatisation de tâches en fonction d’évènements de type matériel.
Ressources :
- https://linuxembedded.fr/2015/05/une-introduction-a-udev
- https://wiki.debian.org/fr/udev
- https://fcebron.github.io/Udev/
- https://www.malekal.com/comment-creer-les-regles-udev-rules/
- https://doc.ubuntu-fr.org/udev
- https://wiki.archlinux.org/title/udev