Puis-je envoyer du texte au STDIN d'un processus actif en cours d'exécution dans une session écran?

69

J'ai un processus serveur de longue durée dans une session d'écran sur mon serveur Linux. C'est un peu instable (et malheureusement, pas avec mon logiciel, je ne peux donc pas résoudre ce problème!), Je souhaite donc créer un script pour relancer le processus tous les soirs afin d'améliorer la stabilité. La seule façon de lui faire faire un arrêt en douceur consiste à accéder au processus d'écran, à passer à la fenêtre dans laquelle il s'exécute et à entrer la chaîne "stop" sur sa console de contrôle.

Existe-t-il des contorsions intelligentes de la redirection que je puisse faire pour qu'un cronjob envoie cette commande stop à une heure précise chaque jour?


la source

Réponses:

85

Cette réponse ne résout pas le problème, mais elle est laissée ici car plus de 30 personnes l'ont trouvé utile , sinon je l'aurais supprimée il y a longtemps.

Ecrire à /proc/*pid of the program*/fd/0. Le fdsous-répertoire contient les descripteurs de tous les fichiers ouverts et le descripteur de fichier correspond à 0l'entrée standard (1 correspond à stdout et 2 à stderr).

Vous pouvez utiliser ceci pour envoyer des messages sur le terminal où un programme est en cours d'exécution, bien que cela ne vous permette pas d'écrire dans le programme lui-même.

Exemple

Terminal 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
la source
3
@ James Lawrie: jetez un œil à proc (5) et proc.txt .
Cristian Ciupitu
2
+2 peu importe combien vous pensez savoir, il y a toujours plus à apprendre :) lisse.
Troyengel
3
Sachez cependant que le proc fd ne redirige que vers ce qui est utilisé comme source de stdin. Dans votre exemple, si vous entrez quelque chose dans le terminal 1, il l’imprimera à nouveau (il est envoyé aux chats stdin et le chat l’imprime), ce qui vous fait le voir deux fois. Par contre si vous envoyez quelque chose à fd / 0, il sera envoyé à la console mais pas au chat, et ne sera donc affiché qu’une fois. Comme chat affiche simplement à nouveau les entrées avec cet exemple, vous ne pouvez pas vraiment savoir si votre entrée ou votre sortie est en cours d'impression, d'où cette idée fausse. / fd / 0 pointe sur la console / pts; voir ls -l /proc/7417/fd/0.
Kissaki
5
Exemple concret: j’ai lancé gphoto2 --get-all-all-files et demande une confirmation 100 fois. Quand j'écho "y"> / proc / PID / fd / 0, gphoto2 ne continue pas, cependant, "y" est imprimé dans le terminal.
Thorsten Staerk
2
@ThorstenStaerk, je sais, c'est pourquoi j'ai ajouté cette note. Vous écrivez uniquement dans le fichier de périphérique correspondant au terminal sur lequel gphoto2 est exécuté (par exemple /dev/pts/19), le ycaractère n'atteignant pas l'application elle-même. C'est semblable à ce qui se passe lorsque vous utilisez la commande write (1) . Quoi qu'il en soit, essayez soit mon autre réponse, soit un outil d'automatisation graphique tel que xdotool .
Cristian Ciupitu
36

Solution basée sur l'écran

Démarrez le serveur comme ceci:

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

l'écran va démarrer en mode détaché, donc si vous voulez voir ce qui se passe, lancez:

# screen -r ServerFault

Contrôlez le serveur comme ceci:

# 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

(cette réponse est basée sur l' envoi de la saisie de texte à un écran détaché du Unix et Linux site frères et soeurs)

Explication des paramètres:

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

des choses [chaîne]

   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.

solution basée sur tmux

Démarrez le serveur comme ceci:

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

tmux démarrera en mode détaché, donc si vous voulez voir ce qui se passe, lancez:

# tmux attach-session -t ServerFault

Contrôlez le serveur comme ceci:

# 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

Explication des paramètres:

 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
la source
4

Essayez ceci pour commencer:

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

Et ceci pour tuer:

# cd /path/to/wd
# echo "stop" > cmd
# rm cmd
Krissi
la source
3
C'est bien, mais cela pourrait avoir l'inconvénient de ne pas pouvoir envoyer d'autres commandes pendant l'exécution du programme. Si le programme s’arrête quand il frappe EOF sur stdin, le programme s’arrêtera le premier echo "xxx" > cmd(car le canal sera fermé). Bien que certains programmes soient assez intelligents pour rouvrir ( rewind(3)) leur stdin quand ils rencontrent EOF.
Cristian Ciupitu
2

Il est possible d'envoyer du texte d'entrée à un processus en cours d'exécution sans exécuter l' screenutilitaire ni aucun autre utilitaire sophistiqué. Et cela peut être fait en envoyant ce texte d'entrée au "fichier" d'entrée standard du processus /proc/PID#/fd/0.

Cependant, le texte saisi doit être envoyé d'une manière spéciale pour être lu par le processus. L'envoi du texte saisi via la writeméthode de fichier standard ne fera pas en sorte que le processus reçoive le texte. En effet, cela ne fera qu'ajouter à ce "fichier", mais ne déclenchera pas le processus pour lire les octets.

Pour que le processus lise les octets, il est nécessaire d'effectuer une IOCTLopération de type TIOCSTIpour chaque octet à envoyer. Cela placera l'octet dans la file d'attente d'entrée standard du processus.

Ceci est discuté ici avec quelques exemples en C, Perl et Python:

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

-

Donc, pour répondre à la question initiale posée il y a presque 9 ans, le travail cron devrait exécuter un script / programme utilitaire similaire aux exemples donnés par les utilisateurs pour cette autre question, qui enverrait la chaîne "stop \ n" à ce processus serveur. dans la question, en envoyant chacun des 5 octets via une IOCTLopération de type TIOCSTI.

Bien entendu, cela ne fonctionnera que sur les systèmes prenant en charge le TIOCSTI IOCTLtype d'opération (comme Linux), et uniquement à partir du rootcompte d'utilisateur, car ces "fichiers" /proc/sont "détenus" par root.

Maratbn
la source
1

Au cas où cela aiderait quelqu'un:
j'ai eu un problème similaire, et comme le processus que j'utilisais ne faisait pas screenou pas tmux, je devais adopter une approche différente.

J'attaché gdbà xtermce que mon processus était en cours d' exécution dans et utilisé call write(5, "stop\n", 5)de gdbpour écrire dans le descripteur de fichier pty maître.
J'ai découvert le descripteur de fichier auquel envoyer les données en cherchant /proc/<pid>/fdun lien /dev/ptmx, puis des essais et des erreurs entre les deux options (l'envoi de ma chaîne aux deux descripteurs de fichier correspondants ne semblait causer aucun préjudice).

MODIFIER

Il s’est avéré que le xtermprocessus auquel j’avais attaché était engendré par l’ spawn-new-terminal() xtermaction d’un raccourci clavier, et le deuxième ptmxdescripteur de fichier ouvert était simplement le processus ptmxparent xtermqui n’avait pas été fermé.
Par conséquent, les appels d'essai et d'erreur avaient envoyé une sortie à cet autre terminal.
La plupart des xtermprocessus n'ont pas deux ptmxdescripteurs de fichier.

FIN ÉDITER

Cela a efficacement tapé cette chaîne dans le terminal et l'a donc envoyée au processus qui l'exécutait.

nb vous devrez peut-être autoriser l'attachement à un processus en cours avec quelque chose comme
sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"

Pomme
la source
0

Puisque je ne peux pas commenter la réponse la plus acceptée de Cristian Ciupitu (de 2010), je dois mettre cela dans une réponse séparée:

Cette question a déjà été résolue dans ce fil de discussion: https://stackoverflow.com/questions/5374255/how-to-write-data-to-existing-processs-stdin-from-external-process

En bref:

Vous devez démarrer votre processus avec un tuyau pour stdin qui ne bloque pas ni ne ferme pas lorsque l'entrée actuelle a été écrite. Ceci peut être implémenté par une simple boucle sans fin qui sera acheminée vers le processus en question:

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

Je peux confirmer que c'est différent de la manière dont Krissi a ouvert un tuyau qui ne fonctionnait pas dans mon cas. La solution montrée a bien fonctionné à la place.

Vous pouvez ensuite écrire dans le fichier ... / fd / 0 du processus pour lui envoyer des instructions. Le seul inconvénient est que vous devez également mettre fin au processus bash, qui exécute la boucle sans fin après la fermeture du serveur.

utilisateur3471872
la source