Могу ли я отправить какой-либо текст на STDIN активного процесса, запущенного в сеансе экрана?
У меня есть длительный процесс сервера внутри сеанса экрана на моем сервере Linux. Это немного нестабильно (и, к сожалению, не мое программное обеспечение, поэтому я не могу это исправить!), Поэтому я хочу написать сценарий перезапуска процесса каждую ночь, чтобы помочь стабильности. Единственный способ заставить его выполнить корректное завершение работы - перейти к экранному процессу, переключиться в окно, в котором он запущен, и ввести строку "стоп" на его консоли управления.
Есть ли какие-нибудь умные перенаправления, которые я могу сделать, чтобы cronjob отправлял эту команду остановки в определенное время каждый день?
9 ответов
Этот ответ не решает проблему, но он оставлен здесь, потому что более 30 человек нашли его полезным, в противном случае я бы удалил его давным-давно.
Написать в /proc/*pid of the program*/fd/0
, fd
подкаталог содержит дескрипторы всех открытых файлов и дескриптор файла 0
стандартный ввод (1 - стандартный вывод, 2 - стандартный вывод).
Вы можете использовать это для вывода сообщений на 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
Экранное решение
Запустите сервер так:
# 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.
Можно отправить входной текст запущенному процессу, не запуская screen
утилита, или любая другая необычная утилита. И это можно сделать, отправив этот входной текст в стандартный "файл" ввода процесса /proc/PID#/fd/0
,
Тем не менее, входной текст должен быть отправлен специальным способом для чтения процессом. Отправка введенного текста через обычный файл write
Метод не приведет к тому, что процесс получит текст. Это потому, что это добавит только к этому "файлу", но не запустит процесс для чтения байтов.
Чтобы запустить процесс для чтения байтов, необходимо сделать IOCTL
операция типа TIOCSTI
за каждый отправляемый байт. Это поместит байт в стандартную очередь ввода процесса.
Это обсуждается здесь с некоторыми примерами на C, Perl и Python:
-
Таким образом, чтобы ответить на первоначальный вопрос, заданный почти 9 лет назад, заданию cron потребуется запустить небольшой служебный скрипт / программу, аналогичную примерам, которые люди написали для этого другого вопроса, который отправит строку "stop \ n" этому процессу сервера. в вопросе, отправив каждый из 5 байтов через IOCTL
операция типа TIOCSTI
,
Конечно, это будет работать только на системах, которые поддерживают TIOCSTI
IOCTL
тип операции (например, Linux), и только из root
учетная запись пользователя, так как эти "файлы" под /proc/
"принадлежат" root
,
Существует более элегантное решение, позволяющее избежать использованияtail -f
и вечные циклы, которые тратят системные ресурсы.
Сначала создайте именованный канал для маршрутизации STDIN:
mkfifo /data/in
.Затем заблокируйте его для записи, чтобы он не закрывался, когда ваш процесс прочитает все текущее содержимое:
sleep infinity > /data/in &
. Спать вечно лучше, чемtailf -f /dev/null
потому что Tailf использует ресурсы inotify и будет запускаться каждый раз, когда какое-либо приложение отправляет данные в /dev/null. Вы можете увидеть это, запустивstrace
в теме. Это также лучше, чемcat > /dev/null &
потому чтоcat
сам будет отключен от STDIN, который, в свою очередь, закроется.Запустите свой процесс в фоновом режиме с помощью
/data/in
предоставление STDIN:application < /data/in &
. Это работает лучше, чем использование трубопровода из хвоста.tail -f /data/in | application &
потому что канал будет завершен только в том случае, если хвост остановится, но если ваше приложение выйдет из строя, канал продолжит работать.Остановите ожидание завершения работы приложения.
wait $(pidof application)
. При этом не используются ресурсы, и если приложение приведет к сбою, ваш код после ожидания будет выполнен. При желании вы можете добавить вокруг него цикл перезапуска приложения.Чтобы завершить работу приложения, корректно перехватывать и передавать ему системные сигналы с помощью
trap 'kill -SIGTERM $(pidof app)' SIGTERM
На случай, если это кому-нибудь поможет:
У меня была похожая проблема, и поскольку процесс, который я использовал, не был под screen
или же tmux
Я должен был принять другой подход.
Я приложил gdb
к xterm
что мой процесс запущен и используется call write(5, "stop\n", 5)
от gdb
записать в дескриптор основного файла pty.
Я выяснил, к какому дескриптору файла отправлять данные, посмотрев на /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"
Попробуйте это начать:
# screen
# cd /path/to/wd
# mkfifo cmd
# my_cmd <cmd
C-A d
И это убить
# cd /path/to/wd
# echo "stop" > cmd
# rm cmd
Поскольку я не могу прокомментировать наиболее приемлемый ответ Кристиана Чиупиту (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, который выполняет бесконечный цикл после того, как сервер завершил работу.
Если вы хотите избежать остановки цикла while в этом ответе, вы можете использовать хвост, который выйдет при завершении процесса.
Вместо записи в /prod//fd/0 необходим канал.
$ mkfifo input_pipe
$ tail -f input_pipe | your_program
Команда хрон
echo "some message" > input_pipe
Большое предостережение: на этом этапе вы не можете вводить команды на самом экране и не видите никаких вводимых данных.
Большинство решений не помогают, если программа уже запущена, то есть в другой оболочке, возможно, с другого компьютера, над которым вы теряете контроль.
В этом случае можно использовать код, показанный в https://unix.stackexchange.com/questions/48103/construct-a-command-by-putting-a-string-into-a-tty , с некоторыми изменениями.
Вариант использования:
- запустите apt dist-upgrade на своем компьютере на работе и забудьте об этом
- иди домой (да, такое случается)
- apt теперь может зависнуть в ожидании ввода
В домашних условиях можно узнать ПТС апп процесса
root@linux:~# w
16:50:27 up 148 days, 18:16, 5 users, load average: 0.77, 0.42, 0.32
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/1 10.0.0.1 14:37 3:39 5.07s 5.03s apt dist-upgrade
Теперь дома скомпилируйте этот код (адаптировано по ссылке выше)
#include <sys/ioctl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
void stackchar(char c)
{
if (ioctl(0, TIOCSTI, &c) < 0) {
perror("ioctl");
exit(1);
}
}
int main(int argc, char *argv[])
{
int i, j;
char c;
for (i = 1; i < argc; i++) {
if (i > 1) stackchar(' ');
for (j=0; (c = argv[i][j]); j++) {
stackchar(c);
}
}
stackchar('\r');
stackchar('\n');
exit(0);
}
Давайте скомпилируем его
root@linux:~# gcc pushTty.c -o pushTty
Затем сделайте (возможно, несколько раз), чтобы отправить Y в качестве входных данных.
root@linux:~# ./pushTty Y </dev/pts/1
или просто отправить новую строку (используя подходящий ответ по умолчанию)
root@linux:~# ./pushTty </dev/pts/1
и apt в конечном итоге завершит работу «чисто».