stderr über ssh -t

11

Dies sendet eine Ausgabe an STDERR, verbreitet aber nicht Ctrl+ C(dh Ctrl+ Ctötet, sshaber nicht die Fernbedienung sleep):

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

Dies verbreitet Ctrl+ C(dh Ctrl+ Ctötet sshund die Fernbedienung sleep), sendet jedoch STDERR an STDOUT:

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

Wie kann ich die Sekunde zwingen, STDERR-Ausgabe an STDERR zu senden, während Ctrl+ weitergeführt wird C?

Hintergrund

GNU Parallel verwendet 'ssh -tt', um Ctrl+ zu verbreiten C. Auf diese Weise können remote ausgeführte Jobs beendet werden. An STDERR gesendete Daten sollten jedoch weiterhin am empfangenden Ende an STDERR gesendet werden.

Ole Tange
quelle

Antworten:

5

Ich glaube nicht, dass Sie das umgehen können.

Mit -tt, sshderzeugt einen Pseudo-Terminal und macht den Slave - Teil der stdin, stdout und Stderr der Schale, die den Fernbefehl ausführt.

sshdliest, was von seinem (einzelnen) fd zum Master-Teil des Pseudo-Terminals kommt, und sendet dies (über einen einzelnen Kanal) an den sshClient. Es gibt keinen zweiten Kanal für stderr wie ohne -t.

Beachten Sie außerdem, dass die Terminalleitungsdisziplin des Pseudo-Terminals die Ausgabe ändern kann (und dies standardmäßig tun wird). Beispielsweise wird der LF dort und nicht auf dem lokalen Terminal in CRLF konvertiert, sodass Sie möglicherweise die Ausgabe-Nachbearbeitung deaktivieren möchten.

$ 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

Auf der Eingabeseite werden viel mehr Dinge passieren (wie das ^CZeichen, das ein SIGINT verursacht, aber auch andere Signale, das Echo und die gesamte Handhabung im Zeileneditor des kanonischen Modus ).

Sie könnten stderr möglicherweise zu einem fifo umleiten und es mit einer Sekunde abrufen ssh:

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

Aber die beste IMO wäre es, die Verwendung -tinsgesamt zu vermeiden . Das ist eigentlich nur für die interaktive Nutzung von einem echten Terminal aus gedacht.

Anstatt sich auf die Übertragung eines ^ C zu verlassen, damit die Verbindung geschlossen wird, können Sie einen Wrapper verwenden, der poll()die unterbrochene sshoder geschlossene Verbindung erkennt .

Vielleicht so etwas wie (vereinfacht, Sie möchten eine Fehlerprüfung hinzufügen):

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'

Das $p->mask(STDOUT, POLLIN)Obige mag albern erscheinen, aber die Idee ist, auf ein Hang-Hup-Ereignis zu warten (bis das Leseende der Pipe auf stdout geschlossen ist). POLLHUP als angeforderte Maske wird ignoriert. POLLHUP ist nur als zurückgegebenes Ereignis von Bedeutung (um anzuzeigen, dass das Schreibende geschlossen wurde).

Wir müssen einen Wert ungleich Null für die Ereignismaske angeben. Wenn wir verwenden 0, perlruft nicht einmal an poll. Hier verwenden wir also POLLIN.

Unabhängig von Ihrer Anforderung gibt poll () POLLERR zurück, wenn die Pipe unterbrochen wird.

Unter Solaris und FreeBSD, wo Pipes bidirektional sind, wird das Leseende der Pipe (das dort auch ein Schreibende ist) mit POLLHUP zurückgegeben (und POLLIN unter FreeBSD, wo Sie POLLIN anfordern müssen oder $p->poll()nicht Rückkehr).

Ich kann nicht sagen, wie portabel es sonst außerhalb dieser drei Betriebssysteme ist.

Stéphane Chazelas
quelle
Ich mag Ihre Idee, aber ich kann Ihren Wrapper nur dann dazu bringen, Signale zu erkennen, wenn '-tt' eingestellt ist. Dies funktioniert : parallel --tag -j1 'ssh -tt localhost perl/catch_wrap perl/catch_all_signals & sleep 1; killall -{} ssh' ::: {1..31}, aber entfernen Sie das '-tt' und dann funktioniert es nicht.
Ole Tange
@OleTange Der Zweck des Wrappers besteht darin, dass SIGHUP an den Remote-Job gesendet wird, wenn ssh stirbt (beim Auflegen der ssh-Verbindung). Ich weiß nicht, was Ihre catch_all_signals tun, aber alles, was es bekommen würde, ist das SIGHUP und erst nachdem die ssh-Verbindung unterbrochen wurde (wenn es also etwas auf stdout druckt, werden Sie es nicht sehen).
Stéphane Chazelas
catch_all_signals protokolliert alle Signale in einer Datei und funktioniert wie erwähnt mit '-tt', schlägt jedoch ohne fehl. Mit anderen Worten: Es erhält kein SIGHUP von catch_wrap, wenn ssh stirbt.
Ole Tange
Funktioniert immer noch nur -ttnach dem Bearbeiten. Bitte denken Sie daran, wenn Sie den Befehl nicht parallel ausführen, erbt ssh das Terminal, von dem aus Sie ihn ausführen.
Ole Tange
@OleTange, ich kann nicht reproduzieren, es funktioniert für mich, hast du es mit dem Code getestet, den ich gepostet habe? Bitte poste deine catch_wrap und catch_all_signals irgendwo, damit ich einen Blick darauf werfen kann. Mit -terwarte ich, dass es nicht funktioniert.
Stéphane Chazelas
1

Damit es auf anderen Plattformen funktioniert, wurde dies die endgültige Lösung. Es wird geprüft, ob der SSH-Client die Verbindung getrennt hat und somit das übergeordnete Element zu PID 1 wurde:

$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
quelle