Udev
|
Versioni Compatibili Tutte le versioni supportate di Debian |
Introduzione
Udev si occupa della gestione dinamica dei dispositivi per il kernel Linux ed è una componente importante di Debian, il cui pacchetto omonimo è quindi installato di default. In particolare gestisce i file di tipo dispositivo nella directory /dev
, ossia tutti gli eventi relativi all'aggiunta e rimozione di hardware, incluso l'eventuale caricamento dei relativi firmware, e tutti i cambiamenti di stato di un dispositivo.
I file dispositivo nella directory /dev
sono creati e rimossi all'occorrenza, anziché essere sempre presenti staticamente, con nomi e proprietà determinate in base alle informazioni lette dal file system /sys
e alle eventuali regole personalizzate presenti nel sistema nella directory /etc/udev/rules.d
.
Udev carica i moduli del kernel in modo parallelo e asincrono, permettendo una maggiore efficienza nei tempi di avvio, ma per contro i nomi assegnati ai dispositivi possono cambiare da un avvio al successivo in presenza di più dispositivi dello stesso tipo (per esempio più interfacce di rete, più dischi fissi, eccetera).
Per ovviare a ciò è importante fare riferimento ai dispositivi a blocchi, come i dischi fissi, tramite il loro UUID al posto del nome dei file di tipo dispositivo creati (per esempio /dev/sda
o /dev/sdb
), mentre per le interfacce di rete e i dispositivi ottici rimovibili esistono regole predefinite introdotte su Debian che ne conservano la persistenza; o in alternativa scrivere delle regole personalizzate.
Sviluppo di udev
A partire dal kernel Linux 2.6 udev svolge le funzionalità in precedenza ricoperte da hotplug
e hwdetect
.
A partire da Debian 8 (Jessie) il suo sviluppo è portato avanti all'interno di systemd, e le funzionalità di demone sono svolte dall'eseguibile systemd-udevd
, richiamato dal servizio systemd-udevd.service
. Il demone è comunque avviabile anche da altri sistemi di init, come il precedente sysvinit.
Strumenti
Per interrogare systemd-udevd
, è possibile ricorrere all'eseguibile udevadm
, che per alcune operazioni richiede privilegi di amministrazione. Di seguito verranno presentate le funzionalità più comuni, finalizzate alla scrittura di regole personalizzate.
Raccolta informazioni
Monitoraggio degli eventi
Monitora tutti gli eventi di udev, stampando anche le proprietà di ciascuno:
$ udevadm monitor --udev
(forma abbreviata: udevadm monitor -u
)
Sono stampati sullo schermo tutti gli eventi generati. Per testarne l'efficacia si può provare, dopo aver lanciato il comando, a inserire una memoria USB. Ecco un esempio con una memoria USB di TDK, contenente una singola partizione FAT:
UDEV [3995.944943] add /devices/pci0000:00/0000:00:1a.7/usb6/6-1 (usb) UDEV [3995.946941] add /devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0 (usb) UDEV [3995.947663] add /devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host9 (scsi) UDEV [3995.948500] add /devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host9/scsi_host/host9 (scsi_host) UDEV [3996.972131] add /devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host9/target9:0:0 (scsi) UDEV [3996.973503] add /devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host9/target9:0:0/9:0:0:0 (scsi) UDEV [3996.974521] add /devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host9/target9:0:0/9:0:0:0/scsi_disk/9:0:0:0 (scsi_disk) UDEV [3996.975626] add /devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host9/target9:0:0/9:0:0:0/bsg/9:0:0:0 (bsg) UDEV [3996.976216] add /devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host9/target9:0:0/9:0:0:0/scsi_device/9:0:0:0 (scsi_device) UDEV [3996.976824] add /devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host9/target9:0:0/9:0:0:0/scsi_generic/sg2 (scsi_generic) UDEV [3999.038112] add /devices/virtual/bdi/8:16 (bdi) UDEV [3999.206364] add /devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host9/target9:0:0/9:0:0:0/block/sdb (block) UDEV [3999.345441] add /devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host9/target9:0:0/9:0:0:0/block/sdb/sdb1 (block)
Tutti questi eventi sono stati generati dal solo inserimento della memoria. L'ultimo riguarda la generazione del file di tipo dispositivo a blocchi /dev/sdb1
, corrispondente alla partizione della memoria USB, mentre il penultimo la creazione del file a blocchi /dev/sdb
, corrispondente all'intero dispositivo, inclusa la tabella delle partizioni. Si noti che gli eventi sono prodotti dal più generale, riguardante un generico dispositivo USB, al più particolare, riguardante la creazione di un file a blocchi che può essere montato per accedere alla partizione della memoria di massa.
Per ottenere maggiori informazioni su ciascun evento, si utilizza l'opzione --property
(abbreviata: -p
).
Quindi per esempio:
$ udevadm monitor -u -p
Restituisce:
... UDEV [4398.237894] add /devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host10/target10:0:0/10:0:0:0/block/sdb (block) ACTION=add DEVLINKS=/dev/disk/by-id/usb-TDKMedia_Trans-It_Drive_07BA1203F92265C2-0:0 /dev/disk/by-label/Debian\x20Inst /dev/disk/by-path/pci-0000:00:1a.7-usb-0:1:1.0-scsi-0:0:0:0 /dev/d isk/by-uuid/DEB0-0001 DEVNAME=/dev/sdb DEVPATH=/devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host10/target10:0:0/10:0:0:0/block/sdb DEVTYPE=disk ID_BUS=usb ID_FS_LABEL=Debian_Inst ID_FS_LABEL_ENC=Debian\x20Inst ID_FS_TYPE=vfat ID_FS_USAGE=filesystem ID_FS_UUID=DEB0-0001 ID_FS_UUID_ENC=DEB0-0001 ID_FS_VERSION=FAT32 ID_INSTANCE=0:0 ID_MODEL=Trans-It_Drive ID_MODEL_ENC=Trans-It\x20Drive\x20\x20 ID_MODEL_ID=0619 ID_PATH=pci-0000:00:1a.7-usb-0:1:1.0-scsi-0:0:0:0 ID_PATH_TAG=pci-0000_00_1a_7-usb-0_1_1_0-scsi-0_0_0_0 ID_REVISION=PMAP ID_SERIAL=TDKMedia_Trans-It_Drive_07BA1203F92265C2-0:0 ID_SERIAL_SHORT=07BA1203F92265C2 ID_TYPE=disk ID_USB_DRIVER=usb-storage ID_USB_INTERFACES=:080650: ID_USB_INTERFACE_NUM=00 ID_VENDOR=TDKMedia ID_VENDOR_ENC=TDKMedia ID_VENDOR_ID=0718 MAJOR=8 MINOR=16 SEQNUM=2048 SUBSYSTEM=block TAGS=:systemd: UDISKS_PRESENTATION_NOPOLICY=0 USEC_INITIALIZED=96565 ...
dove per comodità ci si è limitati al solo output prodotto dal penultimo evento, quello corrispondente alla creazione del file di tipo dispositivo a blocchi /dev/sdb
, ma ciascun evento avrebbe avuto delle variabili associate.
Questo è un comando molto utile per catturare informazioni su un evento desiderato. Infatti le proprietà potranno essere utilizzate come condizioni per creare regole personalizzate relative a un dato dispositivo o a un'intera famiglia di dispositivi (per tipo, marca, ecc...), con la granularità desiderata.
In modo analogo si possono verificare gli eventi derivanti dalla rimozione di un dispositivo, e in generale tutti i cambiamenti di stato di ogni dispositivo gestito da udev.
Informazioni sui file di tipo dispositivo già creati
Un'altra possibilità per recuperare informazioni relative a un dato dispositivo, dopo averlo inserito, è quello di interrogare systemd-udevd
partendo dal nome associato in modo dinamico al dispositivo. Sempre nel caso di una memoria, che si suppone identificata come /dev/sdb
e contenente un'unica partizione /dev/sdb1
:
$ udevadm info --name sdb1 --attribute-walk
(Forma abbreviata: udevadm info -n sdb1 -a
)
Restituisce informazioni sul file dispositivo cercato e tutti quelli presenti a livello superiore, in ordine inverso a come sarebbero monitorati. Per esempio, sempre nel caso della memoria USB, rimuovendo le informazioni meno utili:
... looking at device '/devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host10/target10:0:0/10:0:0:0/block/sdb/sdb1': KERNEL=="sdb1" SUBSYSTEM=="block" DRIVER=="" ATTR{ro}=="0" ATTR{size}=="15099904" ... looking at parent device '/devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host10/target10:0:0/10:0:0:0/block/sdb': KERNELS=="sdb" SUBSYSTEMS=="block" DRIVERS=="" ATTRS{ro}=="0" ATTRS{size}=="15101952" ... looking at parent device '/devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host10/target10:0:0/10:0:0:0': KERNELS=="10:0:0:0" SUBSYSTEMS=="scsi" DRIVERS=="sd" ATTRS{rev}=="PMAP" ATTRS{type}=="0" ATTRS{scsi_level}=="5" ATTRS{model}=="Trans-It Drive " ... ATTRS{vendor}=="TDKMedia" ... looking at parent device '/devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host10/target10:0:0': KERNELS=="target10:0:0" SUBSYSTEMS=="scsi" DRIVERS=="" looking at parent device '/devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host10': KERNELS=="host10" SUBSYSTEMS=="scsi" DRIVERS=="" looking at parent device '/devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0': KERNELS=="6-1:1.0" SUBSYSTEMS=="usb" DRIVERS=="usb-storage" ... looking at parent device '/devices/pci0000:00/0000:00:1a.7/usb6/6-1': KERNELS=="6-1" SUBSYSTEMS=="usb" DRIVERS=="usb" ... ATTRS{manufacturer}=="TDKMedia" ... ATTRS{product}=="Trans-It Drive" looking at parent device '/devices/pci0000:00/0000:00:1a.7/usb6': KERNELS=="usb6" SUBSYSTEMS=="usb" DRIVERS=="usb" ... looking at parent device '/devices/pci0000:00/0000:00:1a.7': KERNELS=="0000:00:1a.7" SUBSYSTEMS=="pci" DRIVERS=="ehci-pci" ... looking at parent device '/devices/pci0000:00': KERNELS=="pci0000:00" SUBSYSTEMS=="" DRIVERS==""
Ottenere informazioni specifiche (query)
Per finire, se si è interessati soltanto a determinati campi, è possibile ricorrere all'opzione --query
(abbreviata: -q
).
Un esempio comune è quello di ottenere il percorso a partire da /sys
del dispositivo, che può essere usato per testare un evento. Sempre con la memoria USB con partizione /dev/sdb1
:
$ udevadm info -q path -n sdb1
Restituisce il percorso (a partire da /sys/
):
/devices/pci0000:00/0000:00:1a.7/usb6/6-1/6-1:1.0/host10/target10:0:0/10:0:0:0/block/sdb/sdb1
Testare le regole
Con il percorso a partire da /sys
di un dato dispositivo, è sufficiente:
$ udevadm test /percorso
Saranno simulati ricorsivamente tutti gli eventi contenuti a partire dal percorso scelto. Possono essere richiesti privilegi di amministrazione, a seconda delle azioni necessarie per gli eventi.
È possibile specificare azioni (come per esempio add o remove) con l'opzione --action=ACTION
(abbreviata: -a
).
Per esempio, sempre con la memoria USB, per simulare gli eventi di aggiunta e rimozione del dispositivo:
$ udevadm test $(udevadm info -q path -n sdb1) -a add $ udevadm test $(udevadm info -q path -n sdb1) -a remove
Le azioni possono essere controllate con il monitoraggio di udev, come visto in precedenza. Di default è utilizzata change, che cattura ogni tipo di azione.
Caricamento delle regole
Di norma le regole di udev sono caricate automaticamente, ma avranno efficacia solo per gli eventi successivi alla modifica.
Per forzare l'esecuzione di tutte le regole, con privilegi di amministrazione è sufficiente:
# udevadm trigger
E in caso il caricamento automatico delle nuove regole fallisse, è possibile caricarle manualmente con:
# udevadm control --reload
File di configurazione
Dispositivi di memoria e interfacce di rete Per il montaggio automatico di dispositivi di memoria di massa, si deve fare riferimento a udisks, e non creare una regola per udev, che si deve occupare soltanto dei nomi e delle proprietà dei file dispositivo nella directory /dev , ma non deve interferire con le funzionalità degli altri demoni di sistema.
Per lo stesso motivo non si deve occupare della gestione delle connessioni di rete. Inoltre i nomi da assegnare alle interfacce di rete sono gestiti dalle regole scritte in |
È sufficiente creare nella directory /etc/udev/rules.d
un nuovo file con estensione .rules
, uno per ciascun dispositivo.
Il formato delle regole, per scriverne di facilmente comprensibili, è molto semplice:
- i commenti sono preceduti da cancelletto (
#
); - ogni regola va scritta su un'unica riga, con possibilità di andare a capo soltanto lasciando un backslash
\
come ultimo carattere; - ogni regola consiste di una o più condizioni, separate e seguite da virgola, e una o più azioni, separate da virgola;
- condizioni e azioni sono formate da tre elementi: chiave, operatore e valore, quest'ultimo racchiuso tra virgolette;
- condizioni e azioni sono distinguibili soltanto dall'operatore utilizzato e dalle chiavi utilizzabili, e inoltre il valore di una condizione può essere un pattern con
*
per rappresentare qualsiasi stringa.
Per una descrizione esaustiva si rimanda al manuale (man udev
), di seguito sono introdotte soltanto le condizioni e le azioni principali.
Per le condizioni si considera soltanto l'operatore di equivalenza (==
) con alcune chiavi comuni (si controlli per un esempio i valori restituiti in precedenza con la chiave USB):
ACTION
per determinare le azioni a cui la regola va applicata, come osservati monitorando gli eventi;KERNEL
il nome del file di tipo dispositivo, come sarebbe scelto senza l'attuale regola. Vista la scelta dinamica è in genere consigliabile utilizzare un pattern con*
(per esempio in/etc/udev/rules.d/70-persistent-net.rules
era utilizzata la condizioneKERNEL=="eth*"
), a meno che si intenda solo cambiare le proprietà di un dispositivo esistente, il cui nome è già determinato staticamente e univoco;SUBSYSTEMS
per identificare il sottosistema dell'evento, in base ai valori monitorati e alla granularità desiderata (block, scsi, usb, pci, ecc...);DRIVERS
per filtrare gli eventi in base al modulo del kernel utilizzato (per esempio nel caso della memoria USB dell'esempio sarebbe stato usb-storage, ma dipende dalla granularità desiderata dal filtro);ENV{proprietà}
per confrontare i valori di una data proprietà, come ottenuti dal monitoraggio di un dato evento.
Per le azioni si considera soltanto l'operatore di assegnamento (=
) con alcune chiavi comuni:
NAME
per determinare il nome del file di tipo dispositivo da creare, permesso solo per interfacce di rete;SYMLINK
per creare un link simbolico personalizzato, azione permessa per tutti i dispositivi, purché il nome non interferisca con quelli riservati dal kernel;OWNER
per determinare l'utente proprietario;GROUP
per determinare il gruppo proprietario;MODE
per i permessi da associare (si vedaman chmod
, la sintassi è la stessa in forma ottale);RUN
per eseguire un programma esterno. Per quest'azione si raccomanda l'uso di programmi veloci che terminano le proprie azioni in tempi brevi, per non rallentare la gestione degli eventi successivi, ed eventuali processi lanciati in background saranno terminati da udev quando termina il programma lanciato in questo modo.
Esempi
Utilizzo di nomi persistenti per memorie rimovibili
Riprendendo l'esempio della memoria USB, basta creare un file in /etc/udev/rules.d
con estensione .rules
, per esempio /etc/udev/rules.d/99-my-usb.rules
, con questo contenuto:
ACTION=="add", SUBSYSTEMS=="usb", DRIVERS=="usb-storage", ENV{ID_VENDOR}=="TDKMedia", ENV{ID_FS_UUID}="...", SYMLINK="myusb"
Le prime cinque condizioni filtrano gli eventi udev, e si attivano durante la creazione (azione: add) dei file dispositivo relativi al sottosistema usb, al modulo del kernel usb-storage per il dispositivo con ID_VENDOR "TDKMedia" e contenente una partizione con un dato UUID. L'azione si limita a creare un link simbolico con il nome scelto, che funge da alias per quello vero e proprio (per esempio /dev/sdb1
).
Per esempio ora è possibile montare la partizione scelta in /mnt con:
# mount /dev/myusb /mnt
a prescindere dal nome che sarà assegnato alla partizione (/dev/sdb1
, /dev/sdc1
, ecc...).
Si noti l'utilizzo di SYMLINK
in luogo di NAME
, in quanto è possibile soltanto creare un link simbolico personalizzato per file che non sono relativi a interfacce di rete. Si ricorda nuovamente che per effettuare il mount vero e proprio l'uso di udev è caldamente sconsigliato.
Un simile risultato si può ottenere impostando una regola personalizzata in /etc/fstab
, che legga l'UUID della partizione, creando una directory allo scopo da riservarsi come punto di mount personalizzato. Lo scopo di questa sezione comunque è solo di esemplificare il funzionamento di udev e i risultati ottenibili, stesso motivo per cui si sono scritte più condizioni di quelle strettamente necessarie.
Permettere lettura e scrittura su una memoria esterna a tutti gli utenti di un dato gruppo
Creo un gruppo usbstorage per gestire tutte le unità rimovibili USB, supponendo siano tutte gestite dal modulo del kernel usb-storage
:
# addgroup usbstorage
Aggiungo l'utente principale a tale gruppo:
# adduser tuo-nome-utente usbstorage
Effettuo logout e login con tale utente, per rendere effettive le modifiche.
Ora scrivo una regola personalizzata per tutti i dispositivi, creando un file /etc/udev/rules.d/99-usbstorage.rules
contenente:
ACTION=="add", SUBSYSTEMS=="usb", DRIVERS=="usb-storage", MODE="0660", OWNER="root", GROUP="usbstorage"
Le prime tre condizioni filtrano gli eventi udev, e si attivano durante la creazione (azione: add) dei file dispositivo relativi al sottosistema usb e al modulo del kernel usb-storage. Le restanti azioni assegnano al file come proprietario l'utente root, il gruppo usbstorage e come permessi 0660
, corrispondente a permessi di lettura e scrittura per solo utente e gruppo.
Ora, in modo analogo a come funzionava il gruppo floppy, sarà possibile formattare e copiare un'immagine CD direttamente sui supporti rimovibili anche senza privilegi di amministrazione, se si è membri del gruppo usbstorage.
ATTENZIONE Si noti invece che aggiungere il proprio utente al gruppo disk, quello utilizzato di default a partire da Debian 8 (Jessie), gli darebbe permessi di lettura e scrittura anche sui dischi fissi utilizzati dal sistema, il che sarebbe praticamente equivalente (quanto a pericolosità dell'impostazione) a essere root. |
Per annullare le modifiche è sufficiente rimuovere il file /etc/udev/rules.d/99-usbstorage.rules
e rimuovere tutte le unità USB ancora collegate.
Limitare i permessi di lettura e scrittura di /dev/fuse al solo gruppo fuse
A partire da Debian 8 (Jessie), il modulo fuse
è utilizzabile da tutti gli utenti. Per ripristinare la situazione precedente, se per esempio si utilizza un ambiente grafico minimale o alcuni utenti non utilizzano nemmeno l'interfaccia grafica, è sufficiente creare una regola personalizzata. Va ricordato infatti che la modifica è stata apportata per via dell'integrazione di fuse nei principali Desktop Environment.
Creare /etc/udev/rules.d/99-fuse.rules
contenente:
ACTION=="change", KERNEL=="fuse", MODE="0660", OWNER="root", GROUP="fuse"
Le prime due condizioni filtrano gli eventi udev, e si attivano a ogni cambiamento (azione: change, che include la creazione) del file /dev/fuse
(kernel: fuse). Le restanti azioni assegnano al file come proprietario l'utente root, il gruppo fuse e come permessi 0660
, corrispondente a permessi di lettura e scrittura per solo utente e gruppo.
Per rendere effettiva la regola da subito, senza attendere il successivo avvio, visto che /dev/fuse
già esiste, è necessario forzarne l'esecuzione con:
# udevadm trigger
Se si utilizzano più utenti, soltanto quelli appartenenti al gruppo fuse potranno utilizzare fuseiso
e in generale tutti i programmi utilizzanti il modulo del kernel fuse.
Per annullare le modifiche è sufficiente eliminare il file /etc/udev/rules.d/99-fuse.rules
e rieseguire udevadm trigger
.
Monitor esterno
Si consideri di voler configurare un monitor esterno in automatico, solo quando questo è aggiunto al sistema. Si rimanda a questa guida Wiki, che utilizza anche l'azione RUN
per lanciare un veloce script.
Approfondimenti
Manuali e file Leggimi
- Per la scrittura delle regole:
$ man udev
- Per i parametri del kernel e la sintassi del file di configurazione
/etc/udev/udev.conf
:$ man udevd
- Per la gestione degli eventi udev:
$ man udevadm
- Informazioni aggiuntive, relative alla sola integrazione in Debian:
$ zless /usr/share/doc/udev/README.Debian.gz
(con note sulle interfacce di rete per/etc/udev/rules.d/70-persistent-net.rules
, non più necessarie a partire da Debian 9 Stretch perché già persistenti di default, e uso di udev con utenti non locali tramite LDAP o NIS)
Collegamenti esterni
- Wiki di ArchLinux
- Guida alla scrittura di regole Udev (non più aggiornata, ma parzialmente ancora utile)
Guida scritta da: HAL 9000 11:27, 21 lug 2019 (CEST) | Debianized 20% |
Estesa da: | |
Verificata da: | |
Verificare ed estendere la guida | Cos'è una guida Debianized |