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 ใช้ขนาน 'SSH -tt' เพื่อเผยแพร่+Ctrl Cสิ่งนี้ทำให้สามารถฆ่างานที่กำลังรันอยู่ในระยะไกลได้ แต่ข้อมูลที่ส่งไปยัง STDERR ควรไปยัง STDERR ต่อไปเมื่อสิ้นสุดการรับ

คำตอบ:


5

ฉันไม่คิดว่าคุณจะไปไหนมาไหนได้

ด้วย-tt, sshdspawns หลอกขั้วและทำให้ส่วนทาส 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 แต่รวมถึงสัญญาณอื่น ๆ เสียงสะท้อนและการจัดการทั้งหมดที่เกี่ยวข้องในตัวแก้ไขบรรทัดโหมด canonical )

คุณอาจเปลี่ยนเส้นทาง stderr ไปยัง fifo และดึงมันกลับมาโดยใช้วินาทีssh:

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

แต่ IMO ที่ดีที่สุดคือหลีกเลี่ยงการใช้งานโดย-tสิ้นเชิง นั่นมีไว้สำหรับการใช้งานแบบโต้ตอบจากเทอร์มินัลจริงเท่านั้น

แทนที่จะอาศัยการส่งสัญญาณของ ^ C เพื่อให้การเชื่อมต่อสิ้นสุดระยะไกลถูกปิดคุณสามารถใช้ wrapper ที่ทำหน้าที่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 (สำหรับส่วนการอ่านของท่อ stdout ที่จะปิด) 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' แล้วมันไม่ทำงาน
Ole Tange

@OleTange จุดประสงค์ของ wrapper ใช้สำหรับ SIGHUP ที่จะส่งไปยังงานรีโมตเมื่อ ssh ตาย (เมื่อการเชื่อมต่อ ssh หยุดทำงาน) ฉันไม่ทราบว่า catch_all_signals ของคุณทำอะไร แต่สิ่งที่จะได้รับคือ SIGHUP และหลังจากการเชื่อมต่อ ssh ลดลง (ดังนั้นถ้ามันพิมพ์อะไรบน stdout คุณจะไม่เห็นมัน)
Stéphane Chazelas

catch_all_signals บันทึกสัญญาณทั้งหมดไปยังไฟล์และตามที่ระบุไว้ใช้งานได้กับ '-tt' แต่ล้มเหลวโดยไม่ต้อง กล่าวอีกนัยหนึ่ง: ไม่ได้รับ SIGHUP จาก catch_wrap เมื่อ ssh ตาย
Ole Tange

ยังคงใช้ได้เฉพาะ-ttหลังจากการแก้ไขของคุณ โปรดจำไว้ว่าถ้าคุณไม่ได้รันคำสั่งผ่านขนาน ssh จะรับค่าเทอร์มินัลที่คุณเรียกใช้จาก
Ole Tange

@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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.