Laufwerk/Gerät immer der selben Gerätedateien zuweisen (udev)

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Was ist udev:
    udev steht für userspace /dev (/dev steht für Gerätedatei; siehe engl. device = Gerät) und ist ein Programm, mit welchem der Linux-Kernel Gerätedateien für die Datenein- und -ausgabe (Input/Output) verwaltet.

    udev ersetzt seit dem Kernel 2.6 das früher genutzte devfs-Dateisystem, dessen Aufgaben es damit übernimmt. Genauso wie devfs verwaltet udev das /dev-Verzeichnis, welches die speziellen Gerätedateien enthält, um von Programmen aus auf die vom System zur Verfügung gestellten Geräte zuzugreifen.

    Mit der Einführung von udev waren sowohl udev als auch devfs im Kernel enthalten. Seit Ende Juni 2006 ist nur noch udev enthalten, devfs wurde vollständig entfernt.


    Vorteile:

    • die Zuordnung von Geräten ist eindeutig – es entstehen keine Probleme mehr, wenn man zB die Reihenfolge von zwei angeschlossenen USB-Druckern ändert
    • die Benennung der Geräte kann durch den Nutzer gewählt werden; sie bleibt beständig
    • für die Ermittlung der Gerätenamen können beliebige Verfahren zur Anwendung kommen, neben statischen Listen und Regeln zB auch die Abfrage einer externen Datenbank, wodurch sich beispielsweise Geräte innerhalb eines Unternehmensnetzwerkes – durch ihre Seriennummer identifiziert – einheitlich verhalten
    • die Benennung ist Linux-Standard-Base-konform


    Nachteile:
    Ein bewusst abgewägter Nachteil von udev besteht darin, dass es im Gegensatz zu devfs beim Aufruf eines /dev-Knotens nicht automatisch die entsprechenden Gerätetreiber lädt. Funktioniert beim Einstecken eines Gerätes HotPlug nicht richtig und laden die benötigten Treiber nicht, können diese nicht mit udev nachgeladen werden.


    Arbeitsweise:
    udev überwacht und wertet hotplug-Ereignisse aus. Finden sich dort Informationen über ein neu angeschlossenes Gerät, werden zusätzliche zu diesem Gerät vorhandene Informationen dem sysfs-Dateisystem entnommen und eine neue Gerätedatei im /dev-Verzeichnis erzeugt. Dabei ist der für die spezielle Datei verwendete Name und die Zugriffsberechtigung frei durch Regeln konfigurierbar.


    Sinn eigener Regeln:
    Standardmäßig werden Gerätenamen unter Linux "hochgezählt". So heißt zB der erste USB-Stick /dev/sda1, der zweite /dev/sdb1. Das System kennt zunächst keinen Unterschied zwischen beiden Sticks. Das bringt einige Nachteile mit sich, wie folgende Beispiele zeigen:

    • Auf einer USB-Festplatte liegen .mp3-Dateien. Da die Platte immer anders eingebunden wird, wird die Sammlung vom Mediaplayer immer neu eingelesen.
    • Ein Gerät verschwindet plötzlich aus dem System, Gerätedateien, z.B. /dev/cdrom funktionieren nicht mehr. Der Grund: SCSI-Geräte werden bei Fehlern einfach "hochgezählt" und fehlerhafte Gerätedateien verworfen. Ein zunächst als sr0 eingebundenes Gerät heißt nach einem Trennen der Verbindung ("disconnect") etwa sr1, und der zugehörige Verweis - z.B. von /dev/cdrom auf /dev/sr0 - ist dann nicht mehr verwendbar.
    • Ein Skript soll von oder zu einem Laufwerk kopieren (z.B. bei der Datensicherung oder beim Kopieren von SD-Karten), ohne von der Gerätebezeichnung abhängig zu sein.

    Durch die Verwendung eigener Regeln ist es möglich, Geräte mit eindeutig zugeordneten Gerätenamen im Dateisystem einzubinden (siehe Erstellen eigener Regeln). Durch die umfangreichen Möglichkeiten von udev lässt sich auch erreichen, dass beim Anstecken eines Gerätes ein Skript ausgeführt wird. Das kann z.B. sehr hilfreich für die Datensicherung sein (siehe Automatischer Start eines Skripts).


    Erstellen eigener udev-Regeln:
    Hier wird die Erstellung eigener udev-Regeln am Beispiel einer USB-Festplatte erklärt. Die Aufgabe ist, die USB-Festplatte beim Einschalten oder Anstecken automatisch einzubinden und ein Backup-Skript zu starten, um bequem eine Datensicherung durchzuführen.
    Letztlich geht es darum, dass das Gerät immer unter einem spezifischen Gerätenamen verfügbar ist.

    Auslesen von Informationen
    Zunächst müssen möglichst eindeutige Informationen über das jeweilige Gerät ausgelesen werden.

    Als Kriterien für eine udev-Regel empfiehlt sich insbesondere:

    • Die Seriennummer des Geräts (falls vorhanden und nicht NULL).
    • Die Kombination aus Herstellerkennung und Produktkennung.
    • Die Kombination aus Hersteller und Modellbezeichnung.

    Informationen kann man auf mehrerlei Weise sammeln. Je nachdem, um was für ein Gerät es sich handelt (und wie es angeschlossen wurde), eignen sich hierfür unterschiedliche Werkzeuge. Als Universalwerkzeug kann udevadm eingesetzt werden.
    Ich gehe hier aber nur auf die bekanntest Methode mithilfe von lsusb ein.


    Daten über ein USB-Gerät herausfinden mit lsusb

    Zuerst sollte man das Gerät anstecken oder einschalten. Ob das Gerät erkannt wurde lässt sich mit dmesg überprüfen.
    Jetzt führt man folgenden Befehl aus:

    Code
    lsusb

    Die Ausgabe sieht dann in etwa wie folgt aus:

    Code
    Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp. 
    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. 
    Bus 001 Device 004: ID 174c:5106 ASMedia Technology Inc. Transcend StoreJet 25M3

    Anhand der Bus-Nummer und der Device-Nummer fragt man jetzt mit der Option -v detailliertere Informationen über das Gerät ab:

    Code
    lsusb -vs  001:004

    Interessant sind vor allem die Zeilen idVendor, idProduct und iSerial, da sich ihre Werte wegen ihrer Eindeutigkeit relativ gut für eine udev-Regel verwenden lassen. Mit diesen Angaben lassen sich bereits funktionierende Bedingungen für eine eigene udev-Regel bauen, wobei man die vorangestellte 0x ignoriert. Das lässt sich auch über die Ausgabe des Befehls dmesg kontrollieren, da sollte diese Werte ebenfalls auftauchen, also in diesem Fall idVendor=174c usw

    Eine weitere Möglichkeit die Informationen zu erhalten die uns interessieren wäre - und der gilt als sicherer sowie eindeutiger - die Events von udev ausgeben zu lassen, was uns in diesem Fall nicht die ATTRibute sondern die ENVironment Variablen aufzeigen würde mit denen udev arbeiten kann. Dazu führt ihr (mit root Rechten) folgenden Befehl aus, bevor ihr das Device einsteckt oder abzieht:

    Code
    udevadm monitor --environment --udev


    udev-Regel schreiben:
    Eigene udev-Regeln erstellt man in einem Editor mit Root-Rechten und legt sie im Verzeichnis /etc/udev/rules.d/ ab.
    Um sicher zu gehen, dass die eigenen Regeln nicht anschließend von Systemstandards überschrieben werden, sollte man den Dateinamen mit einer hohen Zahl oder ohne Zahl beginnen. Wichtig ist, dass die Dateien, die die Regeln enthalten, mit .rules enden, da sie sonst nicht ausgeführt werden.

    Es ist vorteilhaft für spätere Änderungen, nicht mit Kommentaren zu sparen.

    Code
    # USB-Festplatte für Backups

    Nun geht es an das eigentliche Erstellen der udev-Regel aus den oben ermittelten Werten. Zuerst nimmt man die KERNEL-Information des Geräts selbst (also sda1). Da man ja will, dass die Regel auch bei anderer Nummerierung durch den Kernel zutrifft (zB sdc1), sieht der erste Eintrag so aus:

    • Code
      KERNEL=="sd?1"
    • oder:
      Code
      KERNEL=="sd?[1-9]"
    • oder:
      Code
      KERNEL=="sd*"

    Um sicher zu gehen das aber auch die Rule wirklich nur bei einer Partitions-Nummer (sda1) und nicht dem vollständigen Device (sda) ausgeführt wird, solltet ihr aber auf jedenfall mit der ersten oder zweiten Methode arbeiten!

    Da es aber durch aus vorkommen kann dass das Laufwerk nicht immer als sda erkannt wird, sollte man für diesen Buchstaben auf jeden Fall ein sog Wildcard verwenden, also ? oder *.

    Jetzt kombiniert man diesen Eintrag mit den ermittelten Werten aus einem Elternsystem (Wurde die Abfrage mit lsusb gemacht, nimmt man einfach die ermittelten Werte und fügt sie wie folgt ein):

    Code
    KERNEL=="sd?1", SUBSYSTEMS=="usb", ATTRS{serial}=="G5L061KQ66TM7K9I99J7"


    Ist die iSerial nicht gesetzt, kann man die Erkennung auch alternativ über idProduct machen:

    Code
    KERNEL=="sd?1", SUBSYSTEMS=="usb", ATTRS{idProduct}=="5106"


    Oder über idVendor:

    Code
    KERNEL=="sd?1", SUBSYSTEMS=="usb", ATTRS{idVendor}=="174c"


    Oder über eine Kombination aus beiden.

    Damit hat man bereits eindeutige Bedingungen für das Gerät definiert. Nun folgt die Zuweisung, hier der zusätzliche Gerätename in /dev/:

    Code
    KERNEL=="sd?1", SUBSYSTEMS=="usb", ATTRS{serial}=="G5L061KQ66TM7K9I99J7", SYMLINK+="Backup"


    Das Gerät steht dann künftig unter /dev/Backup zur Verfügung.

    Nun gibt es noch einen weiteren wichtigen Teil den man bei einem 'removeable device' beachten sollte, nämlich das man das Gerät ggf erst dann einsteckt wenn das System aber bereits hochgefahren ist, wie zB ein USB-Laufwerk. Damit die Rule also nicht jedesmal ausgeführt wird wenn man das Gerät sowohl einsteckt als auch abzieht, muss man die Rule um die ACTION erweitern:

    • Der Punkt ACTION=="add" sorgt dafür, dass die Regel nur zutrifft wenn das Gerät neu angeschlossen wird.
    • Der Punkt ACTION=="remove" sorgt dafür, dass die Regel nur zutrifft wenn das Gerät abgezogen wird.


    Hinweis: Ihr solltet unbedingt darauf achten keine Leerzeichen oder Sonderzeichen für den Symlink anzugeben!

    Abschließend muss die Datei gespeichert werden, zB als /etc/udev/rules.d/70-usb-storage-custom.rules
    Ein paar Richtlinien, nach denen man die Zahl am Anfang aussuchen sollte, finden sich in der Datei /etc/udev/rules.d/README

    Eine Auflistung zur Regelsyntax findet ihr > hier <


    Automatischer Start eines Skripts:

    Man kann über udev ein Skript automatisch starten lassen, wenn ein Gerät erkannt wird. Hierzu werden der udev-Regel zwei weitere Punkte hinzugefügt:

    • Der Punkt ACTION=="add" sorgt dafür, dass die Regel nur zutrifft wenn das Gerät neu angeschlossen wird, und sollte nach Möglichkeit verwendet werden:

      Code
      ACTION=="add", KERNEL=="sd?1", SUBSYSTEMS=="usb", ATTRS{serial}=="G5L061KQ66TM7K9I99J7", RUN+="/bin/bash /usr/local/bin/backup.sh"
    • Der Punkt RUN+="/bin/bash /usr/local/bin/backup.sh" ruft das Skript /usr/local/bin/backup.sh auf. Dabei muss darauf geachtet werden, dass das Skript mit vollständiger Pfadangabe aufgerufen wird.
      Dem Punkt RUN kann immer nur ein einzelner Befehl (mit Parametern) übergeben werden. Allerdings lassen sich über mehrere RUN+="..."-Einträge mehrere Befehle hintereinander ausführen:

      Code
      ACTION=="add", KERNEL=="sd?1", SUBSYSTEMS=="usb", ATTRS{serial}=="G5L061KQ66TM7K9I99J7", RUN+="/usr/bin/logger Starte Backup.", RUN+="/usr/local/bin/backup.sh"


      In obigem Beispiel wird nach dem Anstecken der Festplatte zuerst die Zeile "Starte Backup." mit logger in das Systemprotokoll geschrieben und danach das Skript /usr/local/bin/backup gestartet. Voraussetzung ist eine gültige Shebang-Zeile.

    • Wenn das Gerät auch während des Betriebs abgezogen werden können soll, dann muss man noch eine neue/weitere ACTION Zeile mit remove einfügen, wobei hierbei darauf zu achten ist das keine ATTR's genutzt werden können da udev beim abziehen diese nicht mehr auslesen kann, ihr müsstet also zur absolut eindeutigen Zuweisung die Environment Variablen verwenden:

      Code
      ACTION=="remove", KERNEL=="sd?1", SUBSYSTEMS=="usb", ENV{ID_SERIAL_SHORT}=="G5L061KQ66TM7K9I99J7", RUN+="/usr/bin/logger Laufwerk /dev/%k wurde entfernt."


      %k ist hierbei eine Variable von UDEV in der der DeviceName hinterlegt ist.

      Zitat

      %k evaluates to the kernel name for the device, e.g. "sda3" for a device that would (by default) appear at /dev/sda3.

      Quelle

    Die vollständige Rule würde für dieses Beispiel also wie folgt aussehen:

    Code
    ACTION=="add", KERNEL=="sd?1", SUBSYSTEMS=="usb", ATTRS{serial}=="G5L061KQ66TM7K9I99J7", RUN+="/usr/bin/logger Starte Backup.", RUN+="/usr/local/bin/backup.sh"
    ACTION=="remove", KERNEL=="sd?1", SUBSYSTEMS=="usb", ENV{ID_SERIAL_SHORT}=="G5L061KQ66TM7K9I99J7", RUN+="/usr/bin/logger Laufwerk /dev/%k wurde entfernt."


    Ein weiteres Beispiel wie solch ein Script aussehen könnte, wäre folgendes:

    Spoiler anzeigen

    Das bewirkt folgendes:

    • Jede Partition eines USB-Laufwerks was eingestöpselt wird, erhält im Ordner /mnt/ ein eigenes Verzeichnis und wird dort eingehängt, zum Beispiel /mnt/sdb1 bis /mnt/sdb9
    • In meinem Fall wird anschließend eine auf dem USB-Laufwerk befindliches swapfile ins System eingebunden - da ich diese nicht auf die SD legen will.


    Neustart des udev-Systems
    Ein Neustart von udev ist eigentlich nicht erforderlich, da udev beim Anschließen eines neuen Gerätes automatisch die Regeln im Verzeichnis /etc/udev/rules.d/ durchgeht. Wurde die Regel für entfernbare Geräte erstellt, können diese einfach entfernt und wieder eingesteckt werden. Wurden dagegen Regeln für fest eingebaute Geräte (zB PCI-Karten) festgelegt, muss udev aber dazu veranlasst werden, alle Geräte neu einzulesen. Dies geschieht mittels:

    Code
    udevadm trigger

    Falls es dennoch notwendig sein sollte, udev neu zu starten, verwendet man den Befehl:

    Code
    /etc/init.d/udev restart


    Funktionstest
    Ein Funktionstest ist recht einfach durchzuführen: Dazu muss man einfach die USB-Festplatte abziehen und neu anstecken. Ist dann eine Gerätedatei /dev/backup erstellt worden, hat alles geklappt. Wenn nicht, dann ist an der udev-Regel etwas falsch, und man sollte sie korrigieren. Dabei helfen verschiedene Werkzeuge.

    Das System prüft eine udev-Regel in /etc/udev/rules.d/ sofort beim Speichern der Datei und protokolliert mögliche Fehler in /var/log/syslog. Es empfiehlt sich deshalb, die Log-Datei in einem Fenster mit tail -n 10 -f /var/log/syslog offen zu halten. Selbst auskommentierte Zeilen werden hier auf ihre Syntax geprüft, auch wenn sie nicht angewendet werden.


    Weitere Hinweise:
    Einige Script's sowie udev-Regeln die ich hier im Forum gepostet habe findet ihr > hier < , > hier < , > hier < und > hier < .
    Eine weitere Möglichkeit besteht darin den 'physical port' in dem das Gerät eingesteckt wurde immer dem selben /dev/ Devicenamen zuzuweisen. Wie das genau gemeint ist wird da beschrieben: http://askubuntu.com/a/50412


    Quellen:
    mein eigenes (geheimes) Wiki, und Ubuntuwiki

  • Laufwerk/Gerät immer der selben Gerätedateien zuweisen (udev)? Schau mal ob du hier fündig wirst!

  • Hi meigrafd,

    Super, ist mit den Erkenntnissen die über meine Fragerei gewonnen ergänzt, obwohl ich noch nicht alles durchgelesen habe. Eines aber fiel mir auf, prüfe das doch mal ob das (allg.) auch zutrifft: als ATTRS ist es ja vermutl. sinnvoll, die Ser.-Nr. zu verwenden, da die eindeutig sein soll. Die konnte ich finden unter sudo udevadm test /sys/class/block/sd* (beachten sd*), das ist aber rel. unübersichtlich. Besser kam ich dran mit sudo lsusb -vs <Device-Nr>.

    Viele Grüße, Charly

    Einmal editiert, zuletzt von karomue (28. September 2014 um 13:46)

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!