Udev

Versione del 21 lug 2019 alle 09:27 di HAL 9000 (discussione | contributi) (verificata, compatibile per tutte le versioni)
(diff) ← Versione meno recente | Versione attuale (diff) | Versione più recente → (diff)
File System e dispositivi fisici
Arrow left.png

Generalità

Locali

Remoti

Strumenti

Arrow right.png



Debian-swirl.png 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 /etc/udev/rules.d/70-persistent-net.rules, generato automaticamente. Il file non esiste più a partire da Debian 9 (Stretch), in quanto le interfacce di rete sono già persistenti di default con le recenti versioni di udev.


È 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 condizione KERNEL=="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 veda man 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




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