Czy mogę wysłać tekst do STDIN aktywnego procesu uruchomionego w sesji ekranowej?

70

Mam długo działający proces serwera w sesji ekranowej na moim serwerze Linux. Jest to trochę niestabilne (i niestety nie moje oprogramowanie, więc nie mogę tego naprawić!), Więc chcę skrypty co noc restartować proces, aby poprawić stabilność. Jedynym sposobem na sprawne zamknięcie jest przejście do procesu wyświetlania ekranu, przejście do okna, w którym działa, i wpisanie ciągu „stop” na konsoli sterowania.

Czy są jakieś sprytne przekierowania, które mogę zrobić, aby cronjob wysyłał komendę stop o określonej godzinie każdego dnia?


źródło

Odpowiedzi:

85

Ta odpowiedź nie rozwiązuje problemu, ale pozostała tutaj, ponieważ ponad 30 osób uznało ją za przydatną , w przeciwnym razie usunąłbym ją dawno temu.

Napisz do /proc/*pid of the program*/fd/0. fdPodkatalogu zawiera deskryptory wszystkich otwartych plików i deskryptor pliku 0jest standardowe wejście (1 i 2 jest stdout stderr).

Można go użyć do wyświetlania komunikatów na terminalu, w którym działa program, chociaż nie pozwala na pisanie do samego programu.

Przykład

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
źródło
3
@James Lawrie: spójrz na proc (5) i proc.txt .
Cristian Ciupitu,
2
+2 bez względu na to, ile myślisz, że wiesz, zawsze jest więcej do nauczenia :) zręczny.
troyengel
3
Pamiętaj jednak, że proc fd przekierowuje tylko do tego, co jest używane jako źródło standardowego wejścia. W twoim przykładzie, jeśli wpiszesz coś w terminalu 1, wydrukuje to ponownie (zostanie wysłane do kotów normalnie, a kot wydrukuje to), co spowoduje, że zobaczysz to dwukrotnie. Z drugiej strony, jeśli wyślesz coś do fd / 0, zostanie ono wysłane do konsoli, ale nie do kota, a zatem wyświetlone tylko raz. Ponieważ cat po prostu drukuje dane wejściowe ponownie w tym przykładzie, tak naprawdę nie można zobaczyć, czy dane wejściowe lub wyjściowe są drukowane, a zatem to błędne przekonanie. / fd / 0 wskazuje na konsolę / pts; zob ls -l /proc/7417/fd/0.
Kissaki,
4
przykład ze świata rzeczywistego: Uruchomiłem gphoto2 --get-all-files i prosi o potwierdzenie 100 razy. Kiedy echo „y”> / proc / PID / fd / 0, gphoto2 nie działa, jednak „y” jest drukowane w terminalu.
Thorsten Staerk
2
@ThorstenStaerk, wiem, dlatego dodałem tę notatkę. Piszesz tylko do pliku urządzenia odpowiadającego terminalowi, na którym działa gphoto2 (np. /dev/pts/19), yZnak nie dociera do samej aplikacji. Jest to podobne do tego, co dzieje się po użyciu polecenia write (1) . W każdym razie albo wypróbuj moją inną odpowiedź, albo narzędzie do automatyzacji grafiki, takie jak xdotool .
Cristian Ciupitu,
36

Rozwiązanie oparte na ekranie

Uruchom serwer w następujący sposób:

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

ekran uruchomi się w trybie odłączonym, więc jeśli chcesz zobaczyć, co się dzieje, uruchom:

# screen -r ServerFault

Kontroluj serwer w następujący sposób:

# 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

(ta odpowiedź opiera się na wysyłaniu wprowadzania tekstu na odłączony ekran ze strony rodzeństwa Unix i Linux )

Objaśnienie parametrów:

-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 [ciąg]

   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.

rozwiązanie oparte na tmux

Uruchom serwer w następujący sposób:

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

tmux uruchomi się w trybie odłączonym, więc jeśli chcesz zobaczyć, co się dzieje, uruchom:

# tmux attach-session -t ServerFault

Kontroluj serwer w następujący sposób:

# 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

Objaśnienie parametrów:

 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
źródło
4

Spróbuj tego, aby rozpocząć:

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

A to zabić:

# cd /path/to/wd
# echo "stop" > cmd
# rm cmd
krissi
źródło
3
Jest to dobre, ale może mieć tę wadę, że nie może wysyłać innych poleceń podczas działania programu. Jeśli program zatrzyma się, gdy uderzy w EOF na standardowym wejściu, to pierwszy echo "xxx" > cmdprogram zatrzyma się (ponieważ rura zostanie zamknięta). Chociaż niektóre programy są wystarczająco inteligentne, aby ponownie otworzyć ( rewind(3)) swoje standardowe wejście, gdy napotkają EOF.
Cristian Ciupitu,
2

Możliwe jest wysyłanie tekstu wejściowego do uruchomionego procesu bez uruchamiania screennarzędzia lub innego wymyślnego narzędzia. Można tego dokonać, wysyłając ten tekst wejściowy do „standardowego” pliku wejściowego procesu /proc/PID#/fd/0.

Jednak tekst wejściowy musi zostać przesłany w specjalny sposób, aby został odczytany przez proces. Wysłanie tekstu wejściowego zwykłą writemetodą pliku nie spowoduje, że proces odbierze tekst. Wynika to z tego, że spowoduje to dołączenie tylko do tego „pliku”, ale nie uruchomi procesu odczytu bajtów.

Aby uruchomić proces odczytu bajtów, konieczne jest wykonanie IOCTLoperacji typu TIOCSTIdla każdego wysłanego bajtu. Spowoduje to umieszczenie bajtu w standardowej kolejce wejściowej procesu.

Jest to omówione tutaj z kilkoma przykładami w C, Perl i Python:

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

-

Aby odpowiedzieć na pierwotne pytanie zadane prawie 9 lat temu, zadanie crona musiałoby uruchomić mały skrypt / program narzędziowy podobny do przykładów, które ludzie napisali dla tego innego pytania, które wysyłałyby ciąg „stop \ n” do tego procesu serwera w pytaniu, wysyłając każdy z 5 bajtów za pomocą IOCTLoperacji typu TIOCSTI.

Oczywiście będzie to działać tylko w systemach obsługujących TIOCSTI IOCTLtyp operacji (takich jak Linux) i tylko z rootkonta użytkownika, ponieważ te „pliki” poniżej /proc/są „własnością” root.

maratbn
źródło
1

Na wypadek, gdyby komukolwiek to pomogło:
miałem podobny problem, a ponieważ proces, którego używałem, nie był zbyt niski screenlub tmuxmusiałem zastosować inne podejście.

Podłączyłem się gdbdo tego xterm, w którym działał mój proces, i użyłem call write(5, "stop\n", 5)od gdbdo zapisu do głównego deskryptora pliku pty.
Dowiedziałem się, do którego deskryptora pliku wysłać dane, szukając /proc/<pid>/fdlinku, /dev/ptmxa następnie próbując i popełniając błąd między tymi dwiema opcjami (wysyłanie mojego ciągu do obu pasujących deskryptorów plików wydawało się nie powodować szkód).

EDYTOWAĆ

Okazało się, że xtermproces, do którego się przyłączyłem, został odrodzony z spawn-new-terminal() xtermakcją z klawiszem, a drugi ptmxotwarty deskryptor pliku był po prostu procesem ptmxnadrzędnym xterm, który nie został zamknięty.
Stąd wywołania prób i błędów wysłały dane wyjściowe do tego drugiego terminala.
Większość xtermprocesów nie ma dwóch ptmxdeskryptorów plików.

EDYCJA KOŃCOWA

To skutecznie wpisało ten ciąg do terminala, a tym samym wysłało go do uruchomionego pod nim procesu.

Uwaga: może być konieczne zezwolenie na dołączenie do uruchomionego procesu za pomocą czegoś takiego
sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"

jabłko
źródło
0

Ponieważ nie mogę skomentować najbardziej akceptowanej odpowiedzi Cristiana Ciupitu (z 2010 r.), Muszę umieścić to w osobnej odpowiedzi:

To pytanie zostało już rozwiązane w tym wątku: https://stackoverflow.com/questions/5374255/how-to-write-data-to-existing-processs-stdin-from-external-process

W skrócie:

Musisz rozpocząć proces od potoku dla stdin, który nie blokuje ani nie zamyka się, gdy bieżące wejście zostało zapisane. Można to zaimplementować za pomocą prostej nieskończonej pętli, która zostanie potokowana do danego procesu:

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

Mogę potwierdzić, że jest to inny sposób krissiego na otwarcie fajki, która nie działała w moim przypadku. Pokazane rozwiązanie działało zamiast tego.

Następnie możesz napisać do pliku ... / fd / 0 procesu, aby wysłać do niego instrukcje. Jedyną wadą jest to, że musisz zakończyć proces bash, który wykonuje pętlę nieskończoną po zamknięciu serwera.

użytkownik3471872
źródło