Ermitteln, wann ein untergeordneter Prozess auf eine Eingabe wartet

Ich schreibe ein Python-Programm zum Ausführen von benutzerdefiniertem (und damit im schlimmsten Fall unsicherem, fehlerhaftem und abstürzendem) Code auf einem Linux-Server. Abgesehen von den Sicherheitsfragen ist es mein Ziel, festzustellen, ob der Code (der in einer beliebigen Sprache verfasst oder interpretiert sein kann) die richtigen Informationen enthältstdout, stderr und andere Dateien auf bestimmten Eingang in das Programm eingespeiststdin. Danach muss ich dem Benutzer die Ergebnisse anzeigen.

Die aktuelle Lösung

Derzeit besteht meine Lösung darin, den untergeordneten Prozess mithilfe von zu erzeugensubprocess.Popen(...) mit Dateihandles für diestdout, stderr undstdin. Die Datei hinter demstdin Handle enthält die Eingaben, die das Programm während des Betriebs liest, und nach dem Beenden des Programms das Handlestdout undstderr Dateien werden gelesen und auf ihre Richtigkeit überprüft.

Das Problem

Dieser Ansatz funktioniert ansonsten einwandfrei, aber wenn ich die Ergebnisse anzeige, kann ich die angegebenen Ein- und Ausgaben nicht kombinieren, sodass die Eingaben an den gleichen Stellen angezeigt werden wie beim Ausführen des Programms über ein Terminal. Das heißt für ein Programm wie

print "Hello."
name = raw_input("Type your name: ")
print "Nice to meet you, %s!" % (name)

den Inhalt der Datei, die das Programm enthältstdout wäre nach dem Laufen:

Hello.
Type your name: 
Nice to meet you, Anonymous!

vorausgesetzt, dass der Inhalt der Datei mit demstdin wurdenAnonymous<LF>. Kurz gesagt, für den angegebenen Beispielcode (und entsprechend fürirgendein anderen Code) Ich möchte ein Ergebnis erzielen wie:

Hello.
Type your name: Anonymous
Nice to meet you, Anonymous!

Daher besteht das Problem darin, zu erkennen, wann das Programm auf eine Eingabe wartet.

Versuchte Methoden

Ich habe die folgenden Methoden zur Lösung des Problems ausprobiert:

Popen.communicate (...)

Auf diese Weise kann der übergeordnete Prozess Daten separat entlang einer sendenRohr, kann aber nur einmal aufgerufen werden und ist daher nicht für Programme mit mehreren Ausgängen und Eingängen geeignet - so wie es aus der Dokumentation hervorgeht.

Lesen Sie direkt ausPopen.stdout undPopen.stderr und schreiben anPopen.stdin

Die Dokumentation warnt davor und diePopen.stdouts .read() und.readline() Aufrufe scheinen unendlich zu blockieren, wenn die Programme auf Eingaben warten.

Verwendenselect.select(...) um festzustellen, ob die Dateizugriffsnummern für die E / A bereit sind

Dies scheint nichts zu verbessern. Anscheinend sind die Pipes immer zum Lesen oder Schreiben bereit, alsoselect.select(...) hilft hier nicht viel.

Verwenden Sie einen anderen Thread, um das Lesen nicht zu blockieren

Wie in vorgeschlagendiese AntwortIch habe versucht, eine separate zu erstellenFaden() das speichert Ergebnisse aus dem Lesen von derstdout in einWarteschlange(). Die Ausgabezeilen vor einer Zeile, die Benutzereingaben erfordert, werden gut angezeigt, aber die Zeile, in der das Programm auf Benutzereingaben wartet ("Type your name: " im obigen Beispiel) wird nie gelesen.

Verwendung einerPTY Slave als der untergeordnete Prozess 'Datei behandelt

Wie angewiesenHier, Ich habe es versuchtpty.openpty() Erstellen eines Pseudoterminals mit Master- und Slave-Dateideskriptoren. Danach habe ich den Sklavendateideskriptor als Argument für das angegebensubprocess.Popen(...) Anrufestdout, stderr undstdin Parameter. Lesen Sie den mit geöffneten Master File Descriptor durchos.fdopen(...) ergibt das gleiche Ergebnis wie bei Verwendung eines anderen Threads: Die Zeile, die eine Eingabe erfordert, wird nicht gelesen.

Bearbeiten: Am Beispiel von @Antti Haapala vonpty.fork() für die Erstellung von untergeordneten Prozessen anstelle vonsubprocess.Popen(...) scheint mir auch zu erlauben die ausgabe von zu lesenraw_input(...).

Verwendenerwarten

Ich habe das auch probiertread(), read_nonblocking() undreadline() Methoden (dokumentiertHier) eines mit pexpect hervorgebrachten Prozesses, aber das beste Ergebnis, das ich mit bekamread_nonblocking(), ist das Gleiche wie zuvor: Die Zeile mit den Ausgaben, bevor der Benutzer etwas eingeben soll, wird nicht gelesen. ist das gleiche wie bei einem mit erstellten PTYpty.fork(): die Linie fordernde Eingabetut zu lesen bekommen.

Bearbeiten: Durch die Nutzungsys.stdout.write(...) undsys.stdout.flush() anstattprintin meinemMeister Das Programm, das das Kind erzeugt, schien zu beheben, dass die Eingabeaufforderungszeile nicht angezeigt wurde - sie wurde jedoch in beiden Fällen tatsächlich gelesen.

Andere

Ich habe es auch versuchtselect.poll(...), aber die Pipe- oder PTY-Masterdateideskriptoren scheinen immer zum Schreiben bereit zu sein.

AnmerkungenAndere LösungenWas mir auch in den Sinn gekommen ist, ist zu versuchen, die Eingabe zu füttern, wenn einige Zeit vergangen ist, ohne dass eine neue Ausgabe generiert wurde. Dies ist jedoch riskant, da es keine Möglichkeit gibt, festzustellen, ob das Programm gerade eine umfangreiche Berechnung durchführt.Wie @Antti Haapala in seiner Antwort erwähnt hat, ist dieread() Der Systemaufruf-Wrapper von glibc könnte ersetzt werden, um die Eingaben an das Master-Programm zu übermitteln. Dies funktioniert jedoch nicht mit statisch verknüpften Programmen oder Assembly-Programmen. (Obwohl ich jetzt darüber nachdenke, könnten solche Aufrufe aus dem Quellcode abgefangen und durch die gepatchte Version von ersetzt werdenread() - könnte noch mühsam umzusetzen sein.)Ändern des Linux-Kernel-Codes zur Kommunikation desread() syscalls zum programm ist wohl wahnsinn ...PTYs

Ich denke, der PTY ist der richtige Weg, da er ein Terminal vortäuscht und interaktive Programme überall auf Terminals laufen. Die Frage ist, wie?

Antworten auf die Frage(2)

Ihre Antwort auf die Frage