stderr над ssh -t


11

Це посилає вихід на STDERR, але не поширює Ctrl+ C(тобто Ctrl+ Cбуде вбивати, sshале не віддалений sleep):

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

Це поширює Ctrl+ C(тобто Ctrl+ Cбуде вбивати sshі віддалений sleep), але посилає STDERR в STDOUT:

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

Як я можу змусити другий надсилати STDERR вихід на STDERR, продовжуючи поширювати Ctrl+ C?

Фон

GNU Parallel використовує 'ssh -tt' для поширення Ctrl+ C. Це дає змогу вбивати віддалено працюючі завдання. Але дані, надіслані STDERR, повинні продовжувати надходити до STDERR на кінці отримання.

Відповіді:


5

Я не думаю, що ти можеш це обійти.

З -tt, sshdпороджує псевдотермінал і робить підлеглий частиною stdin, stdout і stderr оболонки, яка виконує віддалену команду.

sshdчитає те, що надходить з його (одиночного) fd в головну частину псевдотерміналу і надсилає це (через один єдиний канал) sshклієнту. Не існує другого каналу для stderr, як і без нього -t.

Крім того, зауважте, що дисципліна термінальної лінії псевдотерміналу може (і буде за замовчуванням) змінювати вихід. Наприклад, LF буде перетворений в CRLF там, а не на локальному терміналі, тому ви можете відключити вихідну післяобробну обробку.

$ 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

На вхідній стороні відбудеться набагато більше речей (на зразок ^Cсимволу, який спричинить SIGINT, але також і інші сигнали, відлуння та всі керування, що беруть участь у редакторі ліній канонічного режиму ).

Ви, можливо, можете перенаправити stderr на FIFO і отримати його за допомогою секунди ssh:

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

Але найкраще було б уникати використання ІМО -t. Це справді призначене лише для інтерактивного використання з реального терміналу.

Замість того, щоб покладатися на передачу ^ C, щоб віддалений кінець з'єднання був закритим, ви можете використовувати обгортку, яка робить a poll()для виявлення вбитого sshабо закритого зв'язку.

Можливо щось на зразок (спрощено, ви хочете додати перевірку помилок):

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'

$p->mask(STDOUT, POLLIN)Вище може здатися дурним, але ідея полягає в тому, щоб чекати , поки подія похмілля HUP (на кінець читання труби на стандартний висновок повинен бути закритий). POLLHUP як запитувана маска ігнорується. POLLHUP має значення лише як подія, що повернулася (щоб сказати, що кінець написання закрито).

Ми маємо дати ненульове значення для маски події. Якщо ми використовуємо 0, perlнавіть не дзвонимо poll. Тому тут ми використовуємо POLLIN.

У Linux, що б ви не запитали, якщо труба зламана, опитування () повертає POLLERR.

У Solaris та FreeBSD, де труби двонаправлені, коли кінець зчитування труби (який також є записуючим кінцем) закритий, він повертається за допомогою POLLHUP (та POLLIN на FreeBSD, де потрібно запитувати POLLIN або ще $p->poll()ні. повернення).

Я не можу сказати, наскільки він портативний за межами цих трьох операційних систем.


Мені подобається ваша ідея, але я не можу змусити вашу обгортку виявити будь-які сигнали, якщо не встановлено '-tt'. Це працює:, parallel --tag -j1 'ssh -tt localhost perl/catch_wrap perl/catch_all_signals & sleep 1; killall -{} ssh' ::: {1..31}але видаліть '-tt' і тоді він не працює.
Оле Танге

@OleTange Призначення обгортки полягає в тому, щоб SIGHUP був відправлений на віддалене завдання, коли ssh вмирає (після затримки з ssh). Я не знаю, що робить ваш catch_all_signals, але все, що ви отримаєте, це те, що SIGHUP і лише після того, як ssh-з'єднання впаде (тож якщо він надрукує що-небудь на stdout, його ви не побачите).
Стефан Шазелас

catch_all_signals записує всі сигнали до файлу, і як уже згадувалося, він працює з '-tt', але виходить з ладу без. Іншими словами: він не отримує SIGHUP від ​​catch_wrap, коли ssh гине.
Оле Танге

Все ще працює лише -ttпісля редагування. Будь ласка, пам’ятайте, якщо ви не запускаєте команду паралельно, ssh успадкує термінал, з якого ви запускаєте її.
Оле Танге

@OleTange, я не можу відтворити, він працює для мене, ви протестували його з кодом, який я опублікував? Будь ласка, опублікуйте свої кудись catch_wrap та catch_all_signals, щоб я міг подивитися. З -t, я очікую, це не вийде.
Stéphane Chazelas

1

Щоб змусити його працювати на інших платформах, це стало остаточним рішенням. Він перевіряє, чи не відключився ssh-клієнт і, таким чином, батько став 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.