Eigentlich sollte mein Programm nur /proc/self/exe lesen. Und dann finde ich das[1]:
1
a) /proc might not be mounted at all, or it might be mounted somewhere
2
other than /proc.
3
b) / might not be / (e.g. chroot, containers, etc.), and so /proc might
4
be inaccessible.
5
c) There are a variety of situations where the called process's binary
6
is not directly accessible to the called process as a regular file,
7
and /proc/self/exe appears to be a broken symlink.
a) egal, das sollte sich leicht beheben lassen.
b) funktioniert etwa "mount -t proc proc /proc" in so einem Container nicht? Was funktioniert ohne /proc überhaupt noch?
c) wenn das Programm auf einer anderen Partition liegt, ist die busy, umount wird verweigert. Das einzige, was mir einfällt: das Programm liegt auf einem NFS-Server und der hat sich verabschiedet. Das mag bei kleinen Programmen und viel RAM zunächst nicht auffallen, nur der Link ist kaputt. Aber was könnte noch alles hinter "variety" stecken?
b) funktioniert etwa "mount -t proc proc /proc" in so einem Container
nicht? Was funktioniert ohne /proc überhaupt noch?
Kommt drauf an, es gibt tatsächlich fälle, wo das innerhalb eines mount user namespaces nicht mehr erlaubt ist. Aber alle container manager, die ich kenne, mounten es sowieso von Anfang an.
c) wenn das Programm auf einer anderen Partition liegt, ist die busy,
umount wird verweigert. Das einzige, was mir einfällt: das Programm
liegt auf einem NFS-Server und der hat sich verabschiedet. Das mag bei
kleinen Programmen und viel RAM zunächst nicht auffallen, nur der Link
ist kaputt. Aber was könnte noch alles hinter "variety" stecken?
Und auch wenn ich es lösche kann ich noch darauf zugreifen:
x.sh
Aha, readlink liefert den Pfad mit angehängtem " (deleted)", was ja formal ein gültiger Dateiname ist. Also kann man den nicht mehr 1:1 verwenden. Das gleiche passiert nach einem mv auf eine andere Partition. Ein mv auf der gleichen Partition aktualisiert auch den Link.
Mit NFS dazwischen gibt es erst dann ein (deleted), wenn der Cache aktualisiert wurde. Solange liefert stat -L Daten aus dem Cache, anschließend dann "Stale file handle".
Alles ziemlich normal, nicht perfekt, nicht tragisch. Unmittelbar nach dem execve() muss der Link einfach gültig sein. Anschließend gibt es viele Möglichkeiten, alles kaputt zu spielen. Zuverlässiger als argv[0] ist /proc/self/exe auf jeden Fall.
Aha, readlink liefert den Pfad mit angehängtem " (deleted)", was ja
formal ein gültiger Dateiname ist. Also kann man den nicht mehr 1:1
verwenden.
Das interessante ist aber, wie man oben sieht, folgt es dem Link nicht, wenn man /proc/self/exe öffnet, sondern öffnet direkt die gelöschte Datei als wäre nichts gewesen.
Zuverlässiger als argv[0] ist /proc/self/exe auf jeden Fall.
Naja, popen("/proc/self/exe foo bar", "r") geht schon mal schief - "self" ist "/bin/sh". Entspr. auch system(). NFS-Server könnten ein Problem sein (Datei verschwindet) und in wieweit Sachen über z.B. fuse funktionieren, weiß ich nicht. Ebenso wenn Privilegien gedropt oder uid geändert werden.
Das Problem, seinen eigenen Pfad/Namen zu finden, war schon von (Unix) Anfang an ... problematisch[1]. Linux kam dann mit dem /proc/self/exe Symlink und das schien die Lösung zu sein - das " (deleted)" hat's dann doch wieder kaputt gemacht (tatsächlicher Name oder nur Metainfo?). Falls mehrere Pfade zum exe existieren (ggf mit unterschiedlichen Zugriffsrechten), weiß man auch nicht, welcher angezeigt wird. Also benutzen die meisten weiterhin das ebenfalls unperfekte aber portable argv[0].
PS: Soviel ich weiß, war das vom Verhalten her immer ein Hardlink - die Präsentation als Softlink ist nur Informationshalber, eine erneute Pfadauflösung findet nicht statt.
Irgendwie ist /proc/self/exe ne Schmuddelecke :-)
PPS: Im verlinkten Thread geht es um einen Security-Bug falls argc==0 ist. Ist halt einfach ein Bug - die meisten Programme vertragen es auch nicht, wenn stdin/out/err geschlossen sind ...
[1] Ähnlich problematisch ist es, den Pfad des aktuellen Verzeichnisses (CWD) herauszufinden (Rekursiv ".." hochwandern und nach einem Namen durchsuchen, der die gleiche Inode wie "." hat).
Zuverlässiger als argv[0] ist /proc/self/exe auf jeden Fall.
Naja, popen("/proc/self/exe foo bar", "r") geht schon mal schief -
"self" ist "/bin/sh". Entspr. auch system().
Warum gibt es eigentlich kein popen() ohne die shell dazwischen?
Das Problem, seinen eigenen Pfad/Namen zu finden, war schon von (Unix)
Anfang an ... problematisch[1]. Linux kam dann mit dem /proc/self/exe
Symlink und das schien die Lösung zu sein - das " (deleted)" hat's dann
doch wieder kaputt gemacht (tatsächlicher Name oder nur Metainfo?).
Kaputt macht es der User, der sein Programm löscht. Dann existiert die Datei und existiert gleichzeitig nicht. Nur wer weiß, was er tut, darf sie noch benutzen ;) Dafür ist (deleted) doch eine gute Beschreibung.
Falls mehrere Pfade zum exe existieren (ggf mit unterschiedlichen
Zugriffsrechten), weiß man auch nicht, welcher angezeigt wird.
Doch; der Pfad, der beim execve() benutzt wurde. Aber innen drin ist viel Magie im Spiel: ein rename(2) ändert auch den Link.
Also benutzen die meisten weiterhin das ebenfalls unperfekte
aber portable argv[0].
für Fehlermeldungen ist argv[0] ja auch richtig - und unbrauchbar, wenn man wissen will, wo das Programm installiert ist.
PS: Soviel ich weiß, war das vom Verhalten her immer ein Hardlink - die
Präsentation als Softlink ist nur Informationshalber, eine erneute
Pfadauflösung findet nicht statt.
Ursprünglich (sagt 'man proc') lieferte /proc/*/exe die inode, das war natürlich viel einfacher und übersichtlicher und dann siegte die Bequemlichkeit...
Irgendwie ist /proc/self/exe ne Schmuddelecke :-)
PPS: Im verlinkten Thread geht es um einen Security-Bug falls argc==0
ist. Ist halt einfach ein Bug
Da hatte mich nur diese Aussage schockiert: "There are a variety of situations where the called process's binary is not directly accessible to the called process as a regular file". Was ja zunächst völlig unmöglich erscheint. Alle Beispiele dafür funktionieren ja auch erst hinterher und/oder sind mehr oder weniger illegal.
Falls mehrere Pfade zum exe existieren (ggf mit unterschiedlichen
Zugriffsrechten), weiß man auch nicht, welcher angezeigt wird.
Ich hätte gedacht, es wird der angezeigt, über den das Programm gestartet wurde.
Also benutzen die meisten weiterhin das ebenfalls unperfekte aber
portable argv[0].
Naja, was bei einen popen(argv[0], …) rauskommt, ist dann eher Glückssache, weil der Aufrufer beim exec() da reinschreiben kann, was immer er will, und das ist mitnichten immer nur der Name des Executables inklusive vollständigem Pfad.
Das genannte Problem mit mehreren Pfaden zum Executable hat man da übrigens genauso.
PPS: Im verlinkten Thread geht es um einen Security-Bug falls argc==0
ist. Ist halt einfach ein Bug - die meisten Programme vertragen es auch
nicht, wenn stdin/out/err geschlossen sind ...
Warum gibt es eigentlich kein popen() ohne die shell dazwischen?
Das ist halt eine vereinfachte Komfort-Funktion auf Ebene der Standardlib (in dem Sinne, dass es FILE* für die Kommunikation nutzt). Du kannst natürlich immer fork/exec*/pipe/dup2 verwenden, um sowas auch ohne Shell zu implementieren. Genauso gibt es auch kein system() ohne Shell. Wenn man das braucht, nimmt man eben fork und execl.
Das Problem, seinen eigenen Pfad/Namen zu finden, war schon von (Unix)
Anfang an ... problematisch[1]. Linux kam dann mit dem /proc/self/exe
Symlink und das schien die Lösung zu sein - das " (deleted)" hat's dann
doch wieder kaputt gemacht (tatsächlicher Name oder nur Metainfo?).
Kaputt macht es der User, der sein Programm löscht. Dann existiert die
Datei und existiert gleichzeitig nicht.
In Unix löscht man als User/Programm eigentlich keine Dateien. Man löst nur Verzeichniseinträge (unlink). Und wenn kein Eintrag mehr existiert und die Datei auch von keinem Programm mehr geöffnet ist, d.h. der Referenz-Zähler wird 0, dann wird auch der Inhalt gelöscht. Deshalb kann man unter Linux Dateien "löschen", auch wenn ein Programm sie noch offen hat, was unter Windows so nicht geht, weil ihm dieser Mechanismus fehlt.