Могу ли я отправить какой-либо текст на STDIN активного процесса, запущенного в сеансе экрана?

69

У меня есть длительный процесс сервера внутри сеанса экрана на моем сервере Linux. Это немного нестабильно (и, к сожалению, не мое программное обеспечение, поэтому я не могу это исправить!), Поэтому я хочу написать сценарий перезапуска процесса каждую ночь, чтобы помочь стабильности. Единственный способ заставить его выполнить корректное завершение работы - перейти к экранному процессу, переключиться в окно, в котором он запущен, и ввести строку «стоп» на его консоли управления.

Есть ли какие-нибудь умные перенаправления, которые я могу сделать, чтобы cronjob отправлял эту команду остановки в определенное время каждый день?


источник

Ответы:

85

Этот ответ не решает проблему, но он оставлен здесь, потому что более 30 человек нашли его полезным , в противном случае я бы удалил его давным-давно.

Написать в /proc/*pid of the program*/fd/0. fdПодкаталог содержит дескрипторы всех открытых файлов и файловый дескриптор 0является стандартный ввод (1 стандартный вывод и 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 Lawrie: взгляните на proc (5) и proc.txt .
Кристиан Чиупиту
2
+2 неважно, сколько ты думаешь, что знаешь, всегда есть чему поучиться :) ловко.
Троенгель
3
Имейте в виду, что proc fd перенаправляет только на то, что используется в качестве источника stdin. В вашем примере, если вы введете что-то в терминал 1, оно будет распечатано снова (это отправлено на стандартный вывод cat, а cat напечатает это), в результате чего вы увидите это дважды. С другой стороны, если вы отправите что-то в fd / 0, оно будет отправлено на консоль, но не на cat, и, следовательно, будет отображаться только один раз. Поскольку cat просто печатает ввод снова в этом примере, вы не можете видеть, печатается ли ваш ввод или вывод, таким образом, это заблуждение. / fd / 0 указывает на консоль / pts; см ls -l /proc/7417/fd/0.
Киссаки
5
пример из реальной жизни: я запустил gphoto2 --get-all-files, и он запрашивает подтверждение 100 раз. Когда я повторяю «y»> / proc / PID / fd / 0, gphoto2 не продолжается, однако «y» печатается в терминале.
Торстен Стэрк
2
@ ThorstenStaerk, я знаю, поэтому я добавил эту заметку. Вы пишете только в файл устройства, соответствующий терминалу, на котором работает gphoto2 (например /dev/pts/19), yперсонаж не достигает самого приложения. Это похоже на то, что происходит, когда вы используете команду write (1) . В любом случае, попробуйте другой мой ответ или графический инструмент автоматизации, такой как xdotool .
Кристиан Чиупиту
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 на стандартном вводе, то сначала echo "xxx" > cmdпрограмма останавливается (потому что канал будет закрыт). Хотя некоторые программы достаточно умны, чтобы повторно открывать ( rewind(3)) их стандартный ввод, когда они сталкиваются с EOF.
Кристиан Чиупиту
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 байтов с помощью IOCTLоперации типа TIOCSTI.

Конечно, это будет работать только на системах, которые поддерживают TIOCSTI IOCTLтип операции (например, Linux), и только от rootучетной записи пользователя, так как эти «файлы» в /proc/«принадлежат» root.

maratbn
источник
1

В случае, если это кому-нибудь поможет: у
меня была похожая проблема, и поскольку процесс, который я использовал, не проходил, screenили tmuxмне пришлось использовать другой подход.

Я приложил gdbк xtermчто мой процесс был запущен в, и используется call write(5, "stop\n", 5)от gdbзаписи в дескрипторе мастер псевдотерминал файла.
Я выяснил, в какой файловый дескриптор отправлять данные, посмотрев /proc/<pid>/fdссылку /dev/ptmxи затем метод проб и ошибок между двумя вариантами (отправка моей строки в оба соответствующих файловых дескриптора, похоже, не принесла вреда).

РЕДАКТИРОВАТЬ

Оказалось, что xtermпроцесс , к которому я присоединился, был spawn-new-terminal() xtermвызван действием связывания клавиш, и второй ptmxоткрытый дескриптор файла был просто ptmxродительским xtermпроцессом, который не был закрыт.
Следовательно, вызовы проб и ошибок отправили вывод на этот другой терминал.
Большинство xtermпроцессов не имеют двух ptmxфайловых дескрипторов.

КОНЕЦ РЕДАКТИРОВАНИЯ

Это эффективно ввело эту строку в терминал и, следовательно, отправило ее процессу, запущенному под ним.

nb вам может понадобиться разрешить присоединение к запущенному процессу с чем-то вроде
sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"

яблоко
источник
0

Поскольку я не могу прокомментировать наиболее приемлемый ответ Кристиана Чиупиту (2010 г.), я должен поместить это в отдельный ответ:

Этот вопрос уже был решен в этой теме: https://stackoverflow.com/questions/5374255/how-to-write-data-to-existing-processs-stdin-from-external-process

Короче говоря:

Вы должны начать свой процесс с канала для стандартного ввода, который не блокирует и не закрывает, когда текущий ввод был записан. Это может быть реализовано простым бесконечным циклом, который будет передан в рассматриваемый процесс:

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

Я могу подтвердить, что это не то, как Крисси открыл трубу, которая не работала в моем случае. Показанное решение действительно работает вместо.

Затем вы можете записать в файл ... / fd / 0 процесса и отправить ему инструкции. Единственный недостаток заключается в том, что вам необходимо также завершить процесс bash, который выполняет бесконечный цикл после того, как сервер завершил работу.

user3471872
источник