¿Puedo enviar algún texto al STDIN de un proceso activo que se ejecuta en una sesión de pantalla?

69

Tengo un proceso de servidor de larga duración dentro de una sesión de pantalla en mi servidor Linux. Es un poco inestable (y lamentablemente no es mi software, ¡así que no puedo solucionarlo!), Por lo que quiero realizar un reinicio nocturno del proceso para ayudar a la estabilidad. La única forma de hacer que se apague correctamente es ir al proceso de la pantalla, cambiar a la ventana en la que se está ejecutando e ingresar la cadena "stop" en su consola de control.

¿Hay alguna contorsión de redirección inteligente que pueda hacer para que un cronjob envíe ese comando de detención a una hora fija todos los días?


fuente

Respuestas:

85

Esta respuesta no resuelve el problema, pero se dejó aquí porque más de 30 personas lo encontraron útil , de lo contrario lo habría eliminado hace mucho tiempo.

Escribir a /proc/*pid of the program*/fd/0. El fdsubdirectorio contiene los descriptores de todos los archivos abiertos y el descriptor de archivo 0es la entrada estándar (1 es stdout y 2 es stderr).

Puede usar esto para enviar mensajes en el tty donde se ejecuta un programa, aunque no le permite escribir en el programa en sí.

Ejemplo

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
fuente
3
@ James Lawrie: luego eche un vistazo a proc (5) y proc.txt .
Cristian Ciupitu
2
+2 no importa cuánto creas que sabes, siempre hay más para aprender :) pulido.
troyengel
3
Sin embargo, tenga en cuenta que el proceso fd solo redirige a lo que se utiliza como fuente de stdin. En su ejemplo, si ingresa algo en la terminal 1, lo imprimirá nuevamente (se envía a los gatos stdin y cat lo imprime), lo que resulta en que lo vea dos veces. Por otro lado, si envía algo a fd / 0, se enviará a la consola pero no a cat y, por lo tanto, solo se mostrará una vez. Como cat simplemente imprime la entrada nuevamente con este ejemplo, realmente no puede ver si su entrada o salida se está imprimiendo, por lo tanto, esta idea errónea. / fd / 0 apunta a la consola / pts; ver ls -l /proc/7417/fd/0.
Kissaki
55
ejemplo del mundo real: he iniciado gphoto2 --get-all-files y me pide una confirmación 100 veces. Cuando hago eco de "y"> / proc / PID / fd / 0, gphoto2 no continúa, sin embargo, se imprime "y" en el terminal.
Thorsten Staerk
2
@ThorstenStaerk, lo sé, por eso agregué esa nota. Está escribiendo solo en el archivo del dispositivo correspondiente al terminal en el que se ejecuta gphoto2 (por ejemplo /dev/pts/19), el ypersonaje no llega a la aplicación en sí. Es similar a lo que sucede cuando usa el comando write (1) . De todos modos, prueba mi otra respuesta o una herramienta de automatización gráfica como xdotool .
Cristian Ciupitu
36

Solución basada en pantalla

Inicie el servidor de esta manera:

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

la pantalla se iniciará en modo separado, por lo que si desea ver lo que está sucediendo, ejecute:

# screen -r ServerFault

Controla el servidor de esta manera:

# 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

(esta respuesta se basa en el envío de entrada de texto a una pantalla separada desde el sitio hermano de Unix y Linux )

Explicación de los parámetros:

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

cosas [cadena]

   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.

solución basada en tmux

Inicie el servidor de esta manera:

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

tmux comenzará en modo separado, así que si quieres ver lo que está pasando, ejecuta:

# tmux attach-session -t ServerFault

Controla el servidor de esta manera:

# 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

Explicación de los parámetros:

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

Prueba esto para comenzar:

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

Y esto para matar:

# cd /path/to/wd
# echo "stop" > cmd
# rm cmd
krissi
fuente
3
Esto es bueno, pero puede tener la desventaja de no poder enviar otros comandos mientras el programa se está ejecutando. Si el programa se detiene cuando llega a EOF en stdin, entonces el primero echo "xxx" > cmdse detendrá (porque la tubería se cerrará). Aunque algunos programas son lo suficientemente inteligentes como para volver a abrir ( rewind(3)) su stdin cuando se encuentran con EOF.
Cristian Ciupitu
2

Es posible enviar texto de entrada a un proceso en ejecución sin ejecutar la screenutilidad o cualquier otra utilidad sofisticada. Y se puede hacer enviando este texto de entrada al "archivo" de entrada estándar del proceso /proc/PID#/fd/0.

Sin embargo, el texto de entrada debe enviarse de una manera especial para que el proceso lo lea. Enviar el texto de entrada a través del writemétodo de archivo normal no hará que el proceso reciba el texto. Esto se debe a que hacerlo solo se agregará a ese "archivo", pero no activará el proceso para leer los bytes.

Para activar el proceso para leer los bytes, es necesario realizar una IOCTLoperación de tipo TIOCSTIpor cada byte que se envíe. Esto colocará el byte en la cola de entrada estándar del proceso.

Esto se discute aquí con algunos ejemplos en C, Perl y Python:

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

-

Entonces, para responder la pregunta original que se hizo hace casi 9 años, el trabajo cron necesitaría ejecutar un pequeño script / programa de utilidad similar a los ejemplos que la gente escribió para esa otra pregunta, que enviaría la cadena "stop \ n" a ese proceso del servidor en la pregunta, enviando cada uno de los 5 bytes a través de una IOCTLoperación de tipo TIOCSTI.

Por supuesto, esto solo funcionará en sistemas que admitan el TIOCSTI IOCTLtipo de operación (como Linux), y solo desde la rootcuenta de usuario, ya que estos "archivos" /proc/son "propiedad" de ellos root.

maratbn
fuente
1

En caso de que ayude a alguien:
tuve un problema similar, y como el proceso que estaba usando no estaba bajo screeno tmux, tuve que adoptar un enfoque diferente.

Os adjunto gdba la xtermque mi proceso se ejecuta en, y se utiliza call write(5, "stop\n", 5)de gdbescribir en el descriptor de archivo pty maestro.
Descubrí a qué descriptor de archivo enviar los datos buscando /proc/<pid>/fdun enlace /dev/ptmxy luego prueba y error entre las dos opciones (enviar mi cadena a ambos descriptores de archivo coincidentes parecía no causar ningún daño).

EDITAR

Resultó que el xtermproceso al que me había adjuntado se generó con la spawn-new-terminal() xtermacción de una combinación de teclas, y el segundo ptmxdescriptor de archivo abierto era simplemente el ptmxdel xtermproceso principal que no se había cerrado.
Por lo tanto, las llamadas de prueba y error habían enviado salida a ese otro terminal.
La mayoría de los xtermprocesos no tienen dos ptmxdescriptores de archivo.

EDICIÓN FINAL

Esto efectivamente escribió esa cadena en la terminal y, por lo tanto, la envió al proceso que se ejecuta debajo de ella.

Nota: es posible que deba permitir adjuntar a un proceso en ejecución con algo como
sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"

manzana
fuente
0

Como no puedo comentar la respuesta más aceptada de Cristian Ciupitu (de 2010), tengo que poner esto en una respuesta separada:

Esta pregunta ya se resolvió en este hilo: https://stackoverflow.com/questions/5374255/how-to-write-data-to-existing-processs-stdin-from-external-process

En breve:

Debe comenzar su proceso con una tubería para stdin que no bloquea ni cierra cuando se escribió la entrada actual. Esto se puede implementar mediante un bucle sin fin simple que se canalizará al proceso en cuestión:

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

Puedo confirmar que esto es diferente de la forma en que Krissi abre una tubería que no funcionaba en mi caso. La solución mostrada funcionó en su lugar.

Luego puede escribir en el archivo ... / fd / 0 del proceso para enviarle instrucciones. El único inconveniente es que también debe terminar el proceso bash, que está ejecutando el bucle sin fin después de que el servidor se apagó.

usuario3471872
fuente