Posso inviare del testo allo STDIN di un processo attivo in esecuzione in una sessione dello schermo?


69

Ho un processo server di lunga durata all'interno di una sessione dello schermo sul mio server Linux. È un po 'instabile (e purtroppo non è il mio software, quindi non posso risolverlo!), Quindi voglio scrivere un riavvio notturno del processo per aiutare la stabilità. L'unico modo per farlo chiudere con grazia è andare al processo dello schermo, passare alla finestra in cui è in esecuzione ed inserire la stringa "stop" sulla sua console di controllo.

Ci sono contorsioni di reindirizzamento intelligente che posso fare per fare in modo che un cronjob invii quel comando di arresto a un'ora fissa ogni giorno?

Risposte:


85

Questa risposta non risolve il problema, ma viene lasciata qui perché più di 30 persone l'hanno trovato utile , altrimenti lo avrei eliminato molto tempo fa.

Scrivi a /proc/*pid of the program*/fd/0. La fdsottodirectory contiene i descrittori di tutti i file aperti e il descrittore di file 0è l'input standard (1 è stdout e 2 è stderr).

Puoi usarlo per inviare messaggi sul tty su cui è in esecuzione un programma, sebbene non ti permetta di scrivere sul programma stesso.

Esempio

Terminale 1:

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

Terminale 2:

[ciupicri@hermes ~]$ pidof cat
7417
[ciupicri@hermes ~]$ echo "shows on the tty but bypasses cat" > /proc/7417/fd/0

3
@James Lawrie: dai un'occhiata a proc (5) e proc.txt .
Cristian Ciupitu,

2
+2 non importa quanto pensi di sapere, c'è sempre molto da imparare :) slick.
troyengel,

3
Attenzione però che il proc reindirizza solo a ciò che viene utilizzato come fonte di stdin. Nel tuo esempio, se inserisci qualcosa nel terminale 1, lo stamperà di nuovo (viene inviato ai gatti stdin e il gatto lo stampa), risultando così nel vederlo due volte. D'altra parte, se invii qualcosa a fd / 0, verrà inviato alla console ma non a cat, e quindi visualizzato solo una volta. Poiché cat semplicemente stampa di nuovo l'input con questo esempio, non puoi davvero vedere se l'input o l'output sono stati stampati, quindi questo malinteso. / fd / 0 punta alla console / pts; vedi ls -l /proc/7417/fd/0.
Kissaki,

5
esempio del mondo reale: ho avviato gphoto2 --get-all-files e mi chiede una conferma 100 volte. Quando echo "y"> / proc / PID / fd / 0, gphoto2 non procede, tuttavia, "y" viene stampato nel terminale.
Thorsten Staerk,

2
@ThorstenStaerk, lo so, è per questo che ho aggiunto quella nota. Stai scrivendo solo nel file del dispositivo corrispondente al terminale su cui gphoto2 viene eseguito (ad es. /dev/pts/19), Il ypersonaggio non raggiunge l'applicazione stessa. È simile a ciò che accade quando si utilizza il comando write (1) . Ad ogni modo, prova la mia altra risposta o uno strumento di automazione grafica come xdotool .
Cristian Ciupitu,

36

Soluzione basata sullo schermo

Avviare il server in questo modo:

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

lo schermo si avvierà in modalità staccata, quindi se vuoi vedere cosa sta succedendo, esegui:

# screen -r ServerFault

Controlla il server in questo modo:

# 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

(questa risposta si basa sull'invio di input di testo a uno schermo separato dal sito dei fratelli Unix e Linux )

Spiegazione dei parametri:

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

stuff [stringa]

   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.

soluzione basata su tmux

Avviare il server in questo modo:

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

tmux si avvierà in modalità staccata, quindi se vuoi vedere cosa sta succedendo, esegui:

# tmux attach-session -t ServerFault

Controlla il server in questo modo:

# 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

Spiegazione dei parametri:

 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.

4

Prova questo per iniziare:

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

E questo per uccidere:

# cd /path/to/wd
# echo "stop" > cmd
# rm cmd

3
Questo è buono, ma potrebbe avere lo svantaggio di non poter inviare altri comandi mentre il programma è in esecuzione. Se il programma si interrompe quando colpisce EOF su stdin, allora sul primo echo "xxx" > cmdil programma si fermerà (perché il tubo verrà chiuso). Sebbene alcuni programmi siano abbastanza intelligenti da riaprire ( rewind(3)) il loro stdin quando incontrano EOF.
Cristian Ciupitu,

2

È possibile inviare testo di input a un processo in esecuzione senza eseguire l' screenutilità o qualsiasi altra utility di fantasia. E può essere fatto inviando questo testo di input al "file" di input standard del processo /proc/PID#/fd/0.

Tuttavia, il testo di input deve essere inviato in un modo speciale per essere letto dal processo. L'invio del testo di input tramite il writemetodo file normale non comporta la ricezione del testo da parte del processo. Questo perché in questo modo verrà aggiunto solo a quel "file", ma non verrà attivato il processo di lettura dei byte.

Per attivare il processo di lettura dei byte, è necessario eseguire IOCTLun'operazione di tipo TIOCSTIper ogni singolo byte da inviare. Ciò inserirà il byte nella coda di input standard del processo.

Questo è discusso qui con alcuni esempi in C, Perl e Python:

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

-

Quindi, per rispondere alla domanda originale posta quasi 9 anni fa, il cron job avrebbe bisogno di eseguire qualche piccolo script / programma di utilità simile agli esempi scritti dagli utenti per quell'altra domanda, che invierebbe la stringa "stop \ n" a quel processo del server nella domanda, inviando ciascuno dei 5 byte tramite IOCTLun'operazione di tipo TIOCSTI.

Naturalmente questo funzionerà solo su sistemi che supportano il TIOCSTI IOCTLtipo di operazione (come Linux), e solo dall'account rootutente, poiché questi "file" in /proc/"sono" di proprietà di root.


1

Nel caso in cui aiuti qualcuno:
ho avuto un problema simile e poiché il processo che stavo usando non era in corso screeno tmux, ho dovuto adottare un approccio diverso.

Ho attaccato gdbal fatto xtermche il mio processo era in esecuzione e usato call write(5, "stop\n", 5)da gdbper scrivere al descrittore di file master pty.
Ho scoperto a quale descrittore di file inviare i dati cercando /proc/<pid>/fdun collegamento /dev/ptmxe quindi prova ed errore tra le due opzioni (l'invio della mia stringa a entrambi i descrittori di file corrispondenti sembrava non causare alcun danno).

MODIFICARE

Si è scoperto che il xtermprocesso a cui mi ero collegato era generato spawn-new-terminal() xtermdall'azione da un keybinding, e il secondo ptmxdescrittore di file aperto era semplicemente il processo ptmxgenitore xtermche non era stato chiuso.
Quindi le chiamate di prova ed errore avevano inviato l'output a quell'altro terminale.
La maggior parte dei xtermprocessi non ha due ptmxdescrittori di file.

MODIFICA FINE

Ciò ha effettivamente digitato quella stringa nel terminale e quindi l'ha inviata al processo in esecuzione sotto di essa.

nb potrebbe essere necessario consentire il collegamento a un processo in esecuzione con qualcosa di simile
sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"


0

Dal momento che non posso commentare la risposta più accettata di Cristian Ciupitu (del 2010), devo inserire questa in una risposta separata:

Questa domanda è già stata risolta in questa discussione: https://stackoverflow.com/questions/5374255/how-to-write-data-to-existing-processs-stdin-from-external-process

In breve:

Devi iniziare il tuo processo con una pipe per stdin che non si blocchi né si chiuda quando è stato scritto l'input corrente. Questo può essere implementato da un semplice loop infinito che verrà reindirizzato al processo in questione:

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

Posso confermare che questo è diverso dal modo di Krissi di aprire una pipa che non funzionava nel mio caso. La soluzione mostrata ha funzionato invece.

È quindi possibile scrivere nel file ... / fd / 0 del processo per inviare istruzioni. L'unico inconveniente è che è necessario terminare anche il processo bash che sta eseguendo il ciclo infinito dopo l'arresto del server.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.