Kann ich Text an die STDIN eines aktiven Prozesses senden, der in einer Bildschirmsitzung ausgeführt wird?

69

Ich habe einen lang laufenden Serverprozess in einer Bildschirmsitzung auf meinem Linux-Server. Es ist ein bisschen instabil (und leider nicht meine Software, daher kann ich das nicht beheben!), Daher möchte ich einen nächtlichen Neustart des Prozesses ausführen, um die Stabilität zu verbessern. Die einzige Möglichkeit, das System ordnungsgemäß herunterzufahren, besteht darin, zum Bildschirmprozess zu wechseln, zu dem Fenster zu wechseln, in dem es ausgeführt wird, und die Zeichenfolge "stop" auf der Steuerkonsole einzugeben.

Gibt es irgendwelche intelligenten Umleitungsfehler, die ich tun kann, um einen Cronjob zu veranlassen, diesen Stoppbefehl jeden Tag zu einer festgelegten Zeit zu senden?


quelle

Antworten:

85

Diese Antwort löst das Problem nicht, bleibt aber hier, weil mehr als 30 Personen sie nützlich fanden , sonst hätte ich sie vor langer Zeit gelöscht.

Schreiben Sie an /proc/*pid of the program*/fd/0. Das fdUnterverzeichnis enthält die Deskriptoren aller geöffneten Dateien und der Dateideskriptor 0ist die Standardeingabe (1 ist stdout und 2 ist stderr).

Sie können dies verwenden, um Nachrichten auf dem Tty auszugeben, auf dem ein Programm ausgeführt wird, obwohl Sie nicht in das Programm selbst schreiben können.

Beispiel

Schalter 1:

[ciupicri@hermes ~]$ cat
shows on the tty but bypasses cat

Terminal 2:

[ciupicri@hermes ~]$ pidof cat
7417
[ciupicri@hermes ~]$ echo "shows on the tty but bypasses cat" > /proc/7417/fd/0
Cristian Ciupitu
quelle
3
@ James Lawrie: dann schauen Sie sich proc (5) und proc.txt an .
Cristian Ciupitu
2
+2 egal wie viel du glaubst zu wissen, es gibt immer mehr zu lernen :) Slick.
Troyengel
3
Beachten Sie jedoch, dass der proc fd nur zu dem umleitet, was als Quelle für stdin verwendet wird. Wenn Sie in Ihrem Beispiel etwas in Terminal 1 eingeben, wird es erneut ausgedruckt (es wird an Katzen gesendet und Katze druckt es aus), sodass Sie es zweimal sehen. Wenn Sie dagegen etwas an fd / 0 senden, wird es an die Konsole gesendet, aber nicht an cat, und daher nur einmal angezeigt. Da cat mit diesem Beispiel einfach die Eingabe wieder ausgibt, können Sie nicht wirklich sehen, ob Ihre Eingabe oder Ausgabe gedruckt wird, was zu diesem Missverständnis führt. / fd / 0 zeigt auf die Konsole / pts; sehen ls -l /proc/7417/fd/0.
Kissaki
4
Beispiel aus der Praxis: Ich habe gphoto2 --get-all-files gestartet und werde 100 Mal um eine Bestätigung gebeten. Wenn ich "y"> / proc / PID / fd / 0 wiedergebe, wird gphoto2 nicht fortgesetzt, jedoch wird "y" im Terminal gedruckt.
Thorsten Staerk
2
@ThorstenStaerk, ich weiß, deshalb habe ich diese Notiz hinzugefügt. Sie schreiben nur in die Gerätedatei, die dem Terminal entspricht, auf dem gphoto2 ausgeführt wird (z. B. /dev/pts/19), der yCharakter erreicht die Anwendung selbst nicht. Dies ist ähnlich wie bei Verwendung des Befehls write (1) . Probieren Sie auf jeden Fall meine andere Antwort oder ein grafisches Automatisierungswerkzeug wie xdotool aus .
Cristian Ciupitu
36

Bildschirmbasierte Lösung

Starten Sie den Server wie folgt:

# screen -d -m -S ServerFault tr a-z A-Z # replace with your server

Der Bildschirm wird im getrennten Modus gestartet. Wenn Sie also sehen möchten, was los ist, führen Sie Folgendes aus:

# screen -r ServerFault

Steuern Sie den Server wie folgt:

# screen -S ServerFault -p 0 -X stuff "stop^M"
# screen -S ServerFault -p 0 -X stuff "start^M"
# screen -S ServerFault -p 0 -X stuff "^D" # send EOF

(Diese Antwort basiert auf dem Senden von Texteingaben an einen getrennten Bildschirm von der Unix- und Linux- Geschwister-Site.)

Erklärung der Parameter:

-d -m
   Start screen in "detached" mode. This creates a new session but doesn't
   attach to it.  This is useful for system startup scripts.
-S sessionname
   When creating a new session, this option can be used to specify a meaningful
   name for the session.
-r [pid.tty.host]
-r sessionowner/[pid.tty.host]
   resumes a detached screen session.
-p number_or_name|-|=|+
   Preselect a window. This is useful when you want to reattach to a specific
   window or you want to send a command via the "-X" option to a specific
   window.
-X
   Send the specified command to a running screen session e.g. stuff.

Zeug [string]

   Stuff the string string in the input  buffer of the current window.
   This is like the "paste" command but with much less overhead.  Without
   a parameter, screen will prompt for a string to stuff.

TMUX-basierte Lösung

Starten Sie den Server wie folgt:

# tmux new-session -d -s ServerFault 'tr a-z A-Z' # replace with your server

tmux wird im getrennten Modus gestartet. Wenn Sie also sehen möchten, was los ist, führen Sie Folgendes aus:

# tmux attach-session -t ServerFault

Steuern Sie den Server wie folgt:

# tmux send-keys -t ServerFault -l stop
# tmux send-keys -t ServerFault Enter
# tmux send-keys -t ServerFault -l start
# tmux send-keys -t ServerFault Enter
# tmux send-keys -t ServerFault C-d # send EOF

Erklärung der Parameter:

 new-session [-AdDP] [-c start-directory] [-F format] [-n window-name] [-s
         session-name] [-t target-session] [-x width] [-y height]
         [shell-command]
         Create a new session with name session-name.

         The new session is attached to the current terminal unless -d is
         given.  window-name and shell-command are the name of and shell
         command to execute in the initial window.  If -d is used, -x and
         -y specify the size of the initial window (80 by 24 if not
         given).

 send-keys [-lR] [-t target-pane] key ...
               (alias: send)
         Send a key or keys to a window.  Each argument key is the name of
         the key (such as `C-a' or `npage' ) to send; if the string is not
         recognised as a key, it is sent as a series of characters.  The
         -l flag disables key name lookup and sends the keys literally.
Cristian Ciupitu
quelle
4

Versuchen Sie dies, um zu beginnen:

# screen
# cd /path/to/wd
# mkfifo cmd
# my_cmd <cmd
C-A d

Und das zu töten:

# cd /path/to/wd
# echo "stop" > cmd
# rm cmd
krissi
quelle
3
Dies ist gut, kann aber den Nachteil haben, dass andere Befehle nicht gesendet werden können, während das Programm ausgeführt wird. Wenn das Programm stoppt, wenn es auf stdin auf EOF trifft, stoppt das Programm beim ersten Mal echo "xxx" > cmd(weil die Pipe geschlossen wird). Obwohl einige Programme klug genug sind, um rewind(3)ihre Standardeinstellungen wieder zu öffnen, wenn sie auf EOF stoßen.
Cristian Ciupitu,
2

Es ist möglich, Eingabetext an einen laufenden Prozess zu senden, ohne das screenDienstprogramm oder ein anderes ausgefallenes Dienstprogramm auszuführen. Sie können diesen Eingabetext auch an die Standardeingabedatei des Prozesses senden /proc/PID#/fd/0.

Der eingegebene Text muss jedoch auf eine spezielle Weise gesendet werden, damit er vom Prozess gelesen werden kann. Wenn Sie den eingegebenen Text über die reguläre Dateimethode senden write, erhält der Prozess den Text nicht. Dies liegt daran, dass dies nur an diese "Datei" angehängt wird, aber nicht den Prozess zum Lesen der Bytes auslöst.

Um den Prozess zum Lesen der Bytes auszulösen, muss für jedes zu sendende Byte eine IOCTLOperation vom Typ ausgeführt TIOCSTIwerden. Dadurch wird das Byte in die Standardeingabewarteschlange des Prozesses gestellt.

Dies wird hier anhand einiger Beispiele in C, Perl und Python erläutert:

https://unix.stackexchange.com/questions/48103/construct-a-command-by-putting-a-string-into-a-tty/48221

-

Um die ursprüngliche Frage zu beantworten, die vor fast 9 Jahren gestellt wurde, müsste der Cron-Job ein kleines Hilfsprogramm ausführen, das den Beispielen für diese andere Frage ähnelt und die Zeichenfolge "stop \ n" an diesen Serverprozess sendet in der Frage, indem Sie jedes der 5 Bytes über eine IOCTLOperation vom Typ senden TIOCSTI.

Dies funktioniert natürlich nur auf Systemen, die den TIOCSTI IOCTLOperationstyp unterstützen (wie Linux), und nur über das rootBenutzerkonto, da diese "Dateien" unter /proc/"Eigentümer" von " sind" root.

maratbn
quelle
1

Falls es jemandem hilft:
Ich hatte ein ähnliches Problem und da der Prozess, den ich verwendete, nicht unter screenoder lag tmux, musste ich einen anderen Ansatz wählen.

Ich habe an gdbden angehängt, in dem xtermmein Prozess ausgeführt wurde, und habe call write(5, "stop\n", 5)from verwendet gdb, um in den Master-Pty-Dateideskriptor zu schreiben.
Ich fand heraus, an welchen Dateideskriptor die Daten gesendet werden sollten, indem ich nach /proc/<pid>/fdeinem Link zu /dev/ptmxund dann nach einem Versuch und Irrtum zwischen den beiden Optionen suchte (das Senden meiner Zeichenfolge an beide übereinstimmenden Dateideskriptoren schien keinen Schaden zu verursachen).

BEARBEITEN

Es stellte sich heraus, dass der xtermProzess , an den ich angehängt hatte, mit der spawn-new-terminal() xtermAktion einer Tastenkombination erzeugt wurde und der zweite ptmxgeöffnete Dateideskriptor einfach der ptmxdes übergeordneten xtermProzesses war, der nicht geschlossen worden war.
Daher hatten die Versuchs- und Fehleraufrufe eine Ausgabe an das andere Terminal gesendet.
Die meisten xtermProzesse haben keine zwei ptmxDateideskriptoren.

ENDE BEARBEITEN

Dadurch wurde die Zeichenfolge effektiv in das Terminal eingegeben und an den Prozess weitergeleitet, der unter dem Terminal ausgeführt wird.

Hinweis: Möglicherweise müssen Sie das Anhängen an einen laufenden Prozess mit so etwas wie zulassen
sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"

Apfel
quelle
0

Da ich die am meisten akzeptierte Antwort von Cristian Ciupitu (von 2010) nicht kommentieren kann, muss ich dies in einer separaten Antwort ausdrücken:

Diese Frage wurde bereits in diesem Thread gelöst: https://stackoverflow.com/questions/5374255/how-to-write-data-to-existing-processs-stdin-from-external-process

Zusamenfassend:

Sie müssen Ihren Prozess mit einer Pipe für stdin starten, die beim Durchschreiben der aktuellen Eingabe weder blockiert noch schließt. Dies kann durch eine einfache Endlosschleife realisiert werden, die an den jeweiligen Prozess weitergeleitet wird:

$ (while [ 1 ]; do sleep 1; done) | yourProgramToStart

Ich kann bestätigen, dass dies anders ist als Krissis Art, eine Pfeife zu öffnen, die in meinem Fall nicht funktioniert hat. Die gezeigte Lösung hat stattdessen funktioniert.

Anschließend können Sie in die Datei ... / fd / 0 des Prozesses schreiben, um Anweisungen an sie zu senden. Der einzige Nachteil ist, dass Sie auch den Bash-Prozess beenden müssen, der die Endlosschleife ausführt, nachdem der Server heruntergefahren wurde.

user3471872
quelle