ฉันสามารถส่งข้อความไปยัง STDIN ของกระบวนการที่ทำงานอยู่ที่ทำงานอยู่ในเซสชันหน้าจอได้หรือไม่


70

ฉันมีกระบวนการเซิร์ฟเวอร์ที่ใช้เวลานานในเซสชันหน้าจอบนเซิร์ฟเวอร์ Linux ของฉัน มันค่อนข้างไม่เสถียร (และไม่ใช่ซอฟต์แวร์ของฉันดังนั้นฉันจึงไม่สามารถแก้ไขได้!) ดังนั้นฉันต้องการสคริปต์เริ่มกระบวนการทุกคืนเพื่อช่วยให้มีเสถียรภาพ วิธีเดียวที่จะทำให้การปิดระบบอย่างงดงามคือไปที่กระบวนการหน้าจอสลับไปที่หน้าต่างที่กำลังทำงานอยู่และป้อนสตริง "หยุด" บนคอนโซลควบคุม

มีการเปลี่ยนเส้นทางสมาร์ทที่ฉันสามารถทำได้เพื่อให้ cronjob ส่งคำสั่งหยุดที่กำหนดเวลาทุกวัน?

คำตอบ:


85

คำตอบนี้ไม่สามารถแก้ปัญหาได้ แต่เหลือไว้ที่นี่เพราะมีคนกว่า 30 คนที่พบว่ามีประโยชน์มิฉะนั้นฉันจะลบมันไปนานแล้ว

/proc/*pid of the program*/fd/0เขียน fdไดเรกทอรีย่อยมีอธิบายของไฟล์ทั้งหมดที่เปิดและไฟล์อธิบาย0คือเข้ามาตรฐาน (1 stdout และ 2 เป็น stderr)

คุณสามารถใช้สิ่งนี้เพื่อส่งข้อความบน tty ที่โปรแกรมกำลังทำงานแม้ว่ามันจะไม่อนุญาตให้คุณเขียนโปรแกรมเอง

ตัวอย่าง

อาคาร 1:

[ciupicri@hermes ~]$ cat
shows on the tty but bypasses cat

อาคาร 2:

[ciupicri@hermes ~]$ pidof cat
7417
[ciupicri@hermes ~]$ echo "shows on the tty but bypasses cat" > /proc/7417/fd/0

3
@James ลอว์: แล้วมีลักษณะที่proc (5)และproc.txt
Cristian Ciupitu

2
+2 ไม่ว่าคุณจะคิดว่าคุณรู้มากแค่ไหนมันก็มีอะไรให้เรียนรู้มากขึ้น :) เนียน
troyengel

3
ระวังแม้ว่า proc fd จะเปลี่ยนเส้นทางไปยังสิ่งที่ใช้เป็นแหล่งของ stdin เท่านั้น ในตัวอย่างของคุณถ้าคุณป้อนบางอย่างในเทอร์มินัล 1 มันจะพิมพ์ออกมาอีกครั้ง (มันจะถูกส่งไปยังแมว stdin และพิมพ์แมว) จึงทำให้คุณเห็นมันสองครั้ง ในทางกลับกันถ้าคุณส่งบางสิ่งไปยัง fd / 0 มันจะถูกส่งไปยังคอนโซล แต่ไม่ต้อง cat และแสดงเพียงครั้งเดียวเท่านั้น เนื่องจาก cat เพียงพิมพ์อินพุตอีกครั้งด้วยตัวอย่างนี้คุณจึงไม่สามารถดูได้ว่าอินพุตหรือเอาต์พุตกำลังถูกพิมพ์หรือไม่ดังนั้นความเข้าใจผิดนี้ / fd / 0 ชี้ไปที่คอนโซล / pts; เห็นls -l /proc/7417/fd/0ไหม
Kissaki

4
ตัวอย่างในโลกแห่งความจริง: ฉันเริ่ม gphoto2 - get-all-files และมันขอการยืนยัน 100 ครั้ง เมื่อฉัน echo "y"> / proc / PID / fd / 0, gphoto2 ไม่ดำเนินการต่ออย่างไรก็ตาม "y" จะถูกพิมพ์ในเครื่อง
Thorsten Staerk

2
@ThorstenStaerk ฉันรู้ว่านั่นเป็นเหตุผลที่ฉันเพิ่มบันทึกย่อ คุณกำลังเขียนเฉพาะไฟล์ของอุปกรณ์ที่สอดคล้องกับเทอร์มินัลที่ gphoto2 ทำงาน (เช่น/dev/pts/19) yอักขระนั้นไม่สามารถเข้าถึงแอปพลิเคชันได้ คล้ายกับสิ่งที่เกิดขึ้นเมื่อคุณใช้คำสั่งwrite (1) อย่างไรก็ตามลองคำตอบอื่น ๆ ของฉันหรือเครื่องมืออัตโนมัติแบบกราฟิกเช่นxdotool
Cristian Ciupitu

36

วิธีการแก้ปัญหาหน้าจอ

เริ่มเซิร์ฟเวอร์ดังนี้:

# screen -d -m -S ServerFault tr a-z A-Z # replace with your server

หน้าจอจะเริ่มในโหมดเดี่ยวดังนั้นหากคุณต้องการดูว่าเกิดอะไรขึ้นให้เรียกใช้:

# screen -r ServerFault

ควบคุมเซิร์ฟเวอร์ดังนี้:

# screen -S ServerFault -p 0 -X stuff "stop^M"
# screen -S ServerFault -p 0 -X stuff "start^M"
# screen -S ServerFault -p 0 -X stuff "^D" # send EOF

(คำตอบนี้ขึ้นอยู่กับการส่งข้อความไปยังหน้าจอเดี่ยวจากเว็บไซต์Unix & Linux )

คำอธิบายของพารามิเตอร์:

-d -m
   Start screen in "detached" mode. This creates a new session but doesn't
   attach to it.  This is useful for system startup scripts.
-S sessionname
   When creating a new session, this option can be used to specify a meaningful
   name for the session.
-r [pid.tty.host]
-r sessionowner/[pid.tty.host]
   resumes a detached screen session.
-p number_or_name|-|=|+
   Preselect a window. This is useful when you want to reattach to a specific
   window or you want to send a command via the "-X" option to a specific
   window.
-X
   Send the specified command to a running screen session e.g. stuff.

สิ่งที่ [สตริง]

   Stuff the string string in the input  buffer of the current window.
   This is like the "paste" command but with much less overhead.  Without
   a parameter, screen will prompt for a string to stuff.

โซลูชันที่ใช้ tmux

เริ่มเซิร์ฟเวอร์ดังนี้:

# tmux new-session -d -s ServerFault 'tr a-z A-Z' # replace with your server

tmuxจะเริ่มในโหมดเดี่ยวดังนั้นหากคุณต้องการดูว่าเกิดอะไรขึ้นให้เรียกใช้:

# tmux attach-session -t ServerFault

ควบคุมเซิร์ฟเวอร์ดังนี้:

# tmux send-keys -t ServerFault -l stop
# tmux send-keys -t ServerFault Enter
# tmux send-keys -t ServerFault -l start
# tmux send-keys -t ServerFault Enter
# tmux send-keys -t ServerFault C-d # send EOF

คำอธิบายของพารามิเตอร์:

 new-session [-AdDP] [-c start-directory] [-F format] [-n window-name] [-s
         session-name] [-t target-session] [-x width] [-y height]
         [shell-command]
         Create a new session with name session-name.

         The new session is attached to the current terminal unless -d is
         given.  window-name and shell-command are the name of and shell
         command to execute in the initial window.  If -d is used, -x and
         -y specify the size of the initial window (80 by 24 if not
         given).

 send-keys [-lR] [-t target-pane] key ...
               (alias: send)
         Send a key or keys to a window.  Each argument key is the name of
         the key (such as `C-a' or `npage' ) to send; if the string is not
         recognised as a key, it is sent as a series of characters.  The
         -l flag disables key name lookup and sends the keys literally.

4

ลองนี่เพื่อเริ่ม:

# screen
# cd /path/to/wd
# mkfifo cmd
# my_cmd <cmd
C-A d

และนี่เพื่อฆ่า:

# cd /path/to/wd
# echo "stop" > cmd
# rm cmd

3
นี่เป็นสิ่งที่ดี แต่อาจมีข้อเสียเปรียบที่ไม่สามารถส่งคำสั่งอื่น ๆ ในขณะที่โปรแกรมกำลังทำงานอยู่ หากโปรแกรมหยุดทำงานเมื่อพบ EOF ใน stdin ดังนั้นในวันแรกecho "xxx" > cmdโปรแกรมจะหยุดทำงาน (เนื่องจากไปป์จะปิด) แม้ว่าบางโปรแกรมจะฉลาดพอที่จะเปิด ( rewind(3)) stdin อีกครั้งเมื่อพบ EOF
Cristian Ciupitu

2

เป็นไปได้ที่จะส่งข้อความอินพุตไปยังกระบวนการที่กำลังรันอยู่โดยไม่ต้องรันscreenยูทิลิตี้ และก็สามารถทำได้โดยการส่งป้อนข้อความนี้ไปยังกระบวนการเข้ามาตรฐาน /proc/PID#/fd/0'แฟ้ม'

อย่างไรก็ตามต้องป้อนข้อความอินพุตด้วยวิธีพิเศษเพื่อให้กระบวนการอ่าน การส่งข้อความอินพุตด้วยwriteวิธีไฟล์ปกติจะไม่ทำให้กระบวนการรับข้อความ นี่เป็นเพราะการทำเช่นนั้นจะต่อท้าย "ไฟล์" นั้น แต่จะไม่ทริกเกอร์กระบวนการอ่านไบต์

เพื่อทริกเกอร์กระบวนการอ่านไบต์จำเป็นต้องทำการIOCTLดำเนินการของชนิดTIOCSTIสำหรับทุกไบต์เดียวที่จะส่ง สิ่งนี้จะวางไบต์ลงในคิวอินพุตมาตรฐานของกระบวนการ

นี่คือตัวอย่างบางส่วนใน C, Perl และ Python:

https://unix.stackexchange.com/questions/48103/construct-a-command-by-putting-a-string-into-a-tty/48221

-

ดังนั้นเพื่อตอบคำถามเดิมที่ถามมาเกือบ 9 ปีที่แล้วงาน cron จะต้องเรียกใช้สคริปต์ / โปรแกรมอรรถประโยชน์เล็ก ๆ คล้ายกับตัวอย่างที่ผู้คนเขียนสำหรับคำถามอื่นซึ่งจะส่งสตริง "stop \ n" ไปยังกระบวนการเซิร์ฟเวอร์นั้น ในคำถามโดยการส่งแต่ละ 5 ไบต์ผ่านการดำเนินงานของประเภทIOCTLTIOCSTI

หลักสูตรนี้จะทำงานบนระบบที่สนับสนุนTIOCSTI IOCTLประเภทการดำเนินงาน (เช่น Linux) และมีเพียงจากrootบัญชีผู้ใช้เป็นเหล่านี้ "ไฟล์" ภายใต้การ/proc/เป็น "เจ้าของ" rootโดย


1

ในกรณีที่ช่วยทุกคน:
ฉันมีปัญหาที่คล้ายกันและเป็นกระบวนการที่ฉันใช้ไม่ได้อยู่ภายใต้screenหรือtmuxฉันต้องใช้วิธีการที่แตกต่างกัน

ผมติดอยู่gdbกับxtermว่ากระบวนการของฉันคือการทำงานในและใช้call write(5, "stop\n", 5)จากgdbการเขียนไปยังอธิบายหลัก Pty ไฟล์
ฉันพบไฟล์ descriptor ตัวใดที่จะส่งข้อมูลไปให้โดยดู/proc/<pid>/fdลิงค์ไปยัง/dev/ptmxแล้วลองผิดลองถูกระหว่างตัวเลือกทั้งสอง (ส่งสตริงของฉันไปที่ทั้งสองอธิบายไฟล์ที่ตรงกันไม่ทำให้เกิดอันตราย)

แก้ไข

มันกลับกลายเป็นว่าxtermกระบวนการที่ฉันแนบมานั้นเกิดspawn-new-terminal() xtermจากการกระทำจากการผูกกุญแจและตัวptmxอธิบายไฟล์ที่สองที่เปิดอยู่นั้นเป็นเพียงกระบวนการptmxหลักxtermที่ไม่ได้ถูกปิด
ดังนั้นการทดลองและการเรียกข้อผิดพลาดจึงส่งเอาต์พุตไปยังเทอร์มินัลอื่นนั้น กระบวนการ
ส่วนใหญ่xtermไม่มีตัวptmxอธิบายไฟล์สองตัว

สิ้นสุดการแก้ไข

สิ่งนี้จะพิมพ์สตริงนั้นลงในเทอร์มินัลอย่างมีประสิทธิภาพและด้วยเหตุนี้จึงส่งมันไปยังกระบวนการที่กำลังทำงานอยู่

คุณอาจจำเป็นต้องอนุญาตให้มีการแนบไปยังกระบวนการทำงานกับสิ่งที่ชอบ
sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"


0

เนื่องจากฉันไม่สามารถแสดงความคิดเห็นคำตอบที่ได้รับการยอมรับมากที่สุดของ Cristian Ciupitu (ของปี 2010) ฉันต้องใส่คำตอบนี้แยกต่างหาก:

คำถามนี้ได้รับการแก้ไขแล้วในหัวข้อนี้: https://stackoverflow.com/questions/5374255/how-to-write-data-to-existing-processs-stdin-from-external-process

ในระยะสั้น:

คุณต้องเริ่มต้นกระบวนการของคุณด้วยไพพ์สำหรับ stdin ซึ่งไม่ได้บล็อกหรือปิดเมื่ออินพุตปัจจุบันถูกเขียนผ่าน สิ่งนี้สามารถนำมาใช้โดยการวนรอบอย่างไม่สิ้นสุดซึ่งจะถูกส่งไปยังกระบวนการที่เป็นปัญหา:

$ (while [ 1 ]; do sleep 1; done) | yourProgramToStart

ฉันสามารถยืนยันได้ว่านี่เป็นวิธีที่แตกต่างจากวิธีของ Krissi ในการเปิดไปป์ที่ไม่ทำงานในกรณีของฉัน โซลูชันที่แสดงทำงานได้แทน

จากนั้นคุณสามารถเขียนไปยังไฟล์ ... / fd / 0 ของกระบวนการเพื่อส่งคำแนะนำ ข้อเสียเปรียบเพียงอย่างเดียวคือคุณต้องยุติกระบวนการทุบตีเช่นกันซึ่งกำลังดำเนินการวนรอบไม่สิ้นสุดหลังจากเซิร์ฟเวอร์ปิดตัวลง

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