Dateideskriptoren über exec

7

Standardmäßig bleiben Dateideskriptoren für alle Exec-Funktionen geöffnet. Der Nutzen ist vielleicht für die Deskriptoren 0-2 verständlich. Aber gibt es einen praktischen Anwendungsfall, um andere Deskriptoren offen zu halten? Gibt es echte Anwendungen, die auf dieser Tatsache beruhen?

rv
quelle
Ganz im Gegenteil, OpenSSH insbesondere nimmt Schmerzen zu schließen Deskriptoren> = 3 standardmäßig (über den closefrom(2)Anruf).
Thrig
1
@thrig Das bedeutet nicht, dass es sinnlos ist, einen offenen Dateideskriptor für einen anderen Zweck zu übergeben. Tatsächlich müssen OpenSSH große Anstrengungen unternehmen, um sicherzustellen, dass alle anderen Dateideskriptoren geschlossen sind. Dies sollte zeigen, wie nützlich es sein kann, offene Dateideskriptoren auf diese Weise zu übergeben.
Andrew Henle
Sogar einige Shell-Skripte richten Deskriptoren> 2 ein und übergeben sie für nützliche Effekte an Befehlspipes.
Kaz
Sehr oft ist eine Pipe beteiligt und der untergeordnete Prozess liest oder schreibt diese Pipe und der Dateideskriptor einer Pipe ist nicht 0 ... 2
user3629249

Antworten:

11

Es gibt ein Flag, das Sie für einen Dateideskriptor setzen können (bei open(): O_CLOEXEC oder höher mit fcntl(): FD_CLOEXEC), wenn Sie nicht möchten, dass fd an ausgeführte Befehle übergeben wird.

Dies sollten Sie für Ihre internen Dateideskriptoren tun, wenn Sie Befehle ausführen möchten.

In Muscheln ist es das, was ksh93Sie exec 3< some-filezum Beispiel tun . Für andere Shells oder FDS, mit denen geöffnet wurde { cmd1; cmd2; } 3< file, müssen Sie von Hand schließen, wenn Sie nicht möchten cmd1oder cmd2auf dieses Fd zugreifen möchten : {cmd1 3<&-; cmd2; } 3< file. Das ist eine gute Übung, wird aber nicht immer befolgt, da es normalerweise nicht kritisch ist, wenn Sie es nicht tun .

Nun, ob die Funktion nützlich ist. Ja, mehrere Befehle hängen davon ab.

Einige Befehle verwenden einen Dateideskriptor als Argument, das von einem Aufrufer geöffnet werden soll. Einige Beispiele, die mir in den Sinn kommen:

  • xtermmit seiner -SOption
  • qemu für verschiedene Dinge
  • flock (um eine Datei auf dem fd des Anrufers zu sperren)
  • der testBefehl aka [für seine -tOption (OK, das testDienstprogramm ist heutzutage in den meisten Bourne-ähnlichen Shells enthalten, aber es gibt immer noch einen testBefehl, der ausgeführt werden kann).
  • dialog benötigt Dateideskriptoren für die Eingabe vom Benutzer, die Ausgabe und den Fehler für den Benutzer sowie die Eingabe und Ausgabe für den Aufrufer, sodass Sie dafür zusätzliche fds verwenden können.
  • gpgoder opensslan die Sie einen Dateideskriptor angeben können, um die Passphrase oder andere Informationen zu kommunizieren.

Es gibt eine Reihe von Helfer - Dienstprogramme (zum Beispiel die Notwendigkeit, ausführen könnte einen Teil eines Befehls als ein anderer Benutzer oder eine Gruppe mit einem setuid / setgid ausführbare Datei ausgeführt werden ) , die sich darauf verlassen.

Prozesssubstitution beruht darauf:

In ,, diff <(cmd1) <(cmd2)werden 2 Dateideskriptoren (an Pipes) übergeben diffund diff greift auf sie zu, indem sie über das als Argument übergebene spezielle / dev / fd / n geöffnet werden.

Bei Shells ohne Prozessersetzung tun Sie dasselbe von Hand mit folgenden Dingen:

cm1 | { cmd2 | diff /dev/fd/3 -; } 3<&0
Stéphane Chazelas
quelle
Das ist schön zu wissen, dass es so viele Anwendungen gibt. Ehrlich gesagt hatte ich nur ein paar arkane Anwendungsfälle erwartet.
RV
@rv gibt es eigentlich viel mehr. Suchen Sie beispielsweise nach SystemD Socket Activation für Systemdienste.
Pinguin359
4

TinyMUSH und wahrscheinlich viele seiner Geschwister- und Kindercodebasen nutzen diese Funktionalität von exec mit großer Wirkung. Man kann einen Befehl ausgeben, um den Server neu zu starten und möglicherweise auf eine völlig neue Binärdatei zu aktualisieren, während die Benutzer verbunden bleiben.

Dazu schreiben Sie eine kleine Datenbank mit Informationen zu jedem verbundenen Benutzer, einschließlich seines Dateideskriptors. Die neu ausgeführte Kopie von TinyMUSH liest die Neustart-Datenbank, um das Wissen der verbundenen Benutzer wiederherzustellen und dort fortzufahren, wo sie aufgehört hat.

Endergebnis: Neue Funktionen werden mit nur einer kurzen Pause veröffentlicht, die für Benutzer sichtbar ist.

Nginx macht etwas Ähnliches, um binäre Upgrades durchzuführen, ohne Verbindungen zu verlieren.

Lexelby
quelle
1

Verbundene Sockets können auf diese Weise an einen untergeordneten Prozess übergeben werden. So kann beispielsweise ein Netzwerkserver, der eingehende Verbindungen akzeptiert, die Verarbeitung vollständig an einen anderen Prozess übergeben.

Ein allgegenwärtiges Beispiel finden Sie im Quellcode für inetd .

Andrew Henle
quelle
Die Frage bezieht sich auf ausgeführte Befehle, nicht auf untergeordnete Prozesse. Beachten Sie, dass für inetd der Socket auf fds 0,1,2 steht.
Stéphane Chazelas
@ StéphaneChazelas Die Frage bezieht sich auf ausgeführte Befehle, nicht auf untergeordnete Prozesse. Beachten Sie, dass für inetd der Socket auf fds 0,1,2 Nein ist. Die Fragen sind Aber gibt es einen praktischen Anwendungsfall, um andere Deskriptoren offen zu halten? und Gibt es echte Anwendungen, die auf dieser Tatsache beruhen? Meine Antwort ist ein Beispiel für einen "praktischen Anwendungsfall" mit einer "realen Anwendung" - das Übergeben eines bereits geöffneten Sockets an einen untergeordneten Prozess. Die Tatsache , dass inetdFiledeskriptoren verwendet 0, 1und 2für die Zwecke der offenen Buchse vorbei ist eine Konvention , dass Linien mit denen offen aus anderen Gründen zu halten.
Andrew Henle
Ja, inetd ist ein praktischer Fall, bei dem 0,1,2 überschritten wird. Die Quelle, auf die Sie verlinken, schließt alles andere als 0,1,2 sorgfältig, bevor Sie das Programm ausführen. siehe mainund run_service. Es ist kein Beispiel für die Übergabe eines anderen fds als 0,1,2, was die Frage war.
Dave_thompson_085
-1

Ich glaube nicht, dass es echte, generische Programme gibt, die etwas über Deskriptor 2 hinaus übergeben. Wenn dies der Fall ist, müsste das ausgeführte Programm fest codierte Dateideskriptornummern annehmen, z

write(3, ...);

oder

fp = fdopen(3, "r");

Das ist eine schlechte Codierungspraxis. Nur in Fällen, in denen Sie sowohl das Eltern- als auch das Kinderprogramm streng kontrollieren und niemand anderes eingreifen kann, kann dies sinnvoll sein.

Strg-d
quelle
1
Oder der Anrufer kann diese FD als Argument oder env var an den Angerufenen übergeben.
Stéphane Chazelas
1
Es gibt in der Tat viele "echte generische" Programme, die dies tun. Es gibt Bernstein-Tools, die erwarten, die Dateideskriptoren 3, 6 und 7 als Teil genau definierter Protokolle wie UCSPI und Bernstein TTY-Handling zu übergeben / zu empfangen. Die "Socket-Aktivierung" von systemd verfügt über ein Protokoll, das Dateideskriptoren 3 und höher umfasst. Und so weiter.
JdeBP