Obtenez ssh pour transmettre les signaux

22

Je veux pouvoir envoyer des signaux (SIGINT est le plus important) via ssh.

Cette commande:

ssh server "sleep 1000;echo f" > foo

va commencer à dormir sur le serveur et après 1000 secondes, il mettra 'f \ n' dans le fichier foo sur ma machine locale. Si j'appuie sur CTRL-C (c'est-à-dire que j'envoie SIGINT à ssh), cela tuera ssh, mais il ne tuera pas sleep sur le serveur distant. Je veux qu'il tue le sommeil sur le serveur distant.

J'ai donc essayé:

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

Mais si stdin n'est pas un terminal, j'obtiens cette erreur:

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

puis SIGINT n'est toujours pas transmis.

J'ai donc essayé:

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

Mais alors la sortie dans foo n'est pas 'f \ n' mais plutôt 'f \ r \ n' ce qui est désastreux dans ma situation (car ma sortie est des données binaires).

Dans ce qui précède, j'utilise "sleep 1000; echo f", mais en réalité qui est fourni par l'utilisateur, il peut donc contenir n'importe quoi. Cependant, si nous pouvons le faire fonctionner pour "sleep 1000; echo f", nous pouvons très probablement le faire fonctionner pour toutes les situations réalistes.

Je ne me soucie vraiment pas d'obtenir un pseudo-terminal à l'autre extrémité, mais je n'ai pas pu trouver d'autre moyen de faire en sorte que ssh transfère mon SIGINT.

Y a-t-il une autre façon?

Modifier:

L'utilisateur peut donner des commandes qui lisent des données binaires depuis stdin, telles que:

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

L'utilisateur peut donner des commandes gourmandes en CPU, telles que:

ssh server "timeout 1000 burnP6"

Edit2:

La version qui semble fonctionner pour moi est:

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

Merci à digital_infinity de m'avoir pointé dans la bonne direction.

Ole Tange
la source
1
Je pense que votre utilisation réelle de sshdoit être plus compliquée que ce que vous montrez à titre d'exemples, car vous pouvez obtenir le comportement que vous souhaitez avec un simple réarrangement: sleep 1000 && ssh server "echo f" > foo(Il doit l'être &&, non ;, afin que la suppression sleepempêche l' sshexécution de la commande.) Si je avez raison, veuillez rendre vos exemples plus représentatifs de votre utilisation réelle, afin qu'une meilleure réponse puisse être donnée.
Warren Young
Correct: sleep et echo sont en fait des scripts fournis par l'utilisateur et non littéralement sleep et echo. Nous ne savons donc pas ce qu'ils font et devons assumer le pire.
Ole Tange
Sooo ... vous allez trouver un meilleur exemple de commande, non? Celui où un simple réarrangement ne résout pas le problème? Vous posez une bonne question, et j'aimerais qu'il y soit répondu, mais vous êtes moins susceptible d'obtenir une réponse si "alors ne faites pas cela, alors" est une réponse raisonnable.
Warren Young
Oh au fait ... Ne fais pas ça;)
Tim
Comme expliqué dans la question: "" "Dans ce qui précède, j'utilise" sleep 1000; echo f ", mais en réalité qui est fourni par l'utilisateur, il peut donc contenir n'importe quoi" ""
Ole Tange

Réponses:

10

Réponse courte:

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

et arrêtez le programme par CTRL + N.

Explication longue:

  1. Vous devez utiliser l' sttyoption intrpour modifier votre serveur ou le caractère d'interruption local pour ne pas entrer en collision les uns avec les autres. Dans la commande ci-dessus, j'ai changé le caractère d'interruption du serveur en CTRL + N. Vous pouvez modifier votre caractère d'interruption local et laisser celui du serveur sans aucun changement.
  2. Si vous ne voulez pas que le caractère d'interruption soit dans votre sortie (et tout autre caractère de contrôle), utilisez stty -echoctl.
  3. Vous devez vous assurer que les caractères de contrôle sont activés sur le serveur bash appelé par sshd. Si vous ne le faites pas, vous pouvez vous retrouver avec des processus qui traînent toujours après votre déconnexion.stty isig
  4. En fait, vous capturez le SIGINTsignal trap '/bin/true' SIGINTavec une instruction vide. Sans le piège, vous n'aurez aucune sortie après le signal SIGINT de votre côté.
digital_infinity
la source
Il semble ne pas bien gérer l'entrée binaire: seq 1000 | gzip | ssh -t -t server "stty isig intr ^ N -echoctl; trap '/ bin / true' SIGINT; sleep 1; zcat | bzip2" | bzcat> foo;
Ole Tange
Vous ne pouvez pas interrompre par console si vous définissez une entrée standard pour ssh pour autre chose que la console. Dans ce cas, vous devez interrompre par le flux stdin.
digital_infinity
J'ai trouvé que cela seq 1000 | gzip | ssh -tt dell-test "zcat|bzip2" | bzcat > foone fonctionne pas trop. Pour que cette commande fonctionne, nous devons la supprimer -tt. Donc, l'allocation pseudo-terminale prend probablement une entrée de stdin
digital_infinity
J'ai essayé le uuencode. Dans cette version, cela ne fonctionne pas: cat foo.gz | perl -ne 'print pack ("u", $ _)' | ssh -t -t server 'perl -ne "print unpack (\" u \ ", \ $ _)" | zcat | gzip | perl -ne "pack d'impression (\" u \ ", \ $ _)" '| perl -ne 'print unpack ("u", $ _)'> foo2.gz
Ole Tange
1
J'ai ceci: (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. Bien que le caractère d'interruption ne fonctionne pas - nous avons besoin d'une méthode de transmission pour le caractère d'interruption lorsque stdin est occupé .
digital_infinity
3

J'ai essayé toutes les solutions et c'était la meilleure:

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
la source
Cela n'arrête pas le sleepprocessus en cas de perte de connexion.
bleuté
Lorsque la connexion est perdue, le stdin est rompu débloquant le chat qui tuera le groupe de processus. Que ce signal INTerrupt soit suffisant pour votre tâche est une question différente.
Eric Woodruff
Ça devrait être assez bon pour sleepça, non? J'en ai ajouté quelques-uns date >> /tmp/killedaprès le cat, mais cela n'a pas été déclenché. Y a-t-il un délai d'attente impliqué? J'utilise Zsh normalement, mais je l'ai également testé avec bash comme shell de connexion à distance.
bleuté
Je pense que vous êtes à la merci du délai de connexion.
Eric Woodruff
Existe-t-il des paramètres pour contrôler cela? Car côté client -o ServerAliveInterval=3 -o ServerAliveCountMax=2permet de le détecter rapidement, mais y a-t-il quelque chose pour le côté serveur?
bleuté
2

Je pense que vous pouvez trouver le PID du processus que vous exécutez sur le serveur et envoyer un signal en utilisant une autre sshcommande (comme ceci:) ssh server "kill -2 PID".

J'utilise cette méthode pour envoyer des signaux de reconfiguration à des applications fonctionnant sur une machine différente (mes applications attrapent SIGUSR1 et lisent un fichier de configuration). Dans mon cas, trouver un PID est facile, car j'ai des noms de processus uniques et je peux trouver le PID en envoyant une psdemande via ssh.

saeedn
la source
Je ne vois pas de moyen à l'épreuve des balles de trouver le PID du programme exécuté sur la télécommande. N'oubliez pas qu'il est fourni par l'utilisateur. Cela pourrait être: le serveur ssh 'exec $ (echo fyrrc 1000 | / usr / games / rot13)'
Ole Tange
1

La solution est devenue 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
la source
0

----- command.sh

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

----- sur le terminal local

sleep 864000 | ssh -T server command.sh > foo
client
la source
Salut! Je pense que vous pourriez rendre votre réponse beaucoup plus utile en expliquant, avec autant de détails que vous le jugez pertinent, comment cela fonctionne. N'hésitez pas à le modifier pour insérer de nouvelles informations.
dhag