Scenario
Macchina CentOS 6 dotata di 2 dischi rigidi, ovvero:
1) /dev/sda dove è presente la partizione di boot (sda1) e quella di root (sda2);
2) /dev/sdb che consta di un’unica partizione dedicata al salvataggio di dati.
Collegando una memoria flash di tipo USB (che funge da dongle per un applicativo in esecuzione sul server) essa viene inizialmente mappata come /dev/sdc. Dopo un riavvio della macchina, però, il kernel name assegnato alla stessa diventa /dev/sda e di conseguenza i 2 dischi rigidi vengono mappati come /dev/sdb e /dev/sdc. Ciò, fortunatamente, non va ad inficiare il funzionamento del server, in quanto il file /etc/fstab contiene gli UUID (univoci e soprattutto statici) assegnati a ciascuna partizione (anzichè il kernel name che, come abbiamo visto, può variare).
Nel mio caso, però, esiste un problema: poichè utilizzo uno specifico tool per monitorare lo stato delle partizioni, le quali vengono interrogate mediante il loro kernel name, dovrei modificare tale parametro di volta in volta, distinguendo il caso in cui i dischi rigidi vengono mappati in modo “standard”, ovvero come /dev/sda e /dev/sdb, dal caso in cui vengono riconosciuti come /dev/sdb e /dev/sdc (con la memoria flash che fa le veci di /dev/sda). Ergo, devo fare in modo che la memoria flash venga mappata sempre e comunque come /dev/sdd.
Soluzione
Quanto sopra può essere realizzato configurando opportunamente udev. Tale applicativo si basa su alcuni file di configurazione, dislocati su 2 directory differenti, ovvero:
1) /lib/udev/rules.d/ che contiene i file di configurazione di default (non vanno editati);
2) /etc/udev/rules.d che contiene i file di configurazione “customizzabili” dall’utente (il cui contenuto ha una maggiore priorità rispetto a quello dei file di default, “scavalcandoli” all’occorrenza).
Tutti i suddetti file vengono dati in pasto ad udev seguendo un ordine crescente dettato esclusivamente dal loro filename. Da ciò si capisce come mai il contenuto tipico della directory /etc/udev/rules.d è simile al seguente:
[root@linuxbox ~]# ls /etc/udev/rules.d/ 60-pcmcia.rules 70-persistent-cd.rules 90-hal.rules 60-fprint-autosuspend.rules 60-raw.rules 70-persistent-net.rules 90-alsa.rules 98-kexec.rules
dove le direttive presenti nel file 60-pcmcia.rules vengono lette (ed eseguite) prima di quelle relative a 70-persistent-cd.rules.
Poichè vogliamo che il file contenente le nostre direttive custom venga valutato per primo, dobbiamo nominarlo come 10-local.rules. Creiamolo quindi utilizzando il comando:
[root@linuxbox ~]# touch /etc/udev/rules.d/10-local.rules
per poi popolarlo come vi indicherò più avanti.
Dopo aver creato il suddetto file, occorre individuare le caratteristiche tipiche di ciascun disco rigido e della memoria flash, utilizzando il comando:
[root@linuxbox ~]# udevadm info -a -n /dev/sdXY
dove X è la lettera che identifica il device ed Y (facoltativo) è il numero di partizione.
Ad esempio, per /dev/sda possiamo utilizzare il comando:
[root@linuxbox ~]# udevadm info -a -n /dev/sda
il cui output sarà simile al seguente:
custom logging function 0x7f90ec575030 registered udevadm[29910]: custom logging function 0x7f90ec575030 registered selinux=1 udevadm[29910]: selinux=1 calling: info udevadm[29910]: calling: info device 0x7f90ec599460 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0/block/sda' udevadm[29910]: device 0x7f90ec599460 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0/block/sda' 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:0b.0/host0/target0:0:0/0:0:0:0/block/sda': KERNEL=="sda" SUBSYSTEM=="block" DRIVER=="" ATTR{range}=="16" ATTR{ext_range}=="256" ATTR{removable}=="0" ATTR{ro}=="0" ATTR{size}=="976773168" ATTR{alignment_offset}=="0" ATTR{discard_alignment}=="0" ATTR{capability}=="52" ATTR{stat}==" 7302543 1320445 841562942 71451185 6119635 28157362 271395642 272451897 0 51376878 343882883" ATTR{inflight}==" 0 0" device 0x7f90ec59ff90 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0' udevadm[29910]: device 0x7f90ec59ff90 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0' looking at parent device '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0': KERNELS=="0:0:0:0" SUBSYSTEMS=="scsi" DRIVERS=="sd" ATTRS{device_blocked}=="0" ATTRS{type}=="0" ATTRS{scsi_level}=="6" ATTRS{vendor}=="ATA " ATTRS{model}=="TOSHIBA HDWJ105 " ATTRS{rev}=="AX00" ATTRS{state}=="running" ATTRS{timeout}=="30" ATTRS{eh_timeout}=="10" ATTRS{iocounterbits}=="32" ATTRS{iorequest_cnt}=="0xd13eaf" ATTRS{iodone_cnt}=="0xcfd529" ATTRS{ioerr_cnt}=="0x1e" ATTRS{modalias}=="scsi:t-0x00" ATTRS{evt_media_change}=="0" ATTRS{evt_inquiry_change_reported}=="0" ATTRS{evt_capacity_change_reported}=="0" ATTRS{evt_soft_threshold_reached}=="0" ATTRS{evt_mode_parameter_change_reported}=="0" ATTRS{evt_lun_change_reported}=="0" ATTRS{dh_state}=="detached" ATTRS{queue_depth}=="31" ATTRS{queue_ramp_up_period}=="120000" ATTRS{queue_type}=="simple" ATTRS{unload_heads}=="0" device 0x7f90ec5a1b20 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0' udevadm[29910]: device 0x7f90ec5a1b20 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0' looking at parent device '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0': KERNELS=="target0:0:0" SUBSYSTEMS=="scsi" DRIVERS=="" device 0x7f90ec5b3e10 has devpath '/devices/pci0000:00/0000:00:0b.0/host0' udevadm[29910]: device 0x7f90ec5b3e10 has devpath '/devices/pci0000:00/0000:00:0b.0/host0' looking at parent device '/devices/pci0000:00/0000:00:0b.0/host0': KERNELS=="host0" SUBSYSTEMS=="scsi" DRIVERS=="" device 0x7f90ec595fb0 has devpath '/devices/pci0000:00/0000:00:0b.0' udevadm[29910]: device 0x7f90ec595fb0 has devpath '/devices/pci0000:00/0000:00:0b.0' looking at parent device '/devices/pci0000:00/0000:00:0b.0': KERNELS=="0000:00:0b.0" SUBSYSTEMS=="pci" DRIVERS=="ahci" ATTRS{vendor}=="0x10de" ATTRS{device}=="0x0ab4" ATTRS{subsystem_vendor}=="0x1043" ATTRS{subsystem_device}=="0x83e2" ATTRS{class}=="0x010185" ATTRS{irq}=="25" ATTRS{local_cpus}=="f" ATTRS{local_cpulist}=="0-3" ATTRS{modalias}=="pci:v000010DEd00000AB4sv00001043sd000083E2bc01sc01i85" ATTRS{numa_node}=="-1" ATTRS{enable}=="1" ATTRS{broken_parity_status}=="0" ATTRS{msi_bus}=="" device 0x7f90ec5a5200 has devpath '/devices/pci0000:00' udevadm[29910]: device 0x7f90ec5a5200 has devpath '/devices/pci0000:00' looking at parent device '/devices/pci0000:00': KERNELS=="pci0000:00" SUBSYSTEMS=="" DRIVERS==""
Discorso simile vale per la partizione 1 del disco /dev/sda:
[root@linuxbox ~]# udevadm info -a -n /dev/sda1
il cui output sarà simile a:
custom logging function 0x7ff9331c9030 registered udevadm[30106]: custom logging function 0x7ff9331c9030 registered selinux=1 udevadm[30106]: selinux=1 calling: info udevadm[30106]: calling: info device 0x7ff9331ed460 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0/block/sda/sda1' udevadm[30106]: device 0x7ff9331ed460 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0/block/sda/sda1' 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:0b.0/host0/target0:0:0/0:0:0:0/block/sda/sda1': KERNEL=="sda1" SUBSYSTEM=="block" DRIVER=="" ATTR{partition}=="1" ATTR{start}=="2048" ATTR{size}=="1024000" ATTR{alignment_offset}=="0" ATTR{discard_alignment}=="0" ATTR{stat}==" 2379 166 368458 10840 34 49 178 1663 0 11039 12484" ATTR{inflight}==" 0 0"
In particolare, utilizzeremo le informazioni riportate come attributi (ATTR) per identificare nel modo più preciso possibile il device/partizione su cui applicare le nostre regole.
Detto ciò, procediamo con la stesura del contenuto relativo al file 10-local.rules, che dovrà essere simile al seguente:
KERNEL=="sd?1", SUBSYSTEM=="block", ATTR{size}=="3915639", NAME="sdd1" KERNEL=="sd*", SUBSYSTEM=="block", ATTR{size}=="3915776", ATTR{capability}=="53", NAME="sdd" KERNEL=="sdb", SUBSYSTEM=="block", ATTR{size}=="976773168", ATTR{capability}=="52", NAME="sda" KERNEL=="sdb1", SUBSYSTEM=="block", ATTR{size}=="1024000", NAME="sda1" KERNEL=="sdb2", SUBSYSTEM=="block", ATTR{size}=="975747072", NAME="sda2" KERNEL=="sdc", SUBSYSTEM=="block", ATTR{size}=="1953525168", ATTR{capability}=="52", NAME="sdb" KERNEL=="sdc1", SUBSYSTEM=="block", ATTR{size}=="1953520002", NAME="sdb1"
La prima direttiva fa in modo che la partizione della memoria flash (con dimensione pari a 3915639 byte) venga rinominata in sdd1. La seconda direttiva, invece, agisce direttamente sul device, rinominandolo in sdd. Da notare che tutti i campi che contengono le condizioni da matchare per l’identificazione del dispositivo/partizione si avvalgono dell’operatore ==, mentre quelle che agiscono su di essi contengono l’operatore di assegnazione, ovvero =. Inoltre, nell’ambito delle suddette direttive, si fa uso dei caratteri speciali ? e *, con il primo che identifica un solo carattere alfanumerico, e * che identifica qualsiasi occorrenza di caratteri alfanumerici (anche nel caso in cui esse siano pari a 0).
Le rimanenti direttive, stilate sulla falsariga della prima, fanno sì che i dischi rigidi vengano rinominati rispettivamente in /dev/sda e /dev/sdb. Ovviamente tale operazione è da intendersi solo ed esclusivamente nel caso in cui, durante la fase di boot della macchina, la memoria flash sia già collegata ad una porta USB della stessa.
Non ci rimane che testare “in place” (e quindi senza reboot) le suddette regole lanciando il comando:
[root@linuxbox ~]# udevadm test /sys/block/sdX
per i device e:
[root@linuxbox ~]# udevadm test /sys/block/sdX/sdXY
per le parizioni, seguito da un:
[root@linuxbox ~]# ls -ilah /dev
per appurare che i dispositivi siano stati effettivamente mappati secondo le nostre necessità.
Per ora è tutto. A presto.