|
Sin dalle prime versioni del kernel 2.6 c'è stata una piccola rivoluzione nella gestione dei dispositivi connessi al sistema e della gestione dei loro driver: un nuovo file system ha visto la luce (sysfs) ed una nuova procedura di identificazione dei device si è imposta (udev).
Vediamo di illustrare brevemente questi concetti focalizzandoci in particolar modo su udev e su come questo funziona.
Concetti di base
In un sistema UNIX in generale e Linux in particolare la directory /dev viene usata per contenere tutti quei file che identificano un dispositivo all'interno del sistema. Un disco, la tastiera, il mouse ecc. hanno tutti un riferimento nella directory /dev. Ogni file in questa directory è detto nodo (file node) e può essere di due tipi: char o block.
Riporto di seguito un esempio di file di tipo block (il mio disco primario):
$ ls -l /dev/sda* brw-rw---- 1 root disk 8, 0 Apr 15 02:45 /dev/sda brw-rw---- 1 root disk 8, 1 Apr 15 02:45 /dev/sda1 brw-rw---- 1 root disk 8, 2 Apr 15 02:45 /dev/sda2 brw-rw---- 1 root disk 8, 3 Apr 15 02:45 /dev/sda3 brw-rw---- 1 root disk 8, 4 Apr 15 02:45 /dev/sda4
mentre questo è un esempio di dispositivi di tipo char (le porte seriali di default di un qualsiasi PC):
$ ls -l /dev/ttyS* crw-rw---- 1 root dialout 4, 64 Apr 15 02:45 /dev/ttyS0 crw-rw---- 1 root dialout 4, 65 Apr 15 02:45 /dev/ttyS1 crw-rw---- 1 root dialout 4, 66 Apr 15 02:45 /dev/ttyS2 crw-rw---- 1 root dialout 4, 67 Apr 15 02:45 /dev/ttyS3
I dispositivi di tipo block (o block device) sono tutti quei dispositivi che trasferiscono i dati a blocchi di dimensione fissa; i principali «esponenti» di questa categoria sono gli hard disk e tutti quei dispositivi di memorizzazione di massa. Mentre i dispositivi di tipo char (o char device) sono tutti quei dispositivi che trasferiscono i propri dati come flusso di caratteri senza dimensione fissa e si accedono, di solito, come se fossero dei file (si veda il concetto di «astrazione file» dei sistemi UNIX).
Ogni file node presente nella directory /dev rappresenta quindi un dispositivo di sistema e viene utilizzato dai processi che girano nello userspace per accedere al dispositivo a cui fanno riferimento.
Come ben saprete, in un sistema UNIX (e Linux non fa eccezione) ogni dispositivo è pilotato da un particolare software che prende il nome di driver. Il driver riceve i comandi dai processi dallo userland (processi utente) e li passa al dispositivo a lui associato (in realtà un driver può pilotare anche più di un dispositivo; anzi, in realtà il caso che un driver piloti solo un dispositivo è rarissimo e comunque considerato un cattivo esempio di programmazione). L'anello di collegameno tra il driver (e quindi i dispositivi a lui collegati) e i processi utente sono appunto i file nella directory /dev.
In parole povere la catena è questa:
processo utente --> file in /dev --> driver --> dispositivo
Il driver, una volta scritto, viene caricato nel kernel del sistema e rimane lì in attesa di comandi. L'operazione di caricamento del driver nel kernel però, non genera anche l'automatica creazione del file node in /dev poiché questa operazione interessa solamente il kernel del sistema e non lo spazio utente! In particolare il kernel non conosce neppure l'esistenza del file node in /dev, lui conosce solo l'identificativo univoco all'interno del sistema per ogni dispositivo (detto major number) e la sua classe di appartenenza (block o char).
Ad esempio, con riferimento alle porte seriali di cui sopra, si ha che la prima porta seriale chiamata /dev/ttyS0 è definita come:
$ ls -l /dev/ttyS0 crw-rw---- 1 root dialout 4, 64 Apr 15 02:45 /dev/ttyS0
quel 4 dopo la parola dialout è il major number del dispositivo e che identifica, all'interno del sistema, i dispositivi seriali, il 66 e invece il minor number che, all'interno dei dispositivi seriali, identifica la prima porta seriale (il nimor number e gestito in maniera diversa da driver a driver). Il tipo del file node viene invece specificato dalla prima lettera che si legge ion cima ai permessi di accesso, quella c infatti identifica il file node di tipo char.
Come riprova andiamo a vedere nel file /proc/devices, dove si ha l'elenco di tutti i major number definiti nel sistema, se quello che abbiamo detto corrisponde:
$ awk '$1 == 4' /proc/devices 4 /dev/vc/0 4 tty 4 ttyS
Sì, proprio come avevamo detto: il major number 4 raggruppa non solo le porte seriali ma tutta la classe dei terminali (seriali e non).
E' facile allora capire come i file node in /dev siamo importanti per collegare lo spazio utente con i dispositivi del sistema.
Il vecchio metodo
In passato, nei kernel 2.4 e precedenti, per creare questi file node si utilizzavano due tecniche principali:
- o si utilizzava una directory /dev statica dove si definivano tutti i possibili file node di ogni possbile dispositivo collegabile al sistema, oppure
- si utilizzava devfs, un file system virtuale gestito dal kernel stesso che doveva provvedere a creare questi file node ogni volta che un nuovo driver veniva caricato nel sistema.
La prima soluzione era quella più semplice ma che portava ad avere una directory /dev piuttosto grande e, in percentuale, poco utilizzata a causa dell'enorme numero di possibili dispositivi collegabili; la seconda soluzione, invece, cercava di risolvere gli inconvenenti della prima ma con pochi risultati a causa della enorme complessità e di non pochi problemi di robustezza.
Ma col kernel 2.6 le cose sono cambiate...
Il nuovo metodo
Nel kernel 2.6, appunto, Linux acquista un nuovo file system: il sysfs, un file sistem virtuale dove ogni dispositivo del sistema è rappresentato da una directory e da dei file (chiamati attributi). Il sysfs è visitabile nella directory solitamente nella /sys ed è parte integrante del nuovo modello di device di Linux: ogni dispositivo ha associato una struttura struct device che lo identifica univocamente, ogni driver quindi ha a che fare con essa e la usa per comunicare al kernel le operazioni notevoli da fare sul dispositivo stesso: inserimento, rimozione, ecc..
Grazie a questi nuovi concetti sono stati allora definiti gli eventi di nucleo, eventi notevoli, cioè, definiti all'interno del sistema e che descrivono una azione ben precisa su di un ben precisono dispositivo. Se, ad esempio, una chiavetta USB viene inserita nel sistema, una volta che il controller USB host l'ha identificata viene subito creato una nuova struttura struct device che mantiene ed identifica il dispositivo chiavetta USB. Una volta fatto questo, poiché il dispositivo è stato aggiunto nel sistema, viene anche generato un evento inserimento del dispositivo chiavetta USB; questo evento viene quindi catturato da udev.
udev: cosa fa e cosa è
Il programma udev non è altro che un normale processo utenete che sta in attesa degli eventi di sistema e, a seconda di una serie di regole, esegue determinate azioni. Ogni qual volta un evento viene catturato lui analizza le regole specificate per esso (solitamente dei file nella diectory /etc/udev/rules.d) e quindi crea il file node necessario; non solo, è anche in grado di eseguire programmi sul dispositivo stesso e molto altro!
La presenza di queste regole rende udev molto versatile ed in particolare, considerando che possiano lanciare anche programmi esterni, ci permette di:
- rinominare un dispositivo a piacere (dargli quindi un nome diverso dallo standard);
- creare dei nomi persisteni per i dispositivi (ad ogni dispositivo un nome sempe uguale e mai varibile a seconda della configurazione del sistema);
- decidere i permessi di accesso ai dispositivi in maniera dinamica;
- eseguire applicazioni spefiche quando un dispositivo viene aggiunto o rimosso dal sistema (si pensi ad esempio alla possibilità di far visualizzare automaticamente il contenuto di una chiavetta USB dopo che questa è stata collegata al sistema).
Le regole
Come vedremo, le regole di udev sono molto potenti e quindi possono essere anche molto complicate, ma una volta capito come funzionano, ed usandole intelligentemente, si scopre che non sono poi così difficili da capire. Le regole di udev sono contenute in uno o più file, all'interno della directory /etc/udev/rules.d, esso infatti analizza tutti i file presenti nella directory in ordine alfabetico ed ogni volta che trova una corrispondenza esegue l'azione associata.
Una regola, all'interno del file, sta tutta su una linea, quindi non si può spezzare su più linee (non si hanno caratteri speciali per questo), mentre una linea che inizia con # identifica un commento.
La sintassi poi è semplice: una serie di parole chiave separate da virgole:
KERNEL=="hda", NAME="disco_ide"
Questa regola possiede un confronto (segno ==) ed una assegamento (segno =) e dice ad udev che ogni volta che viene agiunto un dispositivo che il kernel chiama hda (il primo disco IDE) lui deve rinominarlo in disco_ide e creare il nodo di conseguenza. Se scrivessi invece:
KERNEL=="hda", DRIVER="ide-disk", NAME="disco_ide", SYMLINK+="disco_spare"
direi ad udev di fare la stessa cosa di prima ma se, e solo se, il driver che gestisce il dispositivo hda è ide-disk (in pratica escludo, ad esempio, i cdrom) ed inoltre gli dico di creare anche un link simbolico al file /dev/disco_ide in modo tale che una applicazione possa accedere al disco aprendo il file /dev/disco_spare.
Le parole chiavi principali di una regola sono (si veda il manuale per la lista esaustiva):
- KERNEL: che identifica il nome che il kernel dà ad un dispositivo.
- SUBSYSTEM: che identifica un sottosistema di appartenenza di un dispositivo.
- DRIVER: che identifica il nome del driver che gestisce un dispositivo.
- ATTR: che identifica un attibuto di un dispositivo (definito nel sysfs).
Mentre, i principali assegamenti che si possono fare sono (si veda il manuale per la lista esaustiva):
- NAME: che identifica il nome con cui va chiamato il dispositivo.
- SYMLINK: che identifica una lista di possibili alias del dispositivo (si noti il carattere += nell'assegnazione dell'esempio sopra dice di aggiungere il nome alla lista dei possibili link simbolici, non di assegnarlo univocamente).
- RUN: che identifica il programma da lanciare sul dispositivo per compiere specifiche azioni su di esso.
Nei confronti di ogni parola chiave il confronto (match) viene fatto carattere per carattere ma si possono usare anche delle wildcard:
- Con * si identificano tutti i caratteri, zero o piu volte.
- Con ? si identificano tutti i caratteri ma una volta sola.
- Con [] si identificano solo i caratteri racchiusi nelle parentesi (si possono specificare degli intervalli usando il segno -).
Mentre, negli assegnamenti si possono usare dei caratteri speciali:
- Con %k si specifica il nome che il kernel dà al dispositivo.
- Con %n si specifica il kernel number, cioe un numero che il kernel assegna al device e che può assumere significati diversi (ad esempio per un disco esso rappresenta il numero della partizione rilevata).
Ad esempio:
KERNEL="fd[0-9]*", NAME="floppy/%n", SYMLINK+="%k"
dice ad udev di rinominare tutti i dispositivi il cui nome inizia per fd seguito da un carattere numerico tra 0 e 9 e quindi da altri caratteri in /dev/floppy/numero, dove numero identifica se è il primo floppy, il secondo e così via; inoltre si richiede anche la creazione di un link simbolico a questi nuovi nomi utilizzando il nome di default scelto dal kernel (magari per mantenere la compatibilità con la vecchia convenzione).
Trovare le informazioni nel sysfs
Come detto più volte, udev si appoggia su sysfs ed è quindi molto importante sapere come è possibile andare a cercare dentro di esso tutte quelle informazioni che ci possono servire per creare le nostre regole.
Ad esempio, se volessimo creare una regola per creare un nodo particolare, e ben definito, per una nostra chiavetta USB, in modo tale che ogni volta che questa viene inserita venga sempre individuata all'interno del sistema con uno stesso nome (indipendentemente dalla configurazione dello stesso), possiamo procedere come segue.
Prima di tutto colleguamo la nostra chiavetta al sistema e poi vediamo dove viene mappata. Nei log di sistema vedo:
scsi 1:0:0:0: Direct-Access Generic STORAGE DEVICE 1.04 PQ: 0 ANSI: 0 sd 1:0:0:0: [sda] 256000 512-byte logical blocks: (131 MB/125 MiB) sd 1:0:0:0: [sda] Write Protect is off sd 1:0:0:0: [sda] Mode Sense: 02 00 00 00 sd 1:0:0:0: [sda] Assuming drive cache: write through sd 1:0:0:0: [sda] Assuming drive cache: write through sda: sda1 sd 1:0:0:0: [sda] Assuming drive cache: write through sd 1:0:0:0: [sda] Attached SCSI removable disk
Quindi la chiavetta è mappata nel nodo /dev/sda e la prima partizione è /dev/sda1, per sapere allora dove sta la directory corrispondente nel sysfs uso:
# udevinfo -q path -n /dev/sda1 /block/sda/sda1
quindi:
# ls /sys/block/sda/sda1 alignment_offset discard_alignment inflight size stat uevent
dev holders partition start subsystem
Ora per avere la lista di tutti gli attributi uso:
# udevinfo -a -p /sys/block/sda/sda1/
Udevinfo 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 '/block/sda/sda1': KERNEL=="sda1" SUBSYSTEM=="block" DRIVER=="" ATTR{partition}=="1" ATTR{start}=="32" ATTR{size}=="255968" ATTR{alignment_offset}=="0" ATTR{discard_alignment}=="4294950912" ATTR{stat}==" 29 128 1256 1550 0 0 0 0 0 920 1550" ATTR{inflight}==" 0 0"
looking at parent device '/block/sda': KERNELS=="sda" SUBSYSTEMS=="block" DRIVERS=="" ATTRS{range}=="16" ATTRS{ext_range}=="256" ATTRS{removable}=="1" ATTRS{ro}=="0" ATTRS{size}=="256000" ATTRS{alignment_offset}=="0" ATTRS{discard_alignment}=="0" ATTRS{capability}=="53" ATTRS{stat}==" 34 140 1392 1650 0 0 0 0 0 990 1650" ATTRS{inflight}==" 0 0"
looking at parent device '/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0/host1/target1:0:0/1:0:0:0': KERNELS=="1:0:0:0" SUBSYSTEMS=="scsi" DRIVERS=="sd" ATTRS{device_blocked}=="0" ATTRS{type}=="0" ATTRS{scsi_level}=="0" ATTRS{vendor}=="Generic " ATTRS{model}=="STORAGE DEVICE " ATTRS{rev}=="1.04" ATTRS{state}=="running" ATTRS{timeout}=="30" ATTRS{iocounterbits}=="32" ATTRS{iorequest_cnt}=="0x1d4" ATTRS{iodone_cnt}=="0x1d4" ATTRS{ioerr_cnt}=="0x1" ATTRS{modalias}=="scsi:t-0x00" ATTRS{evt_media_change}=="0" ATTRS{queue_depth}=="1" ATTRS{queue_type}=="none" ATTRS{max_sectors}=="240"
looking at parent device '/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0/host1/target1:0:0': KERNELS=="target1:0:0" SUBSYSTEMS=="" DRIVERS==""
looking at parent device '/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0/host1': KERNELS=="host1" SUBSYSTEMS=="" DRIVERS==""
looking at parent device '/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0': KERNELS=="1-1.3:1.0" SUBSYSTEMS=="usb" DRIVERS=="usb-storage" ATTRS{bInterfaceNumber}=="00" ATTRS{bAlternateSetting}==" 0" ATTRS{bNumEndpoints}=="02" ATTRS{bInterfaceClass}=="08" ATTRS{bInterfaceSubClass}=="06" ATTRS{bInterfaceProtocol}=="50" ATTRS{modalias}=="usb:v0781p7100d0104dc00dsc00dp00ic08isc06ip50" ATTRS{supports_autosuspend}=="0" ATTRS{interface}=="Bulk-Only"
looking at parent device '/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3': KERNELS=="1-1.3" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{configuration}=="" ATTRS{bNumInterfaces}==" 1" ATTRS{bConfigurationValue}=="1" ATTRS{bmAttributes}=="80" ATTRS{bMaxPower}=="100mA" ATTRS{urbnum}=="1101" ATTRS{idVendor}=="0781" ATTRS{idProduct}=="7100" ATTRS{bcdDevice}=="0104" ATTRS{bDeviceClass}=="00" ATTRS{bDeviceSubClass}=="00" ATTRS{bDeviceProtocol}=="00" ATTRS{bNumConfigurations}=="1" ATTRS{bMaxPacketSize0}=="64" ATTRS{speed}=="12" ATTRS{busnum}=="1" ATTRS{devnum}=="4" ATTRS{devpath}=="1.3" ATTRS{version}==" 2.00" ATTRS{maxchild}=="0" ATTRS{quirks}=="0x0" ATTRS{avoid_reset_quirk}=="0" ATTRS{authorized}=="1" ATTRS{manufacturer}=="SanDisk Corporation " ATTRS{product}=="Cruzer Mini " ATTRS{serial}=="000315435"
looking at parent device '/devices/platform/pxa27x-ohci/usb1/1-1': KERNELS=="1-1" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{configuration}=="" ATTRS{bNumInterfaces}==" 1" ATTRS{bConfigurationValue}=="1" ATTRS{bmAttributes}=="e0" ATTRS{bMaxPower}==" 80mA" ATTRS{urbnum}=="65" ATTRS{idVendor}=="04b4" ATTRS{idProduct}=="6560" ATTRS{bcdDevice}=="9415" ATTRS{bDeviceClass}=="09" ATTRS{bDeviceSubClass}=="00" ATTRS{bDeviceProtocol}=="00" ATTRS{bNumConfigurations}=="1" ATTRS{bMaxPacketSize0}=="64" ATTRS{speed}=="12" ATTRS{busnum}=="1" ATTRS{devnum}=="2" ATTRS{devpath}=="1" ATTRS{version}==" 1.10" ATTRS{maxchild}=="4" ATTRS{quirks}=="0x0" ATTRS{avoid_reset_quirk}=="0" ATTRS{authorized}=="1" ATTRS{manufacturer}=="Cypress Semiconductor" ATTRS{product}=="UniHub(four port)" ATTRS{serial}=="DEF1105C8184"
looking at parent device '/devices/platform/pxa27x-ohci/usb1': KERNELS=="usb1" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{configuration}=="" ATTRS{bNumInterfaces}==" 1" ATTRS{bConfigurationValue}=="1" ATTRS{bmAttributes}=="e0" ATTRS{bMaxPower}==" 0mA" ATTRS{urbnum}=="37" ATTRS{idVendor}=="1d6b" ATTRS{idProduct}=="0001" ATTRS{bcdDevice}=="0206" ATTRS{bDeviceClass}=="09" ATTRS{bDeviceSubClass}=="00" ATTRS{bDeviceProtocol}=="00" ATTRS{bNumConfigurations}=="1" ATTRS{bMaxPacketSize0}=="64" ATTRS{speed}=="12" ATTRS{busnum}=="1" ATTRS{devnum}=="1" ATTRS{devpath}=="0" ATTRS{version}==" 1.10" ATTRS{maxchild}=="3" ATTRS{quirks}=="0x0" ATTRS{avoid_reset_quirk}=="0" ATTRS{authorized}=="1" ATTRS{manufacturer}=="Linux 2.6.34-rc1-01016-gf11f317-dirty ohci_hcd" ATTRS{product}=="PXA27x OHCI" ATTRS{serial}=="pxa27x" ATTRS{authorized_default}=="1"
looking at parent device '/devices/platform/pxa27x-ohci': KERNELS=="pxa27x-ohci" SUBSYSTEMS=="platform" DRIVERS=="pxa27x-ohci" ATTRS{modalias}=="platform:pxa27x-ohci"
looking at parent device '/devices/platform': KERNELS=="platform" SUBSYSTEMS=="" DRIVERS==""
A questo punto direi che è molto facile proseguire, per identificare la mia chiavetta posso infatti usare una parola chiave del dispositivo:
SUBSYSTEM=="block"
e gli attributi di un suo padre (parent) e cioè il vendor ID e il product ID del dispositivo USB che implementa la chiavetta (le parole chiavi dei dispositivi padre si identificano come quelli del dispositivo ma agiungendo una S finale):
ATTRS{idVendor}=="04b4"
ATTRS{idProduct}=="6560"
La regola perciò diventa:
SUBSYSTEM=="block", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="6560", NAME="mykey%n", GROUP="disk"
in questo modo posso essere sicuro che quando accederò il nodo /dev/mykey1 accederò senza dubbio alla mia chiavetta e non ad un altro disposiivo USB, magari con stesso vendor ID e product ID ma che non supporti un disco.
Per provare quanto detto mettiamo la regola in un file nella directory /etc/udev/rules.d come segue:
# cat /etc/udev/rules.d/99-test.rules SUBSYSTEM=="block", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="6560", NAME="mykey%n", GROUP="disk"
e poi rilanciamo udev (se abbiamo attivato la funzionalita inotify questo non è necessario); poi inseriamo la chiavetta di nuovo ed ecco che abbiamo i due nodi come richiesto:
# ls -l /dev/my_key* brw-rw---- 1 root disk 8, 0 Apr 19 16:51 /dev/my_key brw-rw---- 1 root disk 8, 1 Apr 19 16:51 /dev/my_key1
Eseguire programmi
Abbiamo detto che l'assegnazione RUN permette di eseguire programmi su di un dispositivo, questo permette una notevole versatilita nella gestione dei dispositivi.
Una volta assegnato a RUN il nome completo di un programma, questo viene esguito per ogni evento che soddisfa la regola, e lo fa ricevendo i dati di ingresso dall'environment che gli passa udev.
Come esempio vediamo questo semplice programmino che manda nei file di log il suo environment:
# cat /usr/local/bin/dump_env #!/bin/bash
( echo "--- dumping environment - begin -----------------------------------------------" ; env ; echo "--- dumping environment - end -------------------------------------------------" ) | logger
E' subito chiaro che se lo utilizziamo in una regola del tipo:
SUBSYSTEM=="block", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="6560", RUN="/usr/local/bin/dump_env"
e lo aggiungiamo al file /etc/udev/rules.d/99-test.rules di prima avremo che, ogni volta che si inserisce la nostra chiavetta, questo verrà eseguito ed invierà il suo environment nei file di log. Se si usa il comando tail per tenere d'occhio i file di log, si può vedere che dopo un inserimento si ottiene:
# tail -f /var/log/syslog Apr 19 17:13:00 debian logger: --- dumping environment - begin ----------------------------------------------- Apr 19 17:13:00 debian logger: PHYSDEVPATH=/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0/host6/target6:0:0/6:0:0:0 Apr 19 17:13:00 debian logger: ID_MODEL=STORAGE_DEVICE Apr 19 17:13:00 debian logger: ID_REVISION=1.04 Apr 19 17:13:00 debian logger: DEVTYPE=disk Apr 19 17:13:00 debian logger: ID_BUS=usb Apr 19 17:13:00 debian logger: SUBSYSTEM=block Apr 19 17:13:00 debian logger: ID_SERIAL=Generic_STORAGE_DEVICE_000315435-0:0 Apr 19 17:13:00 debian logger: DEVPATH=/block/sda Apr 19 17:13:00 debian logger: MINOR=0 Apr 19 17:13:00 debian logger: ACTION=add Apr 19 17:13:00 debian logger: PWD=/ Apr 19 17:13:00 debian logger: UDEV_LOG=3 Apr 19 17:13:00 debian logger: MAJOR=8 Apr 19 17:13:00 debian logger: DEVLINKS=/dev/block/8:0 /dev/disk/by-id/usb-Generic_STORAGE_DEVICE_000315435-0:0 /dev/disk/by-path/platform-pxa27x-ohci-usb-0:1.3:1.0-scsi-0:0:0:0 Apr 19 17:13:00 debian logger: UDEVD_EVENT=1 Apr 19 17:13:00 debian logger: DEVNAME=/dev/mykey Apr 19 17:13:00 debian logger: SHLVL=1 Apr 19 17:13:00 debian logger: PHYSDEVDRIVER=sd Apr 19 17:13:00 debian logger: ID_TYPE=disk Apr 19 17:13:00 debian logger: ID_INSTANCE=0:0 Apr 19 17:13:00 debian logger: ID_VENDOR=Generic Apr 19 17:13:00 debian logger: ID_SERIAL_SHORT=000315435 Apr 19 17:13:00 debian logger: PHYSDEVBUS=scsi Apr 19 17:13:00 debian logger: ID_PATH=platform-pxa27x-ohci-usb-0:1.3:1.0-scsi-0:0:0:0 Apr 19 17:13:00 debian logger: SEQNUM=1904 Apr 19 17:13:00 debian logger: _=/usr/bin/env Apr 19 17:13:00 debian logger: --- dumping environment - end ------------------------------------------------- Apr 19 17:13:00 debian logger: --- dumping environment - begin ----------------------------------------------- Apr 19 17:13:00 debian logger: PHYSDEVPATH=/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0/host6/target6:0:0/6:0:0:0 Apr 19 17:13:00 debian logger: ID_MODEL=STORAGE_DEVICE Apr 19 17:13:00 debian logger: ID_REVISION=1.04 Apr 19 17:13:00 debian logger: DEVTYPE=partition Apr 19 17:13:00 debian logger: ID_FS_LABEL_SAFE=ZYPAD Apr 19 17:13:00 debian logger: ID_FS_LABEL=ZYPAD Apr 19 17:13:00 debian logger: ID_BUS=usb Apr 19 17:13:00 debian logger: ID_FS_LABEL_ENC=ZYPAD Apr 19 17:13:00 debian logger: SUBSYSTEM=block Apr 19 17:13:00 debian logger: ID_SERIAL=Generic_STORAGE_DEVICE_000315435-0:0 Apr 19 17:13:00 debian logger: ID_FS_UUID=34AB-8496 Apr 19 17:13:00 debian logger: DEVPATH=/block/sda/sda1 Apr 19 17:13:00 debian logger: ID_FS_VERSION=FAT16 Apr 19 17:13:00 debian logger: MINOR=1 Apr 19 17:13:00 debian logger: ACTION=add Apr 19 17:13:00 debian logger: PWD=/ Apr 19 17:13:00 debian logger: UDEV_LOG=3 Apr 19 17:13:00 debian logger: ID_FS_TYPE=vfat Apr 19 17:13:00 debian logger: MAJOR=8 Apr 19 17:13:00 debian logger: DEVLINKS=/dev/block/8:1 /dev/disk/by-id/usb-Generic_STORAGE_DEVICE_000315435-0:0-part1 /dev/disk/by-path/platform-pxa27x-ohci-usb-0:1.3:1.0-scsi-0:0:0:0-part1 /dev/disk/by-uuid/34AB-8496 /dev/disk/by-label/ZYPAD Apr 19 17:13:00 debian logger: UDEVD_EVENT=1 Apr 19 17:13:00 debian logger: DEVNAME=/dev/mykey1 Apr 19 17:13:00 debian logger: SHLVL=1 Apr 19 17:13:00 debian logger: ID_FS_USAGE=filesystem Apr 19 17:13:00 debian logger: PHYSDEVDRIVER=sd Apr 19 17:13:00 debian logger: ID_TYPE=disk Apr 19 17:13:00 debian logger: ID_FS_UUID_ENC=34AB-8496 Apr 19 17:13:00 debian logger: ID_INSTANCE=0:0 Apr 19 17:13:00 debian logger: ID_VENDOR=Generic Apr 19 17:13:00 debian logger: ID_SERIAL_SHORT=000315435 Apr 19 17:13:00 debian logger: PHYSDEVBUS=scsi Apr 19 17:13:00 debian logger: ID_PATH=platform-pxa27x-ohci-usb-0:1.3:1.0-scsi-0:0:0:0 Apr 19 17:13:00 debian logger: SEQNUM=1905 Apr 19 17:13:00 debian logger: _=/usr/bin/env Apr 19 17:13:00 debian logger: --- dumping environment - end -------------------------------------------------
da questo output si possono trarre ulteriori informazioni utili ad affinare le regole e/o avere una lista completa delle variabili di environment che il nostro programma si può aspettare.
Monitorare gli eventi
Durante la scrittura delle regole è importante poter verificare quello che udev vede e cosa no, in particolare è comodo poter vedere gli eventi del kernel man mano che questi arrivano, e per farlo si può usare il tool udevadm come segue:
# udevadm monitor --kernel --environment udevmonitor will print the received events for: UEVENT the kernel uevent
In questo modo, quando inserisco la mia chiavetta, ho in output:
UEVENT[1271698507.951940] add /devices/platform/pxa27x-ohci/usb1/1-1/1-1.3 (usb) ACTION=add DEVPATH=/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3 SUBSYSTEM=usb MAJOR=189 MINOR=3 DEVNAME=bus/usb/001/004 DEVTYPE=usb_device PHYSDEVBUS=usb DEVICE=/proc/bus/usb/001/004 PRODUCT=781/7100/104 TYPE=0/0/0 BUSNUM=001 DEVNUM=004 SEQNUM=1792
UEVENT[1271698507.995788] add /devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0 (usb) ACTION=add DEVPATH=/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0 SUBSYSTEM=usb DEVTYPE=usb_interface PHYSDEVBUS=usb DEVICE=/proc/bus/usb/001/004 PRODUCT=781/7100/104 TYPE=0/0/0 INTERFACE=8/6/80 MODALIAS=usb:v0781p7100d0104dc00dsc00dp00ic08isc06ip50 SEQNUM=1793
UEVENT[1271698508.011382] add /class/scsi_host/host1 (scsi_host) ACTION=add DEVPATH=/class/scsi_host/host1 SUBSYSTEM=scsi_host PHYSDEVPATH=/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0 PHYSDEVBUS=usb PHYSDEVDRIVER=usb-storage SEQNUM=1794
UEVENT[1271698509.038852] add /devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0/host1/target1:0:0/1:0:0:0 (scsi) ACTION=add DEVPATH=/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0/host1/target1:0:0/1:0:0:0 SUBSYSTEM=scsi DEVTYPE=scsi_device PHYSDEVBUS=scsi MODALIAS=scsi:t-0x00 SEQNUM=1795
UEVENT[1271698509.039264] add /class/scsi_disk/1:0:0:0 (scsi_disk) ACTION=add DEVPATH=/class/scsi_disk/1:0:0:0 SUBSYSTEM=scsi_disk PHYSDEVPATH=/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0/host1/target1:0:0/1:0:0:0 PHYSDEVBUS=scsi PHYSDEVDRIVER=sd SEQNUM=1796
UEVENT[1271698509.039457] add /class/scsi_device/1:0:0:0 (scsi_device) ACTION=add DEVPATH=/class/scsi_device/1:0:0:0 SUBSYSTEM=scsi_device PHYSDEVPATH=/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0/host1/target1:0:0/1:0:0:0 PHYSDEVBUS=scsi PHYSDEVDRIVER=sd SEQNUM=1797
UEVENT[1271698509.344458] change /devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0/host1/target1:0:0/1:0:0:0 (scsi) ACTION=change DEVPATH=/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0/host1/target1:0:0/1:0:0:0 SUBSYSTEM=scsi SDEV_MEDIA_CHANGE=1 DEVTYPE=scsi_device DRIVER=sd PHYSDEVBUS=scsi PHYSDEVDRIVER=sd MODALIAS=scsi:t-0x00 SEQNUM=1798
UEVENT[1271698509.390284] add /block/sda (block) ACTION=add DEVPATH=/block/sda SUBSYSTEM=block MAJOR=8 MINOR=0 DEVNAME=sda DEVTYPE=disk PHYSDEVPATH=/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0/host1/target1:0:0/1:0:0:0 PHYSDEVBUS=scsi PHYSDEVDRIVER=sd SEQNUM=1799
UEVENT[1271698509.390736] add /block/sda/sda1 (block) ACTION=add DEVPATH=/block/sda/sda1 SUBSYSTEM=block MAJOR=8 MINOR=1 DEVNAME=sda1 DEVTYPE=partition PHYSDEVPATH=/devices/platform/pxa27x-ohci/usb1/1-1/1-1.3/1-1.3:1.0/host1/target1:0:0/1:0:0:0 PHYSDEVBUS=scsi PHYSDEVDRIVER=sd SEQNUM=1800
UEVENT[1271698509.390977] add /class/bdi/8:0 (bdi) ACTION=add DEVPATH=/class/bdi/8:0 SUBSYSTEM=bdi SEQNUM=1801
da cui posso trarre altre utili informazioni per perfezionare le mie regole.
Questa dispensa del corso Programmazione Linux è opera di Rodolfo Giometti (Copyright © 2010) ed è rilasciata dall'autore «as is» (così com'è) e distribuita sotto licenza Creative Commons Attribuzione – Condividi allo stesso modo 2.5 Italia.
|