Posso enviar algum texto para o STDIN de um processo ativo em execução em uma sessão de tela?

69

Eu tenho um processo de servidor de longa execução dentro de uma sessão de tela no meu servidor Linux. É um pouco instável (e, infelizmente, não é o meu software, por isso não posso consertar isso!), Então quero criar um reinício noturno do processo para ajudar na estabilidade. A única maneira de fazer um desligamento normal é ir para o processo de tela, alternar para a janela em que está sendo executada e inserir a string "stop" em seu console de controle.

Existem contorções de redirecionamento inteligente que posso fazer para que um cronjob envie esse comando de parada em um horário fixo todos os dias?


fonte

Respostas:

85

Esta resposta não resolve o problema, mas foi deixada aqui porque mais de 30 pessoas acharam útil , caso contrário, eu a teria excluído há muito tempo.

Escreva para /proc/*pid of the program*/fd/0. O fdsubdiretório contém os descritores de todos os arquivos abertos e o descritor de arquivo 0é a entrada padrão (1 é stdout e 2 é stderr).

Você pode usar isso para gerar mensagens no tty em que um programa está sendo executado, embora isso não permita que você escreva no próprio programa.

Exemplo

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
fonte
3
@ James Lawrie: então dê uma olhada em proc (5) e proc.txt .
Cristian Ciupitu
2
+2, não importa o quanto você pense que sabe, há sempre mais para aprender :).
troyengel
3
Esteja ciente de que o proc fd redireciona apenas para o que é usado como fonte de stdin. No seu exemplo, se você digitar algo no terminal 1, ele será impresso novamente (é enviado para cats stdin e cat o imprime), resultando na visualização duas vezes. Por outro lado, se você enviar algo para fd / 0, ele será enviado ao console, mas não ao gato, e, portanto, será exibido apenas uma vez. Como o gato simplesmente imprime a entrada novamente com este exemplo, você não pode realmente ver se sua entrada ou saída está sendo impressa, portanto, esse equívoco. / fd / 0 aponta para o console / pontos; veja ls -l /proc/7417/fd/0.
Kissaki
5
exemplo do mundo real: iniciei o gphoto2 --get-all-files e ele solicita uma confirmação 100 vezes. Quando eco "y"> / proc / PID / fd / 0, o gphoto2 não prossegue, no entanto, "y" é impresso no terminal.
Thorsten Staerk
2
@ThorstenStaerk, eu sei, é por isso que adicionei essa nota. Você está gravando apenas no arquivo do dispositivo correspondente ao terminal no qual o gphoto2 é executado (por exemplo /dev/pts/19), o ypersonagem não chega ao aplicativo em si. É semelhante ao que acontece quando você usa o comando write (1) . Enfim, tente minha outra resposta ou uma ferramenta de automação gráfica como o xdotool .
Cristian Ciupitu
36

Solução baseada em tela

Inicie o servidor assim:

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

a tela iniciará no modo desanexado; portanto, se você quiser ver o que está acontecendo, execute:

# screen -r ServerFault

Controle o servidor assim:

# 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 resposta é baseada no envio de entrada de texto para uma tela desanexada do site irmão do Unix e Linux )

Explicação dos 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.

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

solução baseada em tmux

Inicie o servidor assim:

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

O tmux iniciará no modo desanexado, portanto, se você quiser ver o que está acontecendo, execute:

# tmux attach-session -t ServerFault

Controle o servidor assim:

# 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

Explicação dos 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
fonte
4

Tente isto para começar:

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

E isso para matar:

# cd /path/to/wd
# echo "stop" > cmd
# rm cmd
krissi
fonte
3
Isso é bom, mas pode ter a desvantagem de não poder enviar outros comandos enquanto o programa está sendo executado. Se o programa parar quando atingir EOF em stdin, no primeiro, echo "xxx" > cmdo programa será interrompido (porque o tubo será fechado). Embora alguns programas sejam inteligentes o suficiente para reabrir rewind(3)seu stdin quando encontrarem EOF.
Cristian Ciupitu
2

É possível enviar texto de entrada para um processo em execução sem executar o screenutilitário ou qualquer outro utilitário sofisticado. E isso pode ser feito enviando esse texto de entrada para o "arquivo" de entrada padrão do processo /proc/PID#/fd/0.

No entanto, o texto de entrada precisa ser enviado de uma maneira especial para ser lido pelo processo. Enviar o texto de entrada através do writemétodo de arquivo regular não fará com que o processo receba o texto. Isso ocorre porque isso será anexado apenas a esse "arquivo", mas não acionará o processo para ler os bytes.

Para acionar o processo de leitura dos bytes, é necessário executar uma IOCTLoperação do tipo TIOCSTIpara que cada byte seja enviado. Isso colocará o byte na fila de entrada padrão do processo.

Isso é discutido aqui com alguns exemplos em C, Perl e Python:

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

-

Portanto, para responder à pergunta original feita quase 9 anos atrás, o trabalho cron precisaria executar um pequeno script / programa utilitário semelhante aos exemplos que as pessoas escreveram para essa outra pergunta, que enviariam a string "stop \ n" para o processo do servidor em questão, através do envio de cada uma das 5 bytes através de uma IOCTLoperação de tipo TIOCSTI.

Obviamente, isso funcionará apenas em sistemas que suportam o TIOCSTI IOCTLtipo de operação (como Linux) e somente a partir da rootconta do usuário, já que esses "arquivos" /proc/são de "propriedade" deles root.

maratbn
fonte
1

Em caso de ajuda ninguém:
Eu tive um problema semelhante, e como o processo que eu estava usando não estava sob screenou tmux, eu tive que tomar uma abordagem diferente.

Anexei gdbao xtermque meu processo estava sendo executado e usei call write(5, "stop\n", 5)de gdbpara escrever no descritor de arquivo mestre pty.
Descobri para qual descritor de arquivo enviar os dados, procurando /proc/<pid>/fdum link /dev/ptmxe, em seguida, tentativa e erro entre as duas opções (enviar minha string para os dois descritores de arquivo correspondentes parecia não causar nenhum dano).

EDITAR

Aconteceu que o xtermprocesso ao qual eu havia me conectado foi gerado com a spawn-new-terminal() xtermação de uma combinação de teclas, e o segundo ptmxdescritor de arquivo aberto foi simplesmente o processo ptmxpai xtermque não havia sido fechado.
Portanto, as chamadas de tentativa e erro enviaram saída para esse outro terminal.
A maioria dos xtermprocessos não possui dois ptmxdescritores de arquivo.

EDIT FIM

Isso efetivamente digitou essa string no terminal e, portanto, a enviou para o processo em execução sob ele.

nb, talvez seja necessário permitir a conexão com um processo em execução com algo como
sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"

maçã
fonte
0

Como não posso comentar a resposta mais aceita de Cristian Ciupitu (de 2010), tenho que colocar isso em uma resposta separada:

Esta questão já foi resolvida neste segmento: https://stackoverflow.com/questions/5374255/how-to-write-data-to-existing-processs-stdin-from-external-process

Em resumo:

Você deve iniciar seu processo com um canal para stdin que não bloqueia nem fecha quando a entrada atual foi gravada. Isso pode ser implementado por um loop infinito simples que será canalizado para o processo em questão:

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

Posso confirmar que isso é diferente da maneira de Krissi abrir um tubo que não estava funcionando no meu caso. A solução mostrada funcionou.

Você pode gravar no arquivo ... / fd / 0 do processo para enviar instruções para ele. A única desvantagem é que você também precisa encerrar o processo do bash, que executa o loop infinito depois que o servidor foi desligado.

user3471872
fonte