รับ ssh เพื่อส่งต่อสัญญาณ


22

ฉันต้องการที่จะสามารถส่งสัญญาณ (SIGINT เป็นสิ่งสำคัญที่สุด) ผ่าน ssh

คำสั่งนี้:

ssh server "sleep 1000;echo f" > foo

จะเริ่ม sleep บนเซิร์ฟเวอร์และหลังจาก 1,000 วินาทีมันจะใส่ 'f \ n' ในไฟล์ foo บนเครื่องของฉัน ถ้าฉันกด CTRL-C (เช่นส่ง SIGINT ไปยัง ssh) มันจะฆ่า ssh แต่จะไม่ฆ่า sleep บนเซิร์ฟเวอร์ระยะไกล ฉันต้องการที่จะฆ่า sleep บนเซิร์ฟเวอร์ระยะไกล

ดังนั้นฉันจึงพยายาม:

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

แต่ถ้า stdin ไม่ใช่เทอร์มินัลฉันได้รับข้อผิดพลาดนี้:

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

จากนั้น SIGINT ยังไม่ถูกส่งต่อ

ดังนั้นฉันจึงพยายาม:

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

แต่ผลลัพธ์ใน foo ไม่ใช่ 'f \ n' แต่แทน 'f \ r \ n' ซึ่งเป็นหายนะในสถานการณ์ของฉัน (เนื่องจากผลลัพธ์ของฉันคือข้อมูลไบนารี)

ในข้างต้นฉันใช้ "sleep 1000; echo f" แต่ในความเป็นจริงที่ผู้ใช้จัดหาดังนั้นมันสามารถมีอะไรก็ได้ อย่างไรก็ตามหากเราสามารถทำให้มันใช้งานได้สำหรับ "sleep 1000; echo f" เรามักจะทำให้มันทำงานได้ในสถานการณ์จริงทั้งหมด

ฉันไม่สนใจที่จะเอาเครื่องหลอกมาใช้ที่ปลายอีกด้าน แต่ฉันไม่สามารถหาวิธีอื่นในการรับ ssh เพื่อส่งต่อ SIGINT ของฉัน

มีวิธีอื่นอีกไหม?

แก้ไข:

ผู้ใช้สามารถให้คำสั่งที่อ่านข้อมูลไบนารีจาก stdin เช่น:

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

ผู้ใช้สามารถให้คำสั่งที่มี cpu เข้มข้นเช่น:

ssh server "timeout 1000 burnP6"

Edit2:

รุ่นที่ดูเหมือนจะใช้งานได้สำหรับฉันคือ:

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

ขอบคุณ digital_infinity ที่ชี้ให้ฉันไปในทิศทางที่ถูกต้อง


1
ฉันคิดว่าการใช้งานจริงของคุณsshจะต้องซับซ้อนกว่าสิ่งที่คุณแสดงเป็นตัวอย่างเพราะคุณสามารถรับพฤติกรรมที่คุณต้องการได้ด้วยการจัดเรียงใหม่อย่างง่าย: sleep 1000 && ssh server "echo f" > foo(ต้อง&&ไม่ใช่ไม่ใช่การ;ฆ่านั้นsleepทำให้sshคำสั่งไม่ทำงาน) หากฉัน ถูกต้องโปรดทำให้ตัวอย่างของคุณเป็นตัวแทนของการใช้งานจริงของคุณมากขึ้นเพื่อให้ได้คำตอบที่ดีขึ้น
Warren Young

ถูกต้อง: sleep และ echo เป็นสคริปต์ที่ผู้ใช้จัดหาจริง ๆ แล้วไม่ใช่ sleep และ echo ดังนั้นเราไม่ทราบว่าพวกเขาทำอะไรและควรถือว่าแย่ที่สุด
Ole Tange

Sooo ... คุณจะได้คำสั่งตัวอย่างที่ดีขึ้นใช่มั้ย ที่ซึ่งการจัดเรียงแบบง่ายไม่สามารถแก้ไขปัญหาได้ คุณถามคำถามที่ดีและฉันต้องการเห็นคำตอบนั้น แต่คุณมีโอกาสน้อยที่จะได้รับคำตอบถ้า "อย่าทำอย่างนั้น" เป็นคำตอบที่สมเหตุสมผล
Warren Young

โอ้โดยวิธีการ ... อย่าทำอย่างนั้น;)
ทิม

ดังที่กล่าวไว้ในคำถาม: "" "ในข้างต้นฉันใช้" sleep 1000; echo f "แต่ในความเป็นจริงที่จัดหาโดยผู้ใช้ดังนั้นมันสามารถมีอะไร" ""
Ole Tange

คำตอบ:


10

คำตอบสั้น ๆ :

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

และหยุดโปรแกรมด้วย CTRL + N

คำอธิบายยาว:

  1. คุณต้องใช้sttyตัวเลือกintrในการเปลี่ยนเซิร์ฟเวอร์หรืออักขระขัดจังหวะท้องถิ่นเพื่อไม่ให้เกิดการขัดแย้งกัน ในคำสั่งด้านบนฉันได้เปลี่ยนอักขระขัดจังหวะเซิร์ฟเวอร์เป็น CTRL + N คุณสามารถเปลี่ยนอักขระขัดจังหวะโลคัลของคุณและปล่อยให้เซิร์ฟเวอร์ไม่มีการเปลี่ยนแปลง
  2. หากคุณไม่ต้องการให้ตัวละครขัดจังหวะที่จะอยู่ในการส่งออกของคุณ (และตัวละครที่ควบคุมอื่น ๆ ) stty -echoctlการใช้งาน
  3. คุณต้องมั่นใจว่าตัวควบคุมถูกเปิดบนเซิร์ฟเวอร์ทุบตีเรียกใช้โดย sshd หากคุณไม่สามารถจบลงด้วยกระบวนการที่ยังค้างอยู่หลังจากออกจากระบบstty isig
  4. คุณจับSIGINTสัญญาณโดยtrap '/bin/true' SIGINTใช้คำสั่งเปล่า หากไม่มีกับดักคุณจะไม่มี stdout ใด ๆ หลังจากสัญญาณ SIGINT ที่ปลายของคุณ

ดูเหมือนว่าจะไม่ได้รับการจัดการอย่างดีกับการป้อนข้อมูลแบบไบนารี: seq 1000 | gzip | ssh -t -t server "stty isig intr ^ N -echoctl; trap '/ bin / true' SIGINT; sleep 1; zcat | bzip2" | bzcat> foo;
Ole Tange

คุณไม่สามารถขัดจังหวะโดยคอนโซลหากคุณตั้งค่าอินพุตมาตรฐานสำหรับ ssh สำหรับสิ่งอื่นแล้วคอนโซล ในกรณีนี้คุณต้องขัดจังหวะโดยสตรีม stdin
digital_infinity

ฉันพบว่ามันseq 1000 | gzip | ssh -tt dell-test "zcat|bzip2" | bzcat > fooใช้งานไม่ได้เช่นกัน -ttให้มีการทำงานคำสั่งนี้เราจำเป็นต้องลบ ดังนั้นการจัดสรรเทอร์มินัลหลอกอาจใช้อินพุตจาก stdin
digital_infinity

ฉันลอง uuencode ในรุ่นนี้มันใช้งานไม่ได้: cat foo.gz | perl -ne 'print pack ("u", $ _)' | ssh -t -t เซิร์ฟเวอร์ 'perl -ne "พิมพ์ unpack (\" u \ ", \ $ _)" | zcat | gzip | perl -ne "print pack (\" u \ ", \ $ _)" '| perl -ne 'print unpack ("u", $ _)'> foo2.gz
Ole Tange

1
ฉันมีสิ่งนี้: (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. แม้ว่าตัวละครขัดจังหวะการทำงานไม่ได้ - เราต้องมีวิธีการส่งข้อมูลบางอย่างสำหรับตัวละครขัดจังหวะในขณะ stdin เป็นที่วุ่นวาย
digital_infinity

3

ฉันลองวิธีแก้ปัญหาทั้งหมดและนี่คือสิ่งที่ดีที่สุด

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


ไม่ได้หยุดsleepกระบวนการในกรณีที่การเชื่อมต่อขาดหายไป
blueyed

เมื่อการเชื่อมต่อขาดหาย stdin จะไม่ทำการปลดบล็อคแมวซึ่งจะทำการฆ่ากลุ่มกระบวนการ ไม่ว่าสัญญาณ INTerrupt นั้นดีพอสำหรับงานของคุณหรือไม่
Eric Woodruff

มันควรจะดีพอสำหรับsleepมันใช่ไหม? ฉันได้เพิ่มบางอย่างdate >> /tmp/killedหลังจากcatนั้น แต่มันไม่ได้ถูกเรียก มีการหมดเวลาบ้างไหม? ฉันใช้ Zsh ตามปกติ แต่ยังทดสอบด้วย bash เป็นเชลล์ล็อกอินระยะไกล
blueyed

ฉันคิดว่าคุณอยู่ในความเมตตาของการหมดเวลาการเชื่อมต่อ
Eric Woodruff

มีการตั้งค่าเพื่อควบคุมสิ่งนี้หรือไม่? สำหรับฝั่งไคลเอ็นต์-o ServerAliveInterval=3 -o ServerAliveCountMax=2อนุญาตให้ตรวจจับได้อย่างรวดเร็ว แต่มีบางอย่างสำหรับฝั่งเซิร์ฟเวอร์หรือไม่
blueyed

2

ฉันคิดว่าคุณสามารถหา PID ของกระบวนการที่คุณใช้บนเซิร์ฟเวอร์และส่งสัญญาณโดยใช้sshคำสั่งอื่น(เช่นนี้ssh server "kill -2 PID":)

ฉันใช้วิธีนี้ในการส่งสัญญาณการกำหนดค่าใหม่ไปยังแอปพลิเคชันที่ทำงานบนเครื่องอื่น (แอปพลิเคชันของฉันจับ SIGUSR1 และอ่านไฟล์ปรับแต่ง) ในกรณีที่การค้นพบของฉัน PID เป็นเรื่องง่ายเพราะผมมีชื่อกระบวนการที่ไม่ซ้ำกันและผมสามารถหา PID โดยการส่งคำขอผ่านทางpsssh


ฉันไม่เห็นวิธีพิสูจน์หัวข้อย่อยในการค้นหา PID ของโปรแกรมที่กำลังทำงานบนระยะไกล จำไว้ว่าให้ไว้โดยผู้ใช้ อาจเป็น: เซิร์ฟเวอร์ ssh 'exec $ (echo fyrrc 1,000 | / usr / games / rot13)'
Ole Tange

1

โซลูชันนี้พัฒนาเป็น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)

0

----- command.sh

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

----- บนสถานีท้องถิ่น

sleep 864000 | ssh -T server command.sh > foo

Hi! ฉันคิดว่าคุณสามารถทำให้คำตอบของคุณมีประโยชน์มากขึ้นโดยอธิบายรายละเอียดให้มากที่สุดเท่าที่คุณเห็นว่าเกี่ยวข้อง อย่าลังเลที่จะแก้ไขเพื่อแทรกข้อมูลใหม่
dhag
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.