Uzyskaj ssh do przekazywania sygnałów

22

Chcę mieć możliwość wysyłania sygnałów (najważniejszy jest SIGINT) przez ssh.

To polecenie:

ssh server "sleep 1000;echo f" > foo

rozpocznie tryb uśpienia na serwerze i po 1000 sekundach umieści „f \ n” w pliku foo na moim komputerze lokalnym. Jeśli nacisnę CTRL-C (tj. Wyślę SIGINT do ssh), to zabije ssh, ale nie zabije snu na zdalnym serwerze. Chcę, żeby zabiło sen na zdalnym serwerze.

Więc próbowałem:

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

Ale jeśli stdin nie jest terminalem, pojawia się ten błąd:

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

a następnie SIGINT nadal nie jest przekazywany.

Więc próbowałem:

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

Ale wtedy wyjście w foo nie jest „f \ n”, ale zamiast „f \ r \ n”, co jest katastrofalne w mojej sytuacji (ponieważ moje dane wyjściowe to dane binarne).

W powyższym przykładzie używam „sleep 1000; echo f”, ale w rzeczywistości jest on dostarczany przez użytkownika, więc może zawierać wszystko. Jeśli jednak uda nam się sprawić, że będzie działał dla „snu 1000; echo f”, najprawdopodobniej sprawimy, że będzie działał we wszystkich realistycznych sytuacjach.

Naprawdę nie dbam o to, aby uzyskać pseudo-terminal na drugim końcu, ale nie byłem w stanie znaleźć żadnego innego sposobu na uzyskanie ssh do przekazania mojego SIGINT.

Czy jest inny sposób?

Edytować:

Użytkownik może wydawać polecenia odczytujące dane binarne ze standardowego wejścia, takie jak:

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

Użytkownik może wydawać polecenia wymagające dużej mocy obliczeniowej, takie jak:

ssh server "timeout 1000 burnP6"

Edycja2:

Wydaje mi się, że wersja działa:

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

Dzięki digital_infinity za wskazanie mi właściwego kierunku.

Ole Tange
źródło
1
Myślę, że twoje rzeczywiste użycie sshmusi być bardziej skomplikowane niż to, co pokazujesz jako przykłady, ponieważ możesz uzyskać pożądane zachowanie za pomocą prostej rearanżacji: sleep 1000 && ssh server "echo f" > foo(Musi być &&, nie ;, aby zabijanie sleepuniemożliwiało uruchomienie sshpolecenia). Jeśli ja mam rację, proszę, aby przykłady były bardziej reprezentatywne dla faktycznego wykorzystania, aby można było podać lepszą odpowiedź.
Warren Young,
Prawidłowo: tryb uśpienia i echa to w rzeczywistości skrypty dostarczone przez użytkownika, a nie dosłownie tryb uśpienia i echa. Nie wiemy więc, co robią, i powinniśmy zakładać najgorsze.
Ole Tange
Sooo ... wymyślisz lepsze przykładowe polecenie, prawda? Taki, w którym prosta zmiana układu nie rozwiązuje problemu? Zadajesz dobre pytanie i chciałbym, aby na nie udzielono odpowiedzi, ale jest mniej prawdopodobne, że otrzymasz odpowiedź, jeśli „więc nie rób tego, to” jest rozsądną odpowiedzią.
Warren Young,
Och, nawiasem mówiąc ... Nie rób tego;)
Tim
Jak wyjaśniono w pytaniu: „” „W powyższym przykładzie używam„ sleep 1000; echo f ”, ale w rzeczywistości jest on dostarczany przez użytkownika, więc może zawierać wszystko„ ”„
Ole Tange

Odpowiedzi:

10

Krótka odpowiedź:

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

i zatrzymaj program CTRL + N.

Długie wyjaśnienie:

  1. Musisz użyć sttyopcji, intraby zmienić serwer lub lokalny znak przerwania, aby nie kolidowały ze sobą. W powyższym poleceniu zmieniłem znak przerwania serwera na CTRL + N. Możesz zmienić lokalny charakter przerwań i pozostawić serwer bez zmian.
  2. Jeśli nie chcesz, aby znak przerwania był na wyjściu (i jakikolwiek inny znak kontrolny), użyj stty -echoctl.
  3. Musisz upewnić się, że znaki kontrolne są włączone na serwerze bash wywołanym przez sshd. Jeśli nie, możesz skończyć z procesami, które wciąż się zawieszają po wylogowaniu.stty isig
  4. Właściwie łapiesz SIGINTsygnał za trap '/bin/true' SIGINTpomocą pustej instrukcji. Bez pułapki nie będziesz mieć żadnego standardowego sygnału po sygnale SIGINT na swoim końcu.
digital_infinity
źródło
Wydaje się, że nie radzi sobie dobrze z wejściami binarnymi: seq 1000 | gzip | ssh -t -t server "stty isig intr ^ N -echoctl; trap '/ bin / true' SIGINT; sleep 1; zcat | bzip2" | bzcat> foo;
Ole Tange
Nie możesz przerwać konsoli, jeśli ustawisz standardowe wejście dla ssh dla czegoś innego niż konsola. W takim przypadku musisz przerwać przez strumień standardowego wejścia.
digital_infinity
Odkryłem, że to seq 1000 | gzip | ssh -tt dell-test "zcat|bzip2" | bzcat > footeż nie działa. Aby to polecenie działało, musimy je usunąć -tt. Przydział pseudo terminala prawdopodobnie wymaga pewnej ilości danych wejściowych ze standardowego wejścia
digital_infinity
Próbowałem uuencode. W tej wersji nie działa: cat foo.gz | perl -ne 'pakiet wydruku („u”, $ _) ”| ssh -t -t server 'perl -ne "print unpack (\" u \ ", \ $ _)" | zcat | gzip | perl -ne "pakiet wydruku (\" u \ ", \ $ _)" '| perl -ne 'print unpack ("u", $ _)'> foo2.gz
Ole Tange
1
Mam to: (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. Chociaż znak przerwania nie działa - potrzebujemy metody transmisji dla znaku przerwania, gdy standardowe wejście jest zajęte .
digital_infinity
3

Wypróbowałem wszystkie rozwiązania i było to najlepsze:

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
źródło
Nie zatrzymuje to sleepprocesu w przypadku utraty połączenia.
blueyed
Po utracie połączenia stdin zostaje zerwane, odblokowując kota, który zabije grupę procesów. To, czy ten sygnał przerwania jest wystarczający dla twojego zadania, jest innym pytaniem.
Eric Woodruff,
To powinno wystarczyć sleep, prawda? Dodałem kilka date >> /tmp/killedpo cat, ale nie zostało uruchomione. Czy jest jakiś limit czasu? Używam Zsh normalnie, ale przetestowałem go również za pomocą bash jako zdalnej powłoki logowania.
niebiesko
Myślę, że jesteś na łasce limitu czasu połączenia.
Eric Woodruff,
Czy istnieją ustawienia, aby to kontrolować? Po stronie klienta -o ServerAliveInterval=3 -o ServerAliveCountMax=2pozwala to szybko wykryć, ale czy jest coś po stronie serwera?
niebieskawy
2

Myślę, że możesz znaleźć PID procesu uruchomionego na serwerze i wysłać sygnał za pomocą innego sshpolecenia (takiego jak to ssh server "kill -2 PID":).

Używam tej metody do wysyłania sygnałów rekonfiguracyjnych do aplikacji działających na innym komputerze (moje aplikacje łapią SIGUSR1 i czytają plik konfiguracyjny). W moim przypadku znalezienie PID jest łatwe, ponieważ mam unikalne nazwy procesów i mogę znaleźć PID, wysyłając psżądanie za pośrednictwem ssh.

saeedn
źródło
Nie widzę kuloodpornego sposobu znalezienia PID programu uruchamianego na odległość. Pamiętaj, że podany przez użytkownika. Może to być: ssh server 'exec $ (echo fyrrc 1000 | / usr / games / rot13)'
Ole Tange
1

Rozwiązanie rozwinęło się w 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
źródło
0

----- polecenie.sh

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

----- na lokalnym terminalu

sleep 864000 | ssh -T server command.sh > foo
Gość
źródło
Cześć! Myślę, że możesz uczynić swoją odpowiedź o wiele bardziej przydatną, wyjaśniając, tak szczegółowo, jak uznasz to za istotne, jak to działa. Nie wahaj się go edytować, aby wstawić nowe informacje.
dhag