stderr sobre ssh -t


11

Esto envía la salida a STDERR, pero no propaga Ctrl+ C(es decir, Ctrl+ Cmatará sshpero no el control remoto sleep):

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

Esto propaga Ctrl+ C(es decir, Ctrl+ Cmatará sshy el control remoto sleep), pero envía STDERR a STDOUT:

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

¿Cómo puedo forzar al segundo a enviar la salida STDERR a STDERR, mientras sigue propagando Ctrl+ C?

Antecedentes

GNU Parallel usa 'ssh -tt' para propagar Ctrl+ C. Esto hace posible eliminar trabajos que se ejecutan de forma remota. Pero los datos enviados a STDERR deberían continuar yendo a STDERR en el extremo receptor.

Respuestas:


5

No creo que puedas evitar eso.

Con -tt, sshdgenera un pseudo-terminal y hace que la parte esclava sea el stdin, stdout y stderr del shell que ejecuta el comando remoto.

sshdlee lo que viene de su fd (único) a la parte maestra del pseudo-terminal y lo envía (a través de un solo canal) al sshcliente. No hay un segundo canal para stderr, ya que no existe -t.

Además, tenga en cuenta que la disciplina de línea terminal del pseudo-terminal puede (y por defecto) alterará la salida. Por ejemplo, el LF se convertirá en CRLF allí y no en el terminal local, por lo que es posible que desee deshabilitar el procesamiento posterior de salida.

$ 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

Sucederán muchas más cosas en el lado de entrada (como el ^Ccarácter que causará un SIGINT, pero también otras señales, el eco y todo el manejo involucrado en el editor de línea de modo canónico ).

Posiblemente podría redirigir stderr a un fifo y recuperarlo usando un segundo ssh:

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

Pero la mejor OMI sería evitar el uso por -tcompleto. Eso realmente solo está destinado al uso interactivo desde un terminal real.

En lugar de confiar en la transmisión de un ^ C para permitir que el control remoto finalice la conexión, puede usar un contenedor que haga un a poll()para detectar la sshconexión desconectada o cerrada.

Tal vez algo como (simplificado, querrás agregar alguna comprobación de errores):

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'

Lo $p->mask(STDOUT, POLLIN)anterior puede parecer una tontería, pero la idea es esperar un evento de hang-hup (para que se cierre el extremo de lectura de la tubería en stdout). POLLHUP como máscara solicitada se ignora. POLLHUP solo tiene sentido como un evento devuelto (para indicar que el final de la escritura se ha cerrado).

Tenemos que dar un valor distinto de cero para la máscara de eventos. Si usamos 0, perlni siquiera llama poll. Entonces aquí usamos POLLIN.

En Linux, cualquier cosa que solicite, si la tubería se rompe, poll () devuelve POLLERR.

En Solaris y FreeBSD, donde las tuberías son bidireccionales, cuando el extremo de lectura de la tubería (que también es un extremo de escritura allí) está cerrado, regresa con POLLHUP (y POLLIN en FreeBSD, donde tiene que solicitar POLLIN o $p->poll()no lo hace). regreso).

No puedo decir qué tan portátil es fuera de esos tres sistemas operativos.


Me gusta tu idea, pero no puedo hacer que tu contenedor detecte ninguna señal a menos que esté configurado '-tt'. Esto funciona: parallel --tag -j1 'ssh -tt localhost perl/catch_wrap perl/catch_all_signals & sleep 1; killall -{} ssh' ::: {1..31}pero elimina el '-tt' y luego no funciona.
Ole Tange

@OleTange El propósito de la envoltura es que SIGHUP se envíe al trabajo remoto cuando ssh falle (al colgar la conexión ssh). No sé qué hace su catch_all_signals, pero todo lo que obtendría es ese SIGHUP y solo después de que la conexión ssh se haya caído (por lo que si imprime algo en stdout, no lo verá).
Stéphane Chazelas

catch_all_signals registra todas las señales en un archivo y, como se mencionó, funciona con '-tt', pero falla sin él. En otras palabras: no recibe un SIGHUP de catch_wrap cuando ssh muere.
Ole Tange

Todavía solo funciona -ttdespués de su edición. Recuerde que si no ejecuta el comando en paralelo, ssh heredará el terminal desde el que lo ejecuta.
Ole Tange

@OleTange, no puedo reproducir, funciona para mí, ¿lo has probado con el código que publiqué? Publique su catch_wrap y catch_all_signals en algún lugar para que pueda echar un vistazo. Con -t, espero que no funcione.
Stéphane Chazelas

1

Para que funcione en otras plataformas, esta se convirtió en la solución final. Comprueba si el cliente ssh se desconectó y, por lo tanto, el padre se convirtió en 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)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.