it-swarm.com.de

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

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 per Skript durchfü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" in die Steuerkonsole einzugeben.

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

74
Richard Gaywood

Diese Antwort löst das Problem nicht, aber sie bleibt hier, weil mehr als 30 Leute 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 Unterverzeichnis fd enthält die Deskriptoren aller geöffneten Dateien und den Dateideskriptor 0 ist 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

Terminal 1:

[[email protected] ~]$ cat
shows on the tty but bypasses cat

Terminal 2:

[[email protected] ~]$ pidof cat
7417
[[email protected] ~]$ echo "shows on the tty but bypasses cat" > /proc/7417/fd/0
88

Bildschirmbasierte Lösung

Starten Sie den Server wie folgt:

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

screen startet im getrennten Modus. 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 Senden von Texteingabe an einen getrennten Bildschirm von der Unix & 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 [Zeichenfolge]

   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.
40

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
4
krissi

Es ist möglich, Eingabetext an einen laufenden Prozess zu senden, ohne das Dienstprogramm screen oder ein anderes ausgefallenes Dienstprogramm auszuführen. Dies kann durch Senden dieses Eingabetextes an die Standardeingabe "Datei" des Prozesses erfolgen. /proc/PID#/fd/0.

Der Eingabetext muss jedoch auf spezielle Weise gesendet werden, damit er vom Prozess gelesen werden kann. Das Senden des Eingabetextes über die reguläre Datei write führt nicht dazu, dass der Prozess den Text empfängt. 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 Operation IOCTL vom Typ TIOCSTI ausgeführt werden. 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 Hilfsskript/-programm ausführen, das den Beispielen ähnelt, die für diese andere Frage geschrieben wurden, und die die Zeichenfolge "stop\n" an diesen Serverprozess senden würde in der Frage, indem jedes der 5 Bytes über eine IOCTL Operation vom Typ TIOCSTI gesendet wird.

Dies funktioniert natürlich nur auf Systemen, die den Operationstyp TIOCSTIIOCTL unterstützen (wie Linux), und nur über das Benutzerkonto root, da diese "Dateien" unter /proc/ sind "im Besitz" von root.

3
maratbn

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

Ich habe gdb an das xterm angehängt, in dem mein Prozess ausgeführt wurde, und call write(5, "stop\n", 5) von gdb verwendet, um in den Master-Pty-Dateideskriptor zu schreiben.
Ich habe herausgefunden, an welchen Dateideskriptor die Daten gesendet werden sollen, indem ich in /proc/<pid>/fd Nach einem Link zu /dev/ptmx Gesucht und dann zwischen den beiden Optionen versucht habe (meine Zeichenfolge an beide senden) übereinstimmende Dateideskriptoren schienen keinen Schaden zu verursachen).

Es stellte sich heraus, dass der xterm -Prozess, an den ich angehängt hatte, mit der Aktion spawn-new-terminal()xterm aus einer Tastenkombination erzeugt und der zweite Dateideskriptor ptmx geöffnet wurde war einfach das ptmx des übergeordneten xterm Prozesses, der nicht geschlossen worden war.
Daher hatten die Versuchs- und Fehleraufrufe eine Ausgabe an dieses andere Terminal gesendet.
Die meisten xterm Prozesse haben keine zwei ptmx Dateideskriptoren.

Dadurch wurde diese Zeichenfolge effektiv in das Terminal eingegeben und an den darunter ausgeführten Prozess weitergeleitet.

n.b. 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"

1
Apple

Da ich die am meisten akzeptierte Antwort von Cristian Ciupitu (von 2010) nicht kommentieren kann, muss ich dies in eine separate Antwort einfügen:

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 beginnen, die beim Durchschreiben der aktuellen Eingabe weder blockiert noch geschlossen wird. Dies kann durch eine einfache Endlosschleife implementiert werden, die an den betreffenden Prozess weitergeleitet wird:

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

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

Sie können dann in die Datei .../fd/0 des Prozesses schreiben, um Anweisungen an diesen zu senden. Der einzige Nachteil ist, dass Sie auch den Bash-Prozess beenden müssen, der die Endlosschleife nach dem Herunterfahren des Servers ausführt.

1
user3471872