stderr trên ssh -t


11

Điều này sẽ gửi đầu ra đến STDERR, nhưng không lan truyền Ctrl+ C(tức là Ctrl+ Csẽ giết sshnhưng không phải từ xa sleep):

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

Điều này lan truyền Ctrl+ C(tức là Ctrl+ Csẽ giết sshvà từ xa sleep), nhưng gửi STDERR tới STDOUT:

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

Làm cách nào tôi có thể buộc thứ hai gửi đầu ra STDERR đến STDERR, trong khi vẫn truyền Ctrl+ C?

Lý lịch

GNU Parallel sử dụng 'ssh -tt' để truyền bá Ctrl+ C. Điều này làm cho nó có thể giết chết các công việc chạy từ xa. Nhưng dữ liệu được gửi tới STDERR sẽ tiếp tục chuyển đến STDERR ở cuối nhận.

Câu trả lời:


5

Tôi không nghĩ bạn có thể vượt qua điều đó.

Với -tt, sshdsinh ra một thiết bị đầu cuối giả và làm cho phần nô lệ trở thành stdin, stdout và stderr của shell thực thi lệnh từ xa.

sshdđọc những gì đến từ fd (đơn) của nó đến phần chính của thiết bị đầu cuối giả và gửi nó (thông qua một kênh) cho sshkhách hàng. Không có kênh thứ hai cho stderr vì không có -t.

Ngoài ra, lưu ý rằng kỷ luật dòng thiết bị đầu cuối của thiết bị đầu cuối giả có thể (và theo mặc định) sẽ thay đổi đầu ra. Chẳng hạn, LF sẽ được chuyển đổi thành CRLF ở đó chứ không phải trên thiết bị đầu cuối cục bộ, vì vậy bạn có thể muốn tắt xử lý hậu kỳ đầu ra.

$ 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

Nhiều thứ nữa sẽ xảy ra ở phía đầu vào (như ^Cký tự sẽ gây ra SIGINT, nhưng cũng có các tín hiệu khác, tiếng vang và tất cả các xử lý liên quan đến trình chỉnh sửa dòng chế độ chính tắc ).

Bạn có thể có thể chuyển hướng stderr đến fifo và truy xuất nó bằng một giây ssh:

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

Nhưng IMO tốt nhất sẽ là tránh sử dụng -thoàn toàn. Điều đó thực sự chỉ có nghĩa là để sử dụng tương tác từ một thiết bị đầu cuối thực sự.

Thay vì dựa vào việc truyền ^ C để cho kết thúc từ xa kết nối bị đóng, bạn có thể sử dụng trình bao bọc poll()để phát hiện sshkết nối bị chết hoặc đóng.

Có thể một cái gì đó như (đơn giản hóa, bạn sẽ muốn thêm một số kiểm tra lỗi):

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'

Những điều $p->mask(STDOUT, POLLIN)trên có vẻ ngớ ngẩn, nhưng ý tưởng là chờ đợi một sự kiện hang-hup (cho đầu đọc của ống trên thiết bị xuất chuẩn bị đóng). POLLHUP như một mặt nạ được yêu cầu bị bỏ qua. POLLHUP chỉ có ý nghĩa như một sự kiện được trả về (để nói rằng kết thúc bằng văn bản đã bị đóng).

Chúng ta phải đưa ra một giá trị khác không cho mặt nạ sự kiện. Nếu chúng ta sử dụng 0, perlthậm chí không gọi poll. Vì vậy, ở đây chúng tôi sử dụng POLLIN.

Trên Linux, bất cứ điều gì bạn yêu cầu, nếu đường ống bị hỏng, poll () sẽ trả về POLLERR.

Trên Solaris và FreeBSD, nơi các đường ống là hai chiều, khi đầu đọc của ống (cũng là đầu viết ở đó) được đóng lại, nó sẽ trả về với POLLHUP (và POLLIN trên FreeBSD, trong đó bạn phải yêu cầu POLLIN hoặc nếu $p->poll()không thì không yêu cầu POLLIN trở về).

Tôi không thể nói nó di động như thế nào ngoài ba hệ điều hành đó.


Tôi thích ý tưởng của bạn, nhưng tôi không thể làm cho trình bao bọc của bạn phát hiện bất kỳ tín hiệu nào trừ khi '-tt' được đặt. Điều này hoạt động : parallel --tag -j1 'ssh -tt localhost perl/catch_wrap perl/catch_all_signals & sleep 1; killall -{} ssh' ::: {1..31}, nhưng loại bỏ '-tt' và sau đó nó không hoạt động.
Ole Tange

@OleTange Mục đích của trình bao bọc là để SIGHUP được gửi đến công việc từ xa khi ssh chết (khi kết nối ssh bị treo). Tôi không biết Catch_all_signals của bạn làm gì, nhưng tất cả những gì bạn nhận được là SIGHUP và chỉ sau khi kết nối ssh bị rớt (vì vậy nếu nó in bất cứ thứ gì trên thiết bị xuất chuẩn, bạn sẽ không thấy nó).
Stéphane Chazelas

Catch_all_signals ghi lại tất cả các tín hiệu vào một tệp và như đã đề cập, nó hoạt động với '-tt', nhưng không thành công. Nói cách khác: Nó không nhận được SIGHUP từ Catch_wrap khi ssh chết.
Ole Tange

Vẫn chỉ hoạt động với -ttsau khi chỉnh sửa của bạn. Hãy nhớ nếu bạn không chạy lệnh thông qua song song, ssh sẽ kế thừa thiết bị đầu cuối mà bạn chạy nó từ đó.
Ole Tange

@OleTange, tôi không thể sao chép, nó hoạt động với tôi, bạn đã kiểm tra nó với mã tôi đã đăng chưa? Vui lòng gửi Catch_wrap và Catch_all_signals của bạn ở đâu đó để tôi có thể xem qua. Với -t, tôi hy vọng nó không hoạt động.
Stéphane Chazelas

1

Để làm cho nó hoạt động trên các nền tảng khác, đây đã trở thành giải pháp cuối cùng. Nó kiểm tra nếu máy khách ssh bị ngắt kết nối và do đó cha mẹ trở thành 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.