En introduktion til Udev: Linux-undersystemet til håndtering af enhedshændelser

Udev er det Linux-undersystem, som forsyner din computer med enhedshændelser. På almindeligt dansk betyder det, at det er den kode, der registrerer, når du har ting tilsluttet din computer, f.eks. et netværkskort, eksterne harddiske (herunder USB-drev), mus, tastaturer, joysticks og gamepads, DVD-ROM-drev og så videre. Det gør det til et potentielt nyttigt værktøj, og det er godt nok eksponeret til, at en standardbruger manuelt kan skrive et script til det for at gøre ting som f.eks. at udføre bestemte opgaver, når en bestemt harddisk er tilsluttet.

Denne artikel lærer dig at oprette et udev-script, der udløses af en udev-hændelse, f.eks. tilslutning af en bestemt tommelfingerstation. Når du først har forstået processen til at arbejde med udev, kan du bruge den til at gøre alle mulige ting, f.eks. indlæse en bestemt driver, når et gamepad tilsluttes, eller udføre en automatisk sikkerhedskopiering, når du tilslutter dit backupdrev.

Et grundlæggende script

Den bedste måde at arbejde med udev på er i små bidder. Du skal ikke skrive hele scriptet på forhånd, men i stedet starte med noget, der blot bekræfter, at udev udløser en eller anden brugerdefineret begivenhed.

Afhængigt af dit mål med dit script kan du ikke garantere, at du nogensinde vil se resultaterne af et script med dine egne øjne, så sørg for, at dit script logger, at det blev udløst med succes. Det sædvanlige sted for logfiler er i mappen /var, men det er for det meste root-brugerens domæne. Til test kan du bruge /tmp, som er tilgængelig for normale brugere og normalt bliver renset ud ved en genstart.

Åbn din foretrukne teksteditor, og indtast dette enkle script:

#!/usr/bin/bash
/usr/bin/date >> /tmp/udev.log

Placér dette i /usr/local/bin eller et lignende sted i den eksekverbare standardsti. Kald det trigger.sh, og gør det naturligvis eksekverbart med chmod +x.

$ sudo mv trigger.sh /usr/local/bin
$ sudo chmod +x /usr/local/bin/trigger.sh

Dette script har intet med udev at gøre. Når det udføres, placerer scriptet et tidsstempel i filen /tmp/udev.log. Test selv scriptet:

$ /usr/local/bin/trigger.sh
$ cat /tmp/udev.log
Tue Oct 31 01:05:28 NZDT 2035

Det næste trin er at få udev til at udløse scriptet.

Unique enhedsidentifikation

For at dit script kan udløses af en enhedshændelse, skal udev vide, under hvilke betingelser det skal kalde scriptet. I det virkelige liv kan du identificere et tommelfinger drev ved hjælp af dets farve, producenten og det faktum, at du lige har sat det i din computer. Din computer har imidlertid brug for et andet sæt kriterier.

Udev identificerer enheder ved hjælp af serienumre, producenter og endda leverandør-id- og produkt-id-numre. Da dette er tidligt i dit udev-scripts levetid, skal du være så bred, uspecifik og altomfattende som muligt. Med andre ord ønsker du først at fange næsten enhver gyldig udev-hændelse for at udløse dit script.

Med kommandoen udevadm monitor kan du aflytte udev i realtid og se, hvad den ser, når du sætter forskellige enheder til. Bliv root og prøv det.

$ su
# udevadm monitor

Monitor-funktionen udskriver modtagne hændelser for:

  • UDEV: den hændelse udev sender ud efter regelbehandling
  • KERNEL: kernen uevent

Med udevadm monitor kørende kan du tilslutte et USB-drev og se, hvordan alle mulige oplysninger bliver spyttet ud på din skærm. Bemærk, at hændelsestypen er en ADD-hændelse. Det er en god måde at identificere, hvilken type hændelse du vil have.

Kommandoen udevadm monitor giver en masse gode oplysninger, men du kan se dem med smukkere formatering med kommandoen udevadm info, forudsat at du ved, hvor dit thumbdrev i øjeblikket er placeret i dit /dev-træ. Hvis ikke, skal du trække stikket ud og sætte dit tommelfinger drev i igen, og så skal du straks sende denne kommando:

$ su -c 'dmesg | tail | fgrep -i sd*'

Hvis denne kommando f.eks. returnerede sdb: sdb1, ved du, at kernen har tildelt dit tommelfinger drev etiketten sdb.

Alternativt kan du bruge kommandoen lsblk til at se alle drev, der er tilsluttet dit system, herunder deres størrelser og partitioner.

Nu, hvor du har fastslået, hvor dit drev er placeret i dit filsystem, kan du se udev-oplysninger om denne enhed med denne kommando:

# udevadm info -a -n /dev/sdb | less

Dette returnerer en masse oplysninger. Fokuser på den første blok af oplysninger for nu.

Din opgave er at udvælge de dele af udevs rapport om en enhed, der er mest unikke for den pågældende enhed, og derefter fortælle udev, at han skal udløse dit script, når disse unikke attributter registreres.

Udevadm info-processen rapporterer om en enhed (angivet ved enhedsstien) og “vandrer” derefter opad i kæden af overordnede enheder. For hver enhed, der findes, udskriver den alle mulige attributter ved hjælp af et nøgle-værdiformat. Du kan sammensætte en regel for at matche i henhold til attributterne for en enhed plus attributter fra en enkelt overordnet enhed.

looking at device '/devices/000:000/blah/blah//block/sdb':
KERNEL=="sdb"
SUBSYSTEM=="block"
DRIVER==""
ATTR{ro}=="0"
ATTR{size}=="125722368"
ATTR{stat}==" 2765 1537 5393"
ATTR{range}=="16"
ATTR{discard\_alignment}=="0"
ATTR{removable}=="1"
ATTR{blah}=="blah"

En udev-regel skal indeholde en attribut fra en enkelt overordnet enhed.

Forældreattributter er ting, der beskriver en enhed fra det mest grundlæggende niveau, f.eks. at det er noget, der er blevet tilsluttet en fysisk port, eller det er noget med en størrelse, eller dette er en flytbar enhed.

Da KERNEL-etiketten for sdb kan ændre sig afhængigt af, hvor mange andre drev der var tilsluttet, før du tilsluttede dette tommelfinger drev, er det ikke den optimale forælderattribut for en udev-regel. Det virker dog til et proof of concept, så du kunne bruge det. En endnu bedre kandidat er SUBSYSTEM-attributten, som identificerer, at der er tale om en “blok”-systemenhed (hvilket er grunden til, at lsblk-kommandoen viser enheden).

Åbn en fil kaldet 80-local.rules i /etc/udev/rules.d og indtast denne kode:

SUBSYSTEM=="block", ACTION=="add", RUN+="/usr/local/bin/trigger.sh"

Spar filen, træk stikket ud af dit test-drev og genstart.

Venter, genstart på en Linux-maskine?

Theoretisk set kan du bare udstede udevadm control –reload, hvilket burde indlæse alle regler, men på dette tidspunkt i spillet er det bedst at eliminere alle variabler. Udev er kompleks nok, og du har ikke lyst til at ligge i sengen hele natten og spekulere på, om den regel ikke virkede på grund af en syntaksfejl, eller om du bare skulle have genstartet. Så genstart uanset hvad din POSIX-stolthed fortæller dig.

Når dit system er online igen, skal du skifte til en tekstkonsol (med Ctl+Alt+F3 eller lignende) og tilslutte dit tommelfinger drev. Hvis du kører en nyere kerne, vil du sandsynligvis se en masse output i din konsol, når du sætter drevet i stikket. Hvis du ser en fejlmeddelelse som f.eks. kunne ikke udføre /usr/local/bin/trigger.sh, har du sandsynligvis glemt at gøre scriptet eksekverbart. Ellers er det eneste, du forhåbentlig ser, at en enhed blev tilsluttet, at den fik en eller anden form for kerneenhedstildeling osv.

Nu kommer sandhedens øjeblik:

$ cat /tmp/udev.log
Tue Oct 31 01:35:28 NZDT 2035

Hvis du ser en meget nylig dato og tid returneret fra /tmp/udev.log, har udev med succes udløst dit script.

Forfine reglen til noget nyttigt

Problemet med denne regel er, at den er meget generisk. Hvis du sætter en mus, et tommelfinger drev eller en andens tommelfinger drev i stikket, vil det vilkårligt udløse dit script. Nu er det på tide at begynde at fokusere på præcis det tommestokdrev, som du vil have til at udløse dit script.

En måde at gøre dette på er med leverandør-id og produkt-id. For at få disse numre kan du bruge kommandoen lsusb.

$ lsusb
Bus 001 Device 002: ID 8087:0024 Slacker Corp. Hub
Bus 002 Device 002: ID 8087:0024 Slacker Corp. Hub
Bus 003 Device 005: ID 03f0:3307 TyCoon Corp.
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 hub
Bus 001 Device 003: ID 13d3:5165 SBo Networks

I dette eksempel angiver 03f0:3307 før TyCoon Corp. attributterne idVendor og idProduct. Du kan også se disse numre i output af udevadm info -a -n /dev/sdb | grep vendor, men jeg synes, at output af lsusb er lidt lettere at se.

Du kan nu inkludere disse attributter i din regel.

SUBSYSTEM=="block", ATTRS{idVendor}=="03f0", ACTION=="add", RUN+="/usr/local/bin/thumb.sh"

Test dette (ja, du bør stadig genstarte, bare for at sikre dig, at du får friske reaktioner fra udev), og det skulle virke på samme måde som før, blot hvis du nu tilslutter f.eks. et tommelfinger drev fremstillet af et andet firma (derfor med en anden idVendor) eller en mus eller en printer, vil scriptet ikke blive udløst.

Fortsæt med at tilføje nye attributter for yderligere at fokusere på det ene unikke tommelfinger drev, du vil have til at udløse dit script. Ved hjælp af udevadm info -a -n /dev/sdb kan du finde ud af ting som sælgers navn, nogle gange et serienummer eller produktnavnet osv.

For din egen fornufts skyld skal du sørge for kun at tilføje én ny attribut ad gangen. De fleste fejl jeg har begået (og har set andre folk på nettet begå) er at smide en masse attributter ind i deres udev-regel og undre sig over, at tingen ikke længere virker. At teste attributterne en efter en er den sikreste måde at sikre, at udev kan identificere din enhed med succes.

Sikkerhed

Dette bringer sikkerhedsproblemerne op ved at skrive udev-regler til automatisk at gøre noget, når et drev er sat i stikket. På mine maskiner har jeg ikke engang auto-mount slået til, og alligevel foreslår denne artikel scripts og regler, der udfører kommandoer bare ved at have noget tilsluttet.

To ting, du skal være opmærksom på her:

  1. Fokuser dine udev-regler, når du har fået dem til at fungere, så de kun udløser scripts, når du virkelig ønsker det. Det er en dårlig idé at udføre et script, der blindt kopierer data til eller fra din computer, hvis nogen, der tilfældigvis har et tommestokdrev af samme mærke, sætter det i din boks.
  2. Skriv ikke dine udev-regler og scripts og glem dem. Jeg ved, hvilke computere der har mine udev-regler på dem, og disse bokse er oftest mine personlige computere, ikke dem, jeg tager med rundt til konferencer eller har på mit kontor på arbejdet. Jo mere “social” en computer er, jo mindre sandsynligt er det, at den får en udev-regel på sig, som potentielt kan resultere i, at mine data ender på en andens enhed eller andres data eller malware på min enhed.

Med andre ord, som med så meget af den magt, som et GNU-system giver, er det din opgave at være opmærksom på, hvordan du udøver denne magt. Hvis du misbruger den eller undlader at behandle den med respekt, kan det meget vel gå grueligt galt.

Udev i den virkelige verden

Nu kan du bekræfte, at dit script udløses af udev, og du kan vende din opmærksomhed mod scriptets funktion. Lige nu er det ubrugeligt og gør ikke andet end at logge det faktum, at det er blevet udført.

Jeg bruger udev til at udløse automatiserede sikkerhedskopieringer af mine tommelfinger drev. Ideen er, at masterkopierne af mine aktive dokumenter ligger på mit tommestokdrev (da det følger med overalt, hvor jeg går, og der kan arbejdes på det når som helst), og disse masterdokumenter bliver sikkerhedskopieret til min computer, hver gang jeg sætter drevet til denne maskine. Med andre ord er min computer backup-drevet, og mine produktionsdata er mobile. Kildekoden er tilgængelig, så du er velkommen til at kigge på koden for attachup for yderligere eksempler på begrænsning af dine udev-tests.

Da det er det, jeg bruger udev mest til, er det det eksempel, jeg bruger her, men udev kan gribe fat i mange andre ting, f.eks. gamepads (dette er nyttigt på systemer, der ikke er indstillet til at indlæse xboxdrv-modulet, når et gamepad er tilsluttet) og kameraer og mikrofoner (nyttigt til at indstille indgange, når en bestemt mikrofon er tilsluttet), så vær klar over, at det er godt til meget mere end dette ene eksempel.

En simpel version af mit backup-system er en proces med to kommandoer:

SUBSYSTEM=="block", ATTRS{idVendor}=="03f0", ACTION=="add", SYMLINK+="safety%n"
SUBSYSTEM=="block", ATTRS{idVendor}=="03f0", ACTION=="add", RUN+="/usr/local/bin/trigger.sh"

Den første linje registrerer mit thumb-drev med de attributter, der allerede er diskuteret, og tildeler derefter thumb-drevet et symbolsk link inden for enhedstræet. Det symlink, som den tildeler, er safety%n. %n er et udev-makro, der opløses til det nummer, som kernen giver enheden, f.eks. sdb1, sdb2, sdb3 osv. Så %n ville være 1 eller 2 eller 3.

Dette skaber et symbolsk link i dev-træet, så det forstyrrer ikke den normale proces med at tilslutte en enhed. Det betyder, at hvis du bruger et skrivebordsmiljø, der kan lide at auto-mounte enheder, vil du ikke skabe problemer for det.

Den anden linje kører scriptet.

Mit backup-script ser således ud:

#!/usr/bin/bash
mount /dev/safety1 /mnt/hd
sleep 2
rsync -az /mnt/hd/ /home/seth/backups/ && umount /dev/safety1

Scriptet bruger et symbolsk link, hvilket undgår muligheden for, at udev navngiver drevet noget uventet (hvis jeg f.eks. allerede har et tommelfinger-drev kaldet DISK tilsluttet min computer, og jeg tilslutter mit andet tommelfinger-drev, der også hedder DISK, vil det andet blive mærket DISK_, hvilket ville forpurre mit script). Det monterer safety1 (den første partition på drevet) på mit foretrukne monteringspunkt /mnt/hd.

Når det er sikkert monteret, bruger det rsync til at sikkerhedskopiere drevet til min backup-mappe (mit egentlige script bruger rdiff-backup, og dit kan bruge den automatiserede backup-løsning, du foretrækker).

Udev er din dev

Udev er et meget fleksibelt system og giver dig mulighed for at definere regler og funktioner på måder, som få andre systemer tør give brugerne. Lær det og brug det, og nyd POSIX’s kraft.

Denne artikel bygger på indhold fra Slackermedia Handbook, som er licenseret under GNU Free Documentation License 1.3.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.