Seite 1 von 1
Wie kann man alle zu einem Inode gehörenden Pfade finden?
Verfasst: Samstag 6. August 2011, 09:51
von snafu
Ich weiß, dass sowas an sich nicht vom Kernel unterstützt wird, da ja die Pfade eigentlich gar nicht zum Inode gehören, sondern eher verschiedene Pfade auf den selben Inode (resp. Datei) *zeigen*. Trotz alledem gibt mir `os.readlink()` (bzw das darauf aufbauende `os.path.realpath()`) ja die Verlinkung zurück.
Zunächst die Frage: Wie funktioniert das Ganze technisch? Ein `os.stat()` gibt neben anderen Informationen nur den Inode an, zumal der Aufruf meines Wissens ja ohnehin vom Pfad in den Inode übersetzt wird und sozusagen die antwortende Ebene sich nur auf den Inode bezieht. Wo hat der `readlink()`-Aufruf also den Pfad her?
Und als zweites: Kann ich diese Kette auch umkehren? Kann ich z.B. herausfinden, dass `/usr/bin/python2.6` von `/usr/bin/python` referenziert wird?
Hintergrund der Geschichte ist die Symbol-Erkennung von Launchit: Beim Durchgehen der Menü-Einträge merke ich mir für jeden Eintrag ein Tupel, welches den Kommando-Pfad (resp. ausführbare Datei) und den Symbol-Namen für den Eintrag beinhaltet. Die Kommando-Pfade werden mittels
launchit.core.get_trimmed() in den Fällen gekürzt, wo sie als absoluter Pfad vorhanden sind, aber ebenso über den reinen Dateinamen erreicht werden können - nämlich dann, wenn das Verzeichnis im $PATH steht. Das (gekürzte) Kommando wird mit dem Symbol-Namen als Wert in ein Wörterbuch zwecks Caching abgelegt.
Das ist alles schön und gut, aber leider kommen mir Verlinkungen in die Quere. Mir ist das vor allem an einer Stelle aufgefallen, wo `update-alternatives` mit im Spiel ist, namentlich bei `policytool`:
Code: Alles auswählen
$ update-alternatives --list policytool
/usr/lib/jvm/java-6-openjdk/jre/bin/policytool
/usr/lib/jvm/java-6-sun/jre/bin/policytool
In meinem Wörterbuch, welches in `launchit.icongetter.icon_cache` steckt, sind die Kommando-Pfade für `policytool` nun in ihrer absoluten Form angegeben, da eben nicht erkannt wird, dass `/usr/bin/policytool` -> `/etc/alternatives/policytool` -> `/usr/lib/jvm/java-6-openjdk/jre/bin/policytool` gilt und das Programm dadurch "direkt" erreicht werden kann. HIerdurch wird das dortige Kommando, welches sozusagen in seiner aufgelösten Form dort (bei dem Menü-Eintrag) steht, nicht umgewandelt - es bleibt bei `/usr/lib/jvm/java-6-openjdk/jre/bin/policytool`.
Als Lösung dafür ist mir bisher nur eingefallen, dass ich versuche, für den Namen (hier: `policytool`) einen Kommandopfad zu erhalten (hatte `is_command()` etwas umgeschrieben) und dann mittels `os.path.samefile()` zu sehen, ob beide Pfade zum selben Inode führen. Das erscheint mir aber irgendwie unsauber, zumal dies für das o.g. `python2.6`-Beispiel nicht funktionieren würde. Ich möchte ungerne auch noch raten, ob Kommandos mit Versionsangabe möglicherweise das selbe tun, wie ein Aufruf ohne die Version.
Was wäre euer Vorschlag zum Lösen des Problems: Sollte ich mir einfach alle Verlinkungen der Dateien aus den Verzeichnissen des $PATH merken und dafür etwas wie `is_alias()` oder so einbauen? Ich habe in der Sache das Gefühl, dass ich eventuell auf dem Holzweg bin - daher frage ich nach...
Re: Wie kann man alle zu einem Inode gehörenden Pfade finden
Verfasst: Samstag 6. August 2011, 12:14
von lunar
@snafu: Du wirfst hier verschiedenes durcheinander, unter anderem weiche und harte Verknüpfungen. "os.readlink()" löst weiche Verknüpfungen auf. Weiche Verknüpfungen aber verweisen nicht auf Inodes, sondern auf Dateipfade. Verweise auf Inodes werden als harte Verknüpfung bezeichnet, und können grundsätzlich auch nicht mit "os.readlink()" aufgelöst werden.
Im Folgenden also geht es nur um weiche Verknüpfungen, Inodes bleiben völlig außen vor. Zu Deiner Frage: Es ist grundsätzlich nicht möglich, weiche Verknüpfungen rückwärts aufzulösen, also alle auf einen Dateipfad verweisende Verknüpfungen zu finden, ohne den gesamten Verzeichnisbaum zu durchsuchen (weiche Verknüpfungen sind, da sie auf Pfade verweisen, im Gegensatz zu harten Verknüpfungen grundsätzlich dateisystemübergreifend).
Ich bin mir über Dein Problem nicht im klaren, da mir die Beschreibung ehrlich gesagt zu konfus war. Kannst Du eventuell anhand des Beispiels mit policytool nochmal klar beschreiben, was Dein Programm jetzt tut, und was es eigentlich tun sollte? Geht es darum, dass Dein Programm "/usr/lib/jvm/java-6-openjdk/jre/bin/policytool" kein Symbol zuordnen kann, weil es nicht erkennt, dass das Kommando "/usr/bin/policytool" auf diesen Pfad verweist?
Re: Wie kann man alle zu einem Inode gehörenden Pfade finden
Verfasst: Samstag 6. August 2011, 13:09
von snafu
lunar hat geschrieben:Ich bin mir über Dein Problem nicht im klaren, da mir die Beschreibung ehrlich gesagt zu konfus war. Kannst Du eventuell anhand des Beispiels mit policytool nochmal klar beschreiben, was Dein Programm jetzt tut, und was es eigentlich tun sollte? Geht es darum, dass Dein Programm "/usr/lib/jvm/java-6-openjdk/jre/bin/policytool" kein Symbol zuordnen kann, weil es nicht erkennt, dass das Kommando "/usr/bin/policytool" auf diesen Pfad verweist?
Genau darum geht es. Ich plane, später den Cache als eine von `dict` abgeleitete Klasse zu implementieren: Greift man über den "einfachen" Dateinamen zu, dann soll der Wert (resp. Symbol-Name) für den Schlüssel `policytool` ebenso zurück gegeben werden, wie bei einem Zugriff über `/usr/bin/policytool` oder gar `/usr/lib/jvm/java-6-openjdk/jre/bin/policytool`. Das dann vorgeschaltete `get_trimmed()` dient also der "Normalisierung" (falls man das so nennen kann). Die Schlüssel sind sozusagen die normalisierten Versionen der Pfade.
Der Ist-Zustand ist so, dass es meinetwegen für `/usr/bin/gedit` wie gewünscht einen Schlüssel namens `gedit` gibt. Im Fall von `policytool` gibt es aber nur einen Schlüssel mit dem Namen `/usr/lib/jvm/java-6-openjdk/jre/bin/policytool`, weil dieser so im entsprechenden Menü-Eintrag als Kommando steht. `get_trimmed()` "sieht" hier nicht die vorhandene Verknüpfung - und das soll sich ändern.
Die Problematik bei der Symbol-Erkennung ist ja leider, dass die Symbole/Symbol-Namen natürlich nicht in der Datei selbst vorhanden sind (etwa als Meta-Information oder so). Es ist auch nicht immer garantiert, dass ein Symbol-Name unbedingt mit dem Programm-/Datei-Namen übereinstimmt. Daher gehe ich den Weg über die Menü-Einträge.
//edit: Also genau genommen (da du dies gefragt hast) kann es `/usr/lib/jvm/java-6-openjdk/jre/bin/policytool` durchaus ein Symbol zuordnen - aber eben nur dafür. Gibt der Anwender ein simples `policytool` ein, dann wird er kein Symbol dafür erhalten.
Re: Wie kann man alle zu einem Inode gehörenden Pfade finden
Verfasst: Samstag 6. August 2011, 13:40
von lunar
@snafu: Ich sehe Dein Problem immer noch nicht. Wenn nicht gewünscht ist, dass das Ziel der Verknüpfung im Wörterbuch landet, wieso löst Du Verknüpfungen dann überhaupt auf?
Mein naiver Ansatz wäre bei der Eingabe von "policytool" einfach, alle Pfade in "$PATH" nach einer ausführbaren Datei namens "policykit" zu durchsuchen, und anschließend den gefundenen Pfad "/usr/bin/policytool" im Wörterbuch abzulegen. Ganz ohne Auflösen von Verknüpfungen, und ganz ohne komplizierte Normalisierung. Kann es sein, dass Du komplizierter als nötig implementierst?
Abgesehen davon kann man Kommandos eigentlich kein Symbol zuordnen, sondern lediglich Anwendungen aus dem "Desktop Menu" (nach "Freedesktop Desktop Menu Specification"(. Insofern kannst Du Symbole eigentlich nur anzeigen, wenn es eine entsprechende Anwendung gibt (wozu Du die möglichen Anwendungen gemäß der erwähnten Standards eben vor dem Kommandos durchsuchen musst). Mit dem Versuch, Pfaden Symbole zuzuordnen, machst Du eigentlich mehr als geht. Der KDE-Anwendungsstarter zumindest zeigt mithin auch keine Symbole für Kommandos an, sondern eben nur für Symbole aus dem "Desktop Menu".
Re: Wie kann man alle zu einem Inode gehörenden Pfade finden
Verfasst: Sonntag 7. August 2011, 13:38
von snafu
@lunar: Nichts für ungut, aber ich glaube, du verstehst mein Problem gerade einfach nicht.
Gehen wir mal wieder zu dem Beispiel mit `gedit` über: Wenn der Benutzer dies in die Kommandozeile des Launchers eingibt oder diesen Eintrag ausgewählt hat, dann erscheint ein passendes Icon dafür. `gedit` selbst wird bei der Ausführung natürlich zu `/usr/bin/gedit`. Diesen Schritt vollzieht `subprocess.Popen()` ja quasi von alleine, weil die $PATH-Variable ohnehin genutzt wird. Es geht jetzt nur noch um das Icon.
Und jetzt hole ich etwas aus: Damit ein Icon überhaupt angezeigt werden kann, muss zunächst einmal ein Pfad gefunden werden, der zum gewünschten Iconnamen passt. Dieser Pfad zeigt auf eine zu gewünschter Größe und gewünschten Theme passende Grafikdatei. Diese Erkennung ist als ein etwas komplizierterer Mechanismus implementiert, der den Regeln der XDG Icon Spezifikation folgt. Unter anderem wird dabei nachgeschaut, ob eine Datei mit einer "Icon-Endung" (z.B. `.png`) an einem bestimmten Ort existiert, wobei der Name vor dieser Endung nicht weiter verändert wird.
Man kann diesen Pfad bei mir mittels `get_iconpath()` aus dem Modul `icongetter` erhalten. Die Funktion erwartet als Argumente den Namen des Icons, die Größe des Icons und den Namen des Themes, für welches der Pfad benötigt wird. Aber jetzt probier mal ein `launchit.icongetter.get_iconpath('gedit', 32, 'gnome')`. Man würde im ersten Moment meinen, dass ein Iconpfad für den Namen `gedit` mit der Größe 32x32 aus dem Theme `gnome` herauskommt. Du wirst jedoch `None` zurückerhalten, was daran liegt, dass es nunmal kein Icon mit dem Namen `gedit` gibt (d.h. kein `gedit.png` oder ähnliches). Und eben dieser Umstand macht die Sache so kompliziert.
Ich weiß also nun, dass Iconnamen nicht immer den Namen "ihrer" Executables/Kommandos entsprechen und muss mir daher irgendwie anders helfen. Der einzig mir bekannte Weg, dieses Problem zu lösen, ist, einfach nachzuschauen, welches Icon in der Praxis, also z.B. bei einer Verknüpfung für `gedit` als Menüeintrag, verwendet wird. Ich muss dafür zwangsläufig alle Menüeinträge durchgehen und meine benötigten Informationen (d.h. Name der ausführbaren Datei und zugehöriger Symbolname) daraus extrahieren. Um Zeit zu sparen, lege ich die Ergebnisse im `icon_cache` ab, welcher einmalig erstellt wird und für spätere Abfragen genutzt wird. Wie dieser Vorgang konkret implementiert ist, soll an dieser Stelle keine Rolle spielen. Man muss eigentlich nur wissen, dass der Eintrag im Cache für Gedit den Schlüssel `gedit` und den Wert `accessories-text-editor` hat.
Wir haben also jetzt etwas, wofür `get_iconpath()` ein brauchbares Ergebnis liefert, wenn es den genanten Wert aus dem Icon-Cache erhält und müssten eigentlich glücklich darüber sein. Mithilfe des besagten `get_trimmed()` kann man sogar `/usr/bin/gedit` abfragen und erhält auch damit das Ergebnis für den entsprechend übersetzten Schlüssel `gedit`. Es wäre aber ebenso denkbar, dass der Schlüsselname von vornherein auf `/usr/bin/gedit` lautet und man die nötig werdende Übersetzung im Fall von `gedit` sozusagen umgekehrt macht, damit ein tatsächlich existierender Pfad (namentlich: `/usr/bin/gedit`) herauskommt. Dies ist nicht das eigentliche Problem.
Vielmehr sitzt der Verursacher für das Problem mit `policytool` in seinem Menüeintrag. Dort steht nämlich kein `/usr/bin/policytool`, sondern `/usr/lib/jvm/java-6-openjdk/jre/bin/policytool`. Und in dieser letztgenannten Form lautet dadurch auch der entsprechende Eintrag im Icon-Cache. Da steht also gerade *nicht* etwas wie `policytool` oder `/usr/bin/policytool` als Schlüsselname drin, sondern eben die aufgelöste Variante.
Hm, was nun? Mein "Übersetzer" weiß nicht, dass ein Anwender durch die Eingabe von `policytool` quasi über Umwege etwas aufrufen kann, das in Wirklichkeit `/usr/lib/jvm/java-6-openjdk/jre/bin/policytool` lautet. Folglich weiß mein `icongetter` auch nicht, dass er das Icon in seinem Cache unter `/usr/lib/jvm/java-6-openjdk/jre/bin/policytool` finden wird. Er gibt mir also in dem Fall `None` zurück und die Implementierung fällt auf das allgemeine Symbol für ausführbare Dateien zurück (bitte den letzten Teil des Satzes erstmal als gegeben hinnehmen, damit nicht noch mehr Verwirrung entsteht).
Was ist also mein Ziel? Ich will, dass schon beim Schritt des Extrahierens der Informationen aus dem Menüeintrag bzw spätestens beim Anlegen der Werte für den Icon-Cache erkannt wird, dass es für den gefundenen Eintrag namens `/usr/lib/jvm/java-6-openjdk/jre/bin/policytool` ein Kommando mit dem Namen `policytool` gibt, welches letztlich auf eben diesen Eintrag zeigt. Analog lege ich an der Stelle, wo ich `/usr/bin/gedit` in den Menüeinträgen finde, ja auch den Schlüssel `gedit` an und eben nicht `/usr/bin/gedit`.
Das war jetzt sehr viel Text, aber ich hoffe, er hat zum besseren Verständnis beigetragen. Ich löse also die Namen nicht auf, sondern sie sind schon aufgelöst vorhanden und ich mache quasi die Rückübersetzung. Einzig und allein Verlinkungen kommen mir in die Quere. Zumindest dann, wenn der Menüeintrag das aufruft, was man *nach* der Verlinkung hätte. Sind mein Problem und mein Ziel jetzt klarer geworden?
Re: Wie kann man alle zu einem Inode gehörenden Pfade finden
Verfasst: Sonntag 7. August 2011, 16:29
von lunar
@snafu: Dein Problem im engeren Sinne habe ich offenbar doch richtig verstanden. Um es zu lösen, bleibt Dir aber tatsächlich nichts anderes als das, was Du schon im ersten Beitrag festgestellt hast: Alle Verknüpfungen aus $PATH heraus merken und auf den "kanonischen" Namen abbilden.
Allerdings kenne ich auch die Spezifikationen, was ich daher nicht verstanden habe, ist das Problem im weiteren Sinne, oder besser gesagt, welches Problem Du mit Deiner Implementierung zu lösen versuchst.
Du hast doch offensichtlich bereits das Desktop-Menü geladen, und bist auch in der Lage, Symbole aus diesem Menü zu laden. Wieso durchsuchst Du dann nicht einfach dieses Desktop-Menü nach einer Anwendung "policykit"? Besser gesagt, wieso durchsuchst Du nicht einfach die passenden Felder (e.g. "Name", "GenericName" und "Exec") aller Desktop-Dateien im Desktop-Menü nach "policykit"? So gehen die Anwendungsstarter von KDE und Gnome vor. Das ist viel einfacher als der Versuch, "policykit" im $PATH zu suchen, und dann die passende Anwendung dazu zu rekonstruieren, weil Du im Falle eines Treffers in einer Desktop-Datei ja sofort alle nötigen Informationen wie das Symbol sofort aus der passenden Datei auslesen kannst. Das ist auch das, was der Benutzer erwartet, denn im Desktop-Menü registrierte Anwendungen sind dazu gedacht, in einer graphischen Oberfläche interaktiv gestartet zu werden, während die große Mehrheit der Kommandos im $PATH eigentlich keinen unmittelbaren interaktiven Nutzen in einer graphischen Oberfläche hat.
Nur um das nochmal zu wiederholen: Kein Anwendungsstarter durchsucht "$PATH" und versucht dann, Symbole zu gefundenen Kommandos zu "erraten". Der KDE-Anwendungsstarter zeigt bei Eingabe von "kate" zwei Einträge an: Einen, der tatsächlich auf das ausführbare Kommando "/usr/bin/kate" verweist, welches durch Suche in $PATH gefunden wurde. Dieser Eintrag hat kein Symbol. Der zweite Eintrag dagegen verweist auf die Anwendung "kate.desktop", welche durch eine Suche im Desktop-Menü gefunden wurde. Dieser Eintrag trägt das Symbol von Kate, und bei seiner Aktivierung wird nicht einfach "/usr/bin/kate" gestartet, sondern der im "Exec"-Feld von "kate.desktop" angegebene Befehl.
Re: Wie kann man alle zu einem Inode gehörenden Pfade finden
Verfasst: Sonntag 7. August 2011, 22:02
von snafu
@lunar: Danke für den Hinweis. Da habe ich mich offenbar nicht nur hinsichtlich der Verknüpfungen auf dem Holzweg befunden. Ich werde mir das alles nochmal durch den Kopf gehen lassen und dann vermutlich so, wie von dir dargelegt, die Funktionalität implementieren. Danke nochmal.
