stderr sobre ssh -t

11

Envia a saída para STDERR, mas não propaga Ctrl + C (isto é, Ctrl + C mata ssh , mas não o controle remoto sleep ):

$ ssh localhost 'sleep 100;echo foo ">&2"'

Isso propaga Ctrl + C (isto é, Ctrl + C matará ssh e o controle remoto sleep ), mas envia STDERR para STDOUT:

$ ssh -tt localhost 'sleep 100;echo foo ">&2"'

Como posso forçar o segundo a enviar a saída STDERR para STDERR, enquanto ainda propaga Ctrl + C ?

Plano de fundo

GNU Parallel usa 'ssh -tt' para propagar Ctrl + C . Isso permite matar tarefas em execução remotamente. Mas os dados enviados para o STDERR devem continuar para o STDERR no final do recebimento.

    
por Ole Tange 02.06.2014 / 19:09

2 respostas

5

Eu não acho que você possa contornar isso.

Com -tt , sshd gera um pseudo-terminal e faz da parte escrava stdin, stdout e stderr do shell que executa o comando remoto.

sshd lê o que está vindo de seu (único) fd para a parte principal do pseudo-terminal e envia (por meio de um único canal) para o cliente ssh . Não há segundo canal para stderr, pois não há -t .

Além disso, observe que a disciplina de linha terminal do pseudo-terminal pode (e por padrão) alterar a saída. Por exemplo, o LF será convertido em CRLF e não no terminal local, portanto você pode querer desabilitar o pós-processamento de saída.

$ ssh  localhost 'echo x' | hd
00000000  78 0a                                             |x.|
00000002
$ ssh -t localhost 'echo x' | hd
00000000  78 0d 0a                                          |x..|
00000003
$ ssh -t localhost 'stty -opost; echo x' | hd
00000000  78 0a                                             |x.|
00000002

Muito mais coisas acontecerão no lado da entrada (como o caractere ^C que causará um SIGINT, mas também outros sinais, o eco e todo o tratamento envolvido na linha modo canônico editor).

Você poderia redirecionar o stderr para o fifo e recuperá-lo usando um segundo ssh :

ssh -tt host 'mkfifo fifo && cmd 2> fifo' &
ssh host 'cat fifo' >&2

Mas o melhor IMO seria evitar usar -t no total. Isso é apenas para uso interativo de um terminal real.

Em vez de depender da transmissão de um ^ C para permitir que o controle remoto termine a conexão, você pode usar um wrapper que execute poll() para detectar a conexão ssh ou fechada encerrada.

Talvez algo como (simplificado, você deseje adicionar alguma verificação de erros):

LC_HUP_DETECTOR='
  use IO::Poll;
  $SIG{CHLD} = sub {$done = 1};
  $p = IO::Poll->new;
  $p->mask(STDOUT, POLLIN);
  $pid=fork; unless($pid) {setpgrp; exec @ARGV; die "exec: $!\n"}
  $p->poll;
  kill SIGHUP, -$pid unless $done;
  wait; exit ($?&127 ? 128+($?&127) : 1+$?>>8)
' ssh host 'perl -e "$LC_HUP_DETECTOR" some cmd'

O $p->mask(STDOUT, POLLIN) acima pode parecer bobo, mas a idéia é esperar por um evento de hang-hup (para o fim de leitura do canal no stdout ser fechado). POLLHUP como uma máscara solicitada é ignorada. O POLLHUP é apenas significativo como um evento retornado (para dizer que o fim escrito foi fechado).

Precisamos dar um valor diferente de zero para a máscara de evento. Se usarmos 0 , perl nem mesmo chamará poll . Então aqui nós usamos POLLIN.

No Linux, o que você requisitar, se o pipe for quebrado, poll () retorna POLLERR.

No Solaris e no FreeBSD, onde os pipes são bidirecionais, quando o final de leitura do pipe (que também é um fim de escrita) é fechado, ele retorna com POLLHUP (e POLLIN no FreeBSD, onde você precisa solicitar POLLIN ou então $p->poll() não retorna).

Eu não posso dizer como é portável fora desses três sistemas operacionais.

    
por 02.06.2014 / 23:28
1

Para fazê-lo funcionar em outras plataformas, isso se tornou a solução final. Ele verifica se o cliente ssh foi desconectado e, portanto, o pai se tornou pid 1:

$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)
    
por 23.05.2015 / 20:21