En introduktion till Udev: Linux-undersystemet för hantering av enhetshändelser

Udev är Linux-undersystemet som förser din dator med enhetshändelser. I klartext betyder det att det är koden som upptäcker när du har saker anslutna till datorn, som nätverkskort, externa hårddiskar (inklusive USB-minnen), möss, tangentbord, joysticks och gamepads, DVD-ROM-enheter och så vidare. Det gör det till ett potentiellt användbart verktyg, och det är tillräckligt väl exponerat för att en vanlig användare manuellt kan skriva ett skript för att göra saker som att utföra vissa uppgifter när en viss hårddisk är inkopplad.

Den här artikeln lär dig hur du skapar ett udev-skript som utlöses av en udev-händelse, till exempel när du kopplar in en viss USB-minnesenhet. När du väl har förstått processen för att arbeta med udev kan du använda den för att göra alla möjliga saker, som att ladda en specifik drivrutin när en gamepad ansluts, eller utföra en automatisk säkerhetskopiering när du ansluter din säkerhetskopieringsenhet.

Ett grundläggande skript

Det bästa sättet att arbeta med udev är i små bitar. Skriv inte hela skriptet i förväg, utan börja istället med något som helt enkelt bekräftar att udev utlöser någon anpassad händelse.

Avhängigt av ditt mål med skriptet kan du inte garantera att du någonsin kommer att se resultatet av ett skript med egna ögon, så se till att ditt skript loggar att det utlöstes framgångsrikt. Den vanliga platsen för loggfiler är i katalogen /var, men det är oftast rotanvändarens domän. För testning kan du använda /tmp, som är tillgänglig för vanliga användare och som vanligtvis rensas vid en omstart.

Öppna din favorittextredigerare och skriv in det här enkla skriptet:

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

Placera det här i /usr/local/bin eller någon annan plats i den standardmässiga exekverbara sökvägen. Kalla det trigger.sh och gör det naturligtvis körbart med chmod +x.

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

Detta skript har inget att göra med udev. När skriptet körs placerar det en tidsstämpel i filen /tmp/udev.log. Testa skriptet själv:

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

Nästa steg är att få udev att utlösa skriptet.

Utomatisk enhetsidentifiering

För att ditt skript ska kunna utlösas av en enhetshändelse måste udev veta under vilka förutsättningar det ska kalla skriptet. I verkligheten kan du identifiera en tummenhet genom dess färg, tillverkaren och det faktum att du just har satt in den i din dator. Din dator behöver dock en annan uppsättning kriterier.

Udev identifierar enheter med hjälp av serienummer, tillverkare och till och med nummer för leverantörs- och produkt-ID. Eftersom det här är tidigt i ditt udev-skript, ska du vara så bred, ospecifik och allomfattande som möjligt. Med andra ord vill du först fånga upp nästan alla giltiga udev-händelser för att utlösa ditt skript.

Med kommandot udevadm monitor kan du koppla in udev i realtid och se vad den ser när du kopplar in olika enheter. Bli root och prova det.

$ su
# udevadm monitor

Monitor-funktionen skriver ut mottagna händelser för:

  • UDEV: händelsen udev skickar ut efter regelbearbetning
  • KERNEL: kärnans uevent

Med udevadm monitor igång kan du koppla in en USB-minnessticka och se hur all slags information spottas ut på din skärm. Lägg märke till att händelsetypen är en ADD-händelse. Det är ett bra sätt att identifiera vilken typ av händelse du vill ha.

Kommandot udevadm monitor ger mycket bra information, men du kan se den med vackrare formatering med kommandot udevadm info, förutsatt att du vet var din tummenhet för tillfället är placerad i ditt /dev-träd. Om inte, koppla ur och koppla in din tummenhet igen och ge sedan omedelbart det här kommandot:

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

Om det kommandot till exempel gav resultatet sdb: sdb1 vet du att kärnan har tilldelat din tummenhet etiketten sdb.

Alternativt kan du använda kommandot lsblk för att se alla enheter som är anslutna till ditt system, inklusive deras storlekar och partitioner.

När du nu har fastställt var din enhet är placerad i ditt filsystem kan du visa udev-informationen om den enheten med det här kommandot:

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

Det här returnerar en hel del information. Fokusera på det första blocket med information för tillfället.

Ditt jobb är att välja ut delar av udevs rapport om en enhet som är mest unika för den enheten, och sedan tala om för udev att utlösa ditt skript när dessa unika attribut upptäcks.

Processen udevadm info rapporterar om en enhet (specificerad av enhetssökvägen), och ”går” sedan uppåt i kedjan av överordnade enheter. För varje enhet som hittas skriver den ut alla möjliga attribut med hjälp av ett nyckel-värdeformat. Du kan komponera en regel för att matcha enligt en enhets attribut plus attribut från en enda överordnad enhet.

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 måste innehålla ett attribut från en enda överordnad enhet.

Föräldraattribut är saker som beskriver en enhet från den mest grundläggande nivån, t.ex. att det är något som har satts in i en fysisk port eller att det är något med en storlek eller att det här är en flyttbar enhet.

Med tanke på att KERNEL-etiketten för sdb kan ändras beroende på hur många andra enheter som sattes in innan du satte in det där USB-minnet är det inte det optimala föräldraattributet för en udev-regel. Det fungerar dock för ett proof of concept, så du kan använda det. En ännu bättre kandidat är SUBSYSTEM-attributet, som identifierar att detta är en ”block”-systemenhet (vilket är anledningen till att kommandot lsblk listar enheten).

Öppna en fil som heter 80-local.rules i /etc/udev/rules.d och skriv in den här koden:

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

Spara filen, koppla ur ditt testminne och starta om.

Vänta, starta om på en Linux-maskin?

Teoretiskt sett kan du bara utfärda udevadm control –reload, vilket borde ladda alla regler, men i det här skedet i spelet är det bäst att eliminera alla variabler. Udev är tillräckligt komplext och du vill inte ligga i sängen hela natten och undra om den regeln inte fungerade på grund av ett syntaxfel eller om du bara borde ha startat om. Så starta om oavsett vad din POSIX-stolthet säger till dig.

När ditt system är online igen, växla till en textkonsol (med Ctl+Alt+F3 eller liknande) och koppla in ditt USB-minne. Om du kör en nyare kärna kommer du förmodligen att se en massa utdata i konsolen när du kopplar in enheten. Om du ser ett felmeddelande som till exempel Could not execute /usr/local/bin/trigger.sh har du förmodligen glömt att göra skriptet körbart. Annars är förhoppningsvis allt du ser att en enhet sattes in, den fick någon form av kärnenhetstilldelning och så vidare.

Nu kommer sanningens ögonblick:

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

Om du ser ett mycket aktuellt datum och en mycket aktuell tid som returneras från /tmp/udev.log, så har udev lyckats trigga ditt skript.

Förbättra regeln till något användbart

Problemet med den här regeln är att den är mycket generisk. Att koppla in en mus, en tummenhet eller någon annans tummenhet kommer urskillningslöst att utlösa ditt skript. Nu är det dags att börja fokusera på exakt den tummenhet du vill ska utlösa ditt skript.

Ett sätt att göra detta är med leverantörs-ID och produkt-ID. För att få fram dessa nummer kan du använda kommandot 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 det här exemplet anger 03f0:3307 före TyCoon Corp. attributen idVendor och idProduct. Du kan också se dessa nummer i utmatningen av udevadm info -a -n /dev/sdb | grep vendor, men jag tycker att utmatningen av lsusb är lite lättare för ögonen.

Du kan nu inkludera dessa attribut i din regel.

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

Testa detta (ja, du bör fortfarande starta om, bara för att försäkra dig om att du får färska reaktioner från udev), och det bör fungera på samma sätt som tidigare, bara att om du nu kopplar in, säg, en tummenhet som tillverkas av ett annat företag (därför med en annan idVendor) eller en mus eller en skrivare, så kommer inte skriptet att utlösas.

Fortsätt att lägga till nya attribut för att ytterligare fokusera på den där unika tummenheten som du vill att skriptet ska utlösa. Med hjälp av udevadm info -a -n /dev/sdb kan du ta reda på saker som säljarens namn, ibland ett serienummer eller produktnamnet och så vidare.

För ditt eget förnuft bör du se till att bara lägga till ett nytt attribut åt gången. De flesta misstag jag har gjort (och har sett andra personer på nätet göra) är att slänga in en massa attribut i sin udev-regel och undra varför saken inte längre fungerar. Att testa attributen ett efter ett är det säkraste sättet att se till att udev kan identifiera din enhet framgångsrikt.

Säkerhet

Detta tar upp säkerhetsfrågorna när det gäller att skriva udev-regler för att automatiskt göra något när en enhet är inkopplad. På mina maskiner har jag inte ens auto-mount aktiverat, och ändå föreslås i den här artikeln skript och regler som utför kommandon bara genom att något är inkopplat.

Två saker att tänka på här:

  1. Fokusera dina udev-regler när du väl har fått dem att fungera, så att de endast utlöser skript när du verkligen vill att de ska göra det. Att köra ett skript som blint kopierar data till eller från din dator är en dålig idé om någon som råkar ha med sig ett USB-minne av samma märke stoppar in det i din box.
  2. Skriv inte dina udev-regler och skript och glömma bort dem. Jag vet vilka datorer som har mina udev-regler på sig, och dessa lådor är oftast mina personliga datorer, inte de som jag tar med mig på konferenser eller har på mitt kontor på jobbet. Ju mer ”social” en dator är, desto mindre sannolikt är det att den får en udev-regel på sig som potentiellt kan resultera i att mina data hamnar på någon annans enhet eller någon annans data eller skadlig kod på min enhet.

Med andra ord, som med så mycket av den makt som tillhandahålls av ett GNU-system, är det din uppgift att vara uppmärksam på hur du använder denna makt. Om du missbrukar den eller inte behandlar den med respekt kan det mycket väl gå fruktansvärt fel.

Udev i den verkliga världen

När du nu kan bekräfta att ditt skript utlöses av udev kan du rikta din uppmärksamhet mot skriptets funktion. Just nu är det värdelöst och gör inget annat än att logga det faktum att det har utförts.

Jag använder udev för att utlösa automatiserade säkerhetskopieringar av mina tumminspelare. Tanken är att huvudkopiorna av mina aktiva dokument finns på min tummenhet (eftersom den följer med mig överallt och kan bearbetas när som helst), och dessa huvuddokument säkerhetskopieras till min dator varje gång jag ansluter enheten till den maskinen. Med andra ord är min dator backup-enheten och mina produktionsdata är mobila. Källkoden finns tillgänglig, så titta gärna på koden för attachup för ytterligare exempel på begränsning av dina udev-tester.

Då det är det som jag använder udev mest till är det exemplet jag använder här, men udev kan ta tag i massor av andra saker, som gamepads (detta är användbart på system som inte är inställda på att ladda xboxdrv-modulen när en gamepad är ansluten) och kameror och mikrofoner (användbart för att ställa in ingångar när en specifik mikrofon är ansluten), så inse att det är bra för mycket mer än detta enda exempel.

En enkel version av mitt säkerhetskopieringssystem är en process med två kommandon:

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örsta raden upptäcker min tummenhet med de attribut som redan diskuterats och tilldelar sedan tummenheten en symlänk i enhetsträdet. Den symlänk som tilldelas är safety%n. %n är ett udev-makro som löser upp till det nummer som kärnan ger enheten, till exempel sdb1, sdb2, sdb3 och så vidare. Så %n skulle vara 1 eller 2 eller 3.

Detta skapar en symlänk i dev-trädet, så det stör inte den normala processen att koppla in en enhet. Detta innebär att om du använder en skrivbordsmiljö som gillar att automatiskt koppla in enheter kommer du inte att orsaka problem för den.

Den andra raden kör skriptet.

Mitt säkerhetskopieringsskript ser ut så här:

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

Skriptet använder sig av en symbolisk länk, vilket undviker möjligheten att udev namnger enheten på något oväntat sätt (om jag till exempel redan har en tummenhet som heter DISK inkopplad i min dator, och jag kopplar in en annan tummenhet som också heter DISK, så kommer den andra att kallas DISK_, vilket skulle sabotera mitt skript). Det monterar safety1 (den första partitionen på enheten) på min föredragna monteringspunkt /mnt/hd.

När den väl är säkert monterad använder den rsync för att säkerhetskopiera enheten till min säkerhetskopieringsmapp (mitt egentliga skript använder rdiff-backup, och ditt kan använda vilken automatiserad säkerhetskopieringslösning som du föredrar).

Udev är din dev

Udev är ett mycket flexibelt system och gör det möjligt för dig att definiera regler och funktioner på ett sätt som få andra system vågar ge användare. Lär dig det och använd det och njut av POSIX kraft.

Denna artikel bygger på innehåll från Slackermedia Handbook, som är licensierad under GNU Free Documentation License 1.3.

Lämna ett svar

Din e-postadress kommer inte publiceras.