stderr sur ssh -t

11

Cela envoie la sortie à STDERR, mais ne propage pas Ctrl+ C(c'est-à-dire que Ctrl+ Ctuera sshmais pas la télécommande sleep):

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

Cela propage Ctrl+ C(c'est-à-dire que Ctrl+ Ctuera sshet la télécommande sleep), mais envoie STDERR à STDOUT:

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

Comment puis-je forcer le second à envoyer la sortie STDERR à STDERR, tout en propageant Ctrl+ C?

Contexte

GNU Parallel utilise 'ssh -tt' pour propager Ctrl+ C. Cela permet de supprimer les travaux exécutés à distance. Mais les données envoyées à STDERR devraient continuer à aller à STDERR à l'extrémité de réception.

Ole Tange
la source

Réponses:

5

Je ne pense pas que vous puissiez contourner cela.

Avec -tt, sshdengendre un pseudo-terminal et fait de la partie esclave les stdin, stdout et stderr du shell qui exécute la commande à distance.

sshdlit ce qui vient de son (unique) fd vers la partie maître du pseudo-terminal et l'envoie (via un seul canal) au sshclient. Il n'y a pas de deuxième canal pour stderr comme il n'y en a pas -t.

De plus, notez que la discipline de ligne terminale du pseudo-terminal peut (et modifiera par défaut) la sortie. Par exemple, le LF sera converti en CRLF là-bas et non sur le terminal local, vous pouvez donc désactiver le post-traitement de sortie.

$ 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

Beaucoup plus de choses se produiront du côté de l'entrée (comme le ^Ccaractère qui provoquera un SIGINT, mais aussi d'autres signaux, l'écho et toute la gestion impliquée dans l' éditeur de ligne en mode canonique ).

Vous pouvez éventuellement rediriger stderr vers un fifo et le récupérer en utilisant une seconde ssh:

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

Mais le meilleur IMO serait d'éviter d'utiliser -tcomplètement. Cela n'est vraiment destiné qu'à une utilisation interactive à partir d'un vrai terminal.

Au lieu de compter sur la transmission d'un ^ C pour laisser l'extrémité distante la connexion est fermée, vous pouvez utiliser un wrapper qui fait un poll()pour détecter la sshconnexion interrompue ou fermée.

Peut-être quelque chose comme (simplifié, vous voudrez ajouter une vérification d'erreur):

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'

Ce qui $p->mask(STDOUT, POLLIN)précède peut sembler idiot, mais l'idée est d'attendre un événement de blocage (pour que l'extrémité de lecture du tuyau sur stdout soit fermée). POLLHUP en tant que masque demandé est ignoré. POLLHUP n'a de sens que comme un événement retourné (pour indiquer que la fin de l' écriture a été fermée).

Nous devons donner une valeur non nulle pour le masque d'événement. Si nous utilisons 0, perln'appelle même pas poll. Nous utilisons donc ici POLLIN.

Sous Linux, quoi que vous demandiez, si le canal est cassé, poll () renvoie POLLERR.

Sur Solaris et FreeBSD, où les tuyaux sont bi - directionnel, lorsque la fin de la lecture de la conduite (qui est aussi une extrémité d'écriture là - bas) est fermé, il revient avec POLLHUP (et POLLIN sur FreeBSD, où vous devez demander POLLIN ou bien $p->poll()ne pas revenir).

Je ne peux pas dire à quel point il est portable en dehors de ces trois systèmes d'exploitation.

Stéphane Chazelas
la source
J'aime votre idée, mais je ne peux pas faire en sorte que votre wrapper détecte des signaux à moins que «-tt» ne soit défini. Cela fonctionne parallel --tag -j1 'ssh -tt localhost perl/catch_wrap perl/catch_all_signals & sleep 1; killall -{} ssh' ::: {1..31}:, mais supprimez le '-tt' et cela ne fonctionnera pas.
Ole Tange
@OleTange Le but de l'encapsuleur est d'envoyer SIGHUP au travail distant lorsque ssh meurt (lors du raccrochage de la connexion ssh). Je ne sais pas ce que fait votre catch_all_signals, mais tout ce qu'il obtiendrait, c'est que SIGHUP et seulement après que la connexion ssh soit tombée (donc s'il imprime quelque chose sur stdout, vous ne le verrez pas).
Stéphane Chazelas
catch_all_signals enregistre tous les signaux dans un fichier, et comme mentionné, il fonctionne avec '-tt', mais échoue sans. En d'autres termes: il ne reçoit pas de SIGHUP de catch_wrap lorsque ssh meurt.
Ole Tange
Fonctionne toujours avec -ttaprès votre modification. N'oubliez pas que si vous n'exécutez pas la commande en parallèle, ssh héritera du terminal à partir duquel vous l'exécutez.
Ole Tange
@OleTange, je ne peux pas reproduire, cela fonctionne pour moi, l'avez-vous testé avec le code que j'ai posté? Veuillez poster vos catch_wrap et catch_all_signals quelque part afin que je puisse y jeter un œil. Avec -t, je m'attends à ce que ça ne marche pas.
Stéphane Chazelas
1

Pour le faire fonctionner sur d'autres plateformes, c'est devenu la solution finale. Il vérifie si le client ssh s'est déconnecté et donc le parent est devenu 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)
Ole Tange
la source