Bisakah saya mengirim beberapa teks ke STDIN dari proses aktif yang berjalan di sesi layar?

69

Saya memiliki proses server yang sudah berjalan lama di dalam sesi layar di server Linux saya. Agak tidak stabil (dan sayangnya bukan perangkat lunak saya jadi saya tidak bisa memperbaikinya!), Jadi saya ingin membuat skrip proses restart setiap malam untuk membantu stabilitas. Satu-satunya cara untuk membuatnya melakukan shutdown yang anggun adalah dengan masuk ke proses layar, beralih ke jendela yang sedang berjalan, dan masukkan string "berhenti" pada konsol kontrolnya.

Apakah ada contortions pengalihan cerdas yang bisa saya lakukan untuk membuat cronjob mengirim perintah berhenti itu pada waktu yang tetap setiap hari?


sumber

Jawaban:

85

Jawaban ini tidak menyelesaikan masalah, tetapi tetap ada di sini karena 30+ orang menganggapnya berguna , kalau tidak saya sudah lama menghapusnya.

Menulis untuk /proc/*pid of the program*/fd/0. The fdsubdirektori berisi deskriptor dari semua file yang dibuka dan file descriptor 0adalah standar input (1 adalah stdout dan 2 adalah stderr).

Anda dapat menggunakan ini untuk menampilkan pesan pada tty di mana program sedang berjalan, meskipun tidak memungkinkan Anda untuk menulis ke program itu sendiri.

Contoh

Terminal 1:

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

Terminal 2:

[ciupicri@hermes ~]$ pidof cat
7417
[ciupicri@hermes ~]$ echo "shows on the tty but bypasses cat" > /proc/7417/fd/0
Cristian Ciupitu
sumber
3
@James Lawrie: kemudian lihat proc (5) dan proc.txt .
Cristian Ciupitu
2
+2 tidak peduli seberapa banyak Anda pikir Anda tahu, selalu ada lebih banyak untuk dipelajari :) apik.
troyengel
3
Sadarilah bahwa prok fd hanya mengarahkan ke apa yang digunakan sebagai sumber stdin. Dalam contoh Anda, jika Anda memasukkan sesuatu di terminal 1 itu akan mencetaknya lagi (itu dikirim ke kucing stdin dan kucing mencetaknya), sehingga mengakibatkan Anda melihatnya dua kali. Di sisi lain, jika Anda mengirim sesuatu ke fd / 0 itu akan dikirim ke konsol tetapi tidak ke kucing, dan dengan demikian hanya ditampilkan sekali. Karena kucing hanya mencetak input lagi dengan contoh ini Anda tidak dapat benar-benar melihat apakah input atau output Anda sedang dicetak, dengan demikian kesalahpahaman ini. / fd / 0 poin ke konsol / Poin; lihat ls -l /proc/7417/fd/0.
Kissaki
5
contoh dunia nyata: Saya sudah mulai gphoto2 --get-semua-file dan meminta konfirmasi 100 kali. Ketika saya echo "y"> / proc / PID / fd / 0, gphoto2 tidak melanjutkan, namun, "y" dicetak di terminal.
Thorsten Staerk
2
@ ThorstenStaerk, saya tahu, itu sebabnya saya menambahkan catatan itu. Anda hanya menulis ke file perangkat yang sesuai dengan terminal tempat gphoto2 berjalan (mis. /dev/pts/19), yKarakter tidak mencapai aplikasi itu sendiri. Ini mirip dengan apa yang terjadi ketika Anda menggunakan perintah write (1) . Bagaimanapun, cobalah jawaban saya yang lain atau alat otomatisasi grafis seperti xdotool .
Cristian Ciupitu
36

Solusi berbasis layar

Mulai server seperti ini:

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

layar akan mulai dalam mode terpisah, jadi jika Anda ingin melihat apa yang terjadi, jalankan:

# screen -r ServerFault

Kontrol server seperti ini:

# 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

(jawaban ini didasarkan pada pengiriman input teks ke layar terpisah dari situs saudara Unix & Linux )

Penjelasan parameter:

-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.

barang [string]

   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.

solusi berbasis tmux

Mulai server seperti ini:

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

tmux akan mulai dalam mode terpisah, jadi jika Anda ingin melihat apa yang terjadi, jalankan:

# tmux attach-session -t ServerFault

Kontrol server seperti ini:

# 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

Penjelasan parameter:

 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.
Cristian Ciupitu
sumber
4

Coba ini untuk memulai:

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

Dan ini untuk membunuh:

# cd /path/to/wd
# echo "stop" > cmd
# rm cmd
Krissi
sumber
3
Ini bagus, tetapi mungkin memiliki kelemahan karena tidak dapat mengirim perintah lain saat program sedang berjalan. Jika program berhenti ketika menyentuh EOF pada stdin maka pada program pertama echo "xxx" > cmdakan berhenti (karena pipa akan ditutup). Meskipun beberapa program cukup pintar untuk membuka kembali ( rewind(3)) stdin mereka ketika mereka menghadapi EOF.
Cristian Ciupitu
2

Dimungkinkan untuk mengirim teks input ke proses yang berjalan tanpa menjalankan screenutilitas, atau utilitas mewah lainnya. Dan itu bisa dilakukan dengan mengirimkan teks input ini ke proses 'standard input "file" /proc/PID#/fd/0.

Namun, teks input perlu dikirim dengan cara khusus untuk dibaca oleh proses. Mengirim teks input melalui writemetode file biasa tidak akan menyebabkan proses untuk menerima teks. Ini karena hal itu hanya akan menambah "file" itu, tetapi tidak akan memicu proses untuk membaca byte.

Untuk memicu proses membaca byte, perlu dilakukan IOCTLoperasi tipe TIOCSTIuntuk setiap byte tunggal yang akan dikirim. Ini akan menempatkan byte ke dalam antrian masukan standar proses.

Ini dibahas di sini dengan beberapa contoh dalam C, Perl, dan Python:

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

-

Jadi untuk menjawab pertanyaan awal yang diajukan hampir 9 tahun yang lalu, tugas cron perlu menjalankan beberapa skrip / program utilitas kecil yang mirip dengan contoh yang ditulis orang untuk pertanyaan lain itu, yang akan mengirim string "stop \ n" ke proses server itu dalam pertanyaan, dengan mengirim masing-masing 5 byte melalui IOCTLoperasi ketik TIOCSTI.

Tentu saja ini hanya akan bekerja pada sistem yang mendukung TIOCSTI IOCTLjenis operasi (seperti Linux), dan hanya dari rootakun pengguna, karena "file" di bawah /proc/ini "dimiliki" oleh root.

maratbn
sumber
1

Seandainya itu membantu siapa pun:
Saya punya masalah yang sama, dan karena proses yang saya gunakan tidak di bawah screenatau tmux, saya harus mengambil pendekatan yang berbeda.

Aku melekat gdbpada xtermproses saya berjalan di, dan digunakan call write(5, "stop\n", 5)dari gdbmenulis ke deskriptor menguasai pty berkas.
Saya menemukan deskriptor file yang akan dikirimi data dengan mencari /proc/<pid>/fdtautan /dev/ptmxdan kemudian coba-coba antara dua opsi (mengirim string saya ke kedua deskriptor file yang cocok sepertinya tidak menimbulkan bahaya).

SUNTING

Ternyata xtermproses yang saya lampirkan terpelajar dengan spawn-new-terminal() xtermaksi dari keybinding, dan ptmxdeskriptor file kedua terbuka hanyalah proses ptmxinduk xtermyang belum ditutup.
Karenanya panggilan percobaan dan kesalahan telah mengirim output ke terminal lain itu.
Sebagian besar xtermproses tidak memiliki dua ptmxpenjelas file.

AKHIR EDIT

Ini secara efektif mengetik string itu ke terminal, dan karenanya mengirimkannya ke proses yang berjalan di bawahnya.

nb Anda mungkin perlu mengizinkan melampirkan ke proses yang berjalan dengan sesuatu seperti
sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"

apel
sumber
0

Karena saya tidak dapat mengomentari jawaban yang paling diterima dari Cristian Ciupitu (2010), saya harus memasukkan ini dalam jawaban yang terpisah:

Pertanyaan ini telah dipecahkan di utas ini: https://stackoverflow.com/questions/5374255/how-to-write-data-to-existing-processs-stdin-from-external-process

Pendeknya:

Anda harus memulai proses Anda dengan pipa untuk stdin yang tidak memblokir atau menutup ketika input saat ini ditulis. Ini dapat diimplementasikan oleh loop tak berujung sederhana yang akan disalurkan ke proses yang dimaksud:

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

Saya dapat mengkonfirmasi bahwa ini berbeda dari cara krissi untuk membuka pipa yang tidak berfungsi dalam kasus saya. Solusi yang ditampilkan malah bekerja.

Anda kemudian dapat menulis ke file ... / fd / 0 dari proses untuk mengirim instruksi kepadanya. Satu-satunya kelemahan adalah bahwa Anda perlu menghentikan proses bash juga yang menjalankan loop tak berujung setelah server dimatikan.

pengguna3471872
sumber