Obter ssh para encaminhar sinais

22

Quero poder enviar sinais (SIGINT é o mais importante) através do ssh.

Este comando:

ssh server "sleep 1000;echo f" > foo

iniciará o modo de suspensão no servidor e após 1000 segundos ele colocará 'f \ n' no arquivo foo na minha máquina local. Se eu pressionar CTRL-C (por exemplo, enviar SIGINT para ssh), ele matará o ssh, mas não matará o sono no servidor remoto. Eu quero matar o sono no servidor remoto.

Então eu tentei:

ssh server -t "sleep 1000;echo f" > foo

Mas se stdin não for um terminal, recebo este erro:

Pseudo-terminal will not be allocated because stdin is not a terminal.

e o SIGINT ainda não é encaminhado.

Então eu tentei:

ssh server -t -t "sleep 1000;echo f" > output

Mas então a saída em foo não é 'f \ n', mas sim 'f \ r \ n', o que é desastroso na minha situação (como minha saída são dados binários).

No exemplo acima, uso "sleep 1000; echo f", mas, na realidade, é fornecido pelo usuário, portanto pode conter qualquer coisa. No entanto, se podemos fazê-lo funcionar para "dormir 1000; eco de", provavelmente podemos fazê-lo para todas as situações realistas.

Realmente não me importo em obter um pseudo-terminal na outra extremidade, mas não consegui encontrar outra maneira de obter o ssh para encaminhar meu SIGINT.

Existe outro caminho?

Editar:

O usuário pode fornecer comandos que leem dados binários do stdin, como:

seq 1000 | gzip | ssh server "zcat|bzip2; sleep 1000" | bzcat > foo

O usuário pode fornecer comandos que consomem muita CPU, como:

ssh server "timeout 1000 burnP6"

Edit2:

A versão que parece funcionar para mim é:

your_preprocessing |
  uuencode a | ssh -tt -oLogLevel=quiet server "stty isig -echoctl -echo ; uudecode -o - |
your_command |
  uuencode a" | uudecode -o - |
your_postprocessing

Obrigado ao digital_infinity por me indicar a direção certa.

Ole Tange
fonte
1
Eu acho que seu uso real sshdeve ser mais complicado do que o que você mostra como exemplos, porque você pode obter o comportamento desejado com um simples rearranjo: sleep 1000 && ssh server "echo f" > foo(Tem que ser &&, não ;, para que matar sleepimpeça a sshexecução do comando). Estou certo, torne seus exemplos mais representativos do seu uso real, para que uma resposta melhor possa ser dada.
Warren Young
Correto: suspensão e eco são, na verdade, scripts fornecidos pelo usuário e não literalmente suspensão e eco. Portanto, não sabemos o que eles fazem e devemos assumir o pior.
Ole Tange
Sooo ... você vai encontrar um comando de exemplo melhor, certo? Um onde um simples rearranjo não resolve o problema? Você faz uma boa pergunta e gostaria que ela fosse respondida, mas é menos provável que você obtenha uma resposta se "não faça isso, então" for uma resposta razoável.
Warren Young
Ah, a propósito ... Não faça isso;)
Tim
Conforme detalhado na pergunta: "" "No exemplo acima, uso" sleep 1000; echo f ", mas, na realidade, é fornecido pelo usuário, portanto pode conter qualquer coisa" ""
Ole Tange

Respostas:

10

Resposta curta:

ssh -t fs "stty isig intr ^N -echoctl ; trap '/bin/true' SIGINT; sleep 1000; echo f" > foo

e pare o programa com CTRL + N.

Explicação longa:

  1. Você deve usar a sttyopção intrpara alterar o servidor ou o caractere de interrupção local para não colidir. No comando acima, alterei o caractere de interrupção do servidor para CTRL + N. Você pode alterar o caractere de interrupção local e deixar o do servidor sem nenhuma alteração.
  2. Se você não deseja que o caractere de interrupção esteja em sua saída (e qualquer outro caractere de controle), use stty -echoctl.
  3. Você deve garantir que os caracteres de controle estejam ativados no bash do servidor chamado pelo sshd. Caso contrário, você pode acabar com os processos ainda pendentes após o logout.stty isig
  4. Você realmente capta o SIGINTsinal trap '/bin/true' SIGINTcom a declaração vazia. Sem a armadilha, você não terá nenhum desvio padrão após o sinal SIGINT do seu lado.
digital_infinity
fonte
Parece não lidar bem com a entrada binária: seq 1000 | gzip | servidor ssh -t -t "stty isig intr ^ N -echoctl; trap '/ bin / true' SIGINT; sleep 1; zcat | bzip2" | bzcat> foo;
precisa
Você não pode interromper pelo console se configurar a entrada padrão para ssh para algo que não seja o console. Nesse caso, você deve interromper pelo fluxo stdin.
digital_infinity
Eu descobri que isso seq 1000 | gzip | ssh -tt dell-test "zcat|bzip2" | bzcat > footambém não funciona. Para que este comando funcione, precisamos removê-lo -tt. Assim, a alocação de pseudo terminal provavelmente leva alguma entrada de stdin
digital_infinity
Eu tentei o uuencode. Nesta versão, não funciona: cat foo.gz | perl -ne 'pacote de impressão ("u", $ _)' | ssh -t -t server 'perl -ne "imprime descompactar (\" u \ ", \ $ _)" | zcat | gzip | perl -ne "pacote de impressão (\" u \ ", \ $ _)" '| perl -ne 'print unpack ("u", $ _)'> foo2.gz
Ole Tange
1
Eu tenho este: (sleep 1; seq 1000 ) | gzip | uuencode bin | ssh -tt dell-test "stty isig intr ^N -echoctl -echo ; trap '/bin/true' SIGINT; sleep 1; uudecode -o /dev/stdout | zcat |bzip2 | uuencode bin2 " | uudecode -o /dev/stdout | bzcat >foo. Embora o caractere de interrupção não funcione - precisamos de algum método de transmissão para o caractere de interrupção enquanto stdin estiver ocupado .
digital_infinity
3

Eu tentei todas as soluções e esta foi a melhor:

ssh host "sleep 99 < <(cat; kill -INT 0)" <&1

/programming/3235180/starting-a-process-over-ssh-using-bash-and-then-killing-it-on-sigint/25882610#25882610

Eric Woodruff
fonte
Isso não interrompe o sleepprocesso caso a conexão seja perdida.
blueyed 13/05
Quando a conexão é perdida, o stdin é quebrado, desbloqueando o gato, o que mata o grupo de processos. Se o sinal do INTerrupt é bom o suficiente para a sua tarefa é uma questão diferente.
Eric Woodruff
Deve ser bom o suficiente para o sleep, não deveria? Eu adicionei alguns date >> /tmp/killedapós o cat, mas não foi acionado. Existe algum tempo limite envolvido? Estou usando o Zsh normalmente, mas também o testei com o bash como o shell de login remoto.
blueyed 14/05
Eu acho que você está à mercê do tempo limite da conexão.
Eric Woodruff
Existem configurações para controlar isso? Para o lado do cliente -o ServerAliveInterval=3 -o ServerAliveCountMax=2permite detectá-lo rapidamente, mas há algo para o lado do servidor?
blueyed 15/05
2

Eu acho que você pode encontrar o PID do processo em execução no servidor e enviar um sinal usando outro sshcomando (como este ssh server "kill -2 PID":).

Eu uso esse método para enviar sinais de reconfiguração para aplicativos em execução em uma máquina diferente (meus aplicativos capturam o SIGUSR1 e leem um arquivo de configuração). No meu caso, é fácil encontrar o PID, pois tenho nomes de processo exclusivos e posso encontrá-lo enviando uma pssolicitação via ssh.

saeedn
fonte
Não vejo uma maneira à prova de balas de encontrar o PID do programa que está sendo executado no controle remoto. Lembre-se de que é dado pelo usuário. Poderia ser: servidor ssh '$ exec (echo fyrrc 1000 | / usr / games / rot13)'
Ole Tange
1

A solução evoluiu para http://www.gnu.org/software/parallel/parallel_design.html#Remote-Ctrl-C-and-standard-error-stderr

$SIG{CHLD} = sub { $done = 1; };
$pid = fork;
unless($pid) {
    # Make own process group to be able to kill HUP it later
    setpgrp;
    exec $ENV{SHELL}, "-c", ($bashfunc."@ARGV");
    die "exec: $!\n";
}
do {
    # Parent is not init (ppid=1), so sshd is alive
    # Exponential sleep up to 1 sec
    $s = $s < 1 ? 0.001 + $s * 1.03 : $s;
    select(undef, undef, undef, $s);
} until ($done || getppid == 1);
# Kill HUP the process group if job not done
kill(SIGHUP, -${pid}) unless $done;
wait;
exit ($?&127 ? 128+($?&127) : 1+$?>>8)
Ole Tange
fonte
0

----- command.sh

#! /bin/sh
trap 'trap - EXIT; kill 0; exit' EXIT
(sleep 1000;echo f) &
read ans

----- no terminal local

sleep 864000 | ssh -T server command.sh > foo
hóspede
fonte
Oi! Acho que você poderia tornar sua resposta muito mais útil, explicando, com o máximo de detalhes que julgar relevantes, como ela funciona. Não hesite em editá-lo para inserir novas informações.
dhag