I have a long-running server process inside a screen session on my Linux server. It's a bit unstable (and sadly not my software so I can't fix that!), so I want to script a nightly restart of the process to help stability. The only way to make it do a graceful shutdown is to go to the screen process, switch to the window it's running in, and enter the string "stop" on its control console.
Are there any smart redirection contortions I can do to make a cronjob send that stop command at a fixed time every day?
This answer doesn't solve the problem, but it's left here because 30+ people found it useful, otherwise I would have deleted it long time ago.
Write to
/proc/*pid of the program*/fd/0
. Thefd
subdirectory contains the descriptors of all the opened files and file descriptor0
is the standard input (1 is stdout and 2 is stderr).You can use this to output messages on the tty where a program is running, though it does not allow you to write to the program itself.
Example
Terminal 1:
Terminal 2:
Screen based solution
Start the server like this:
screen will start in detached mode, so if you want to see what's going on, run:
Control the server like this:
(this answer is based on sending text input to a detached screen from the Unix & Linux sibling site)
Explanation of the parameters:
tmux based solution
Start the server like this:
tmux will start in detached mode, so if you want to see what's going on, run:
Control the server like this:
Explanation of the parameters:
It is possible to send input text to a running process without running the
screen
utility, or any other fancy utility. And it can be done by sending this input text to the process' standard input "file"/proc/PID#/fd/0
.However, the input text needs to be sent in a special way to be read by the process. Sending the input text via the regular file
write
method will not cause the process to receive the text. This is because doing so will only append to that "file", but will not trigger the process to read the bytes.To trigger the process to read the bytes, it is necessary to do an
IOCTL
operation of typeTIOCSTI
for every single byte to be sent. This will place the byte into the process' standard input queue.This is discussed here with some examples in C, Perl, and Python:
https://unix.stackexchange.com/questions/48103/construct-a-command-by-putting-a-string-into-a-tty/48221
--
So to answer the original question asked almost 9 years ago, the cron job would need to run some small utility script / program similar to the examples people wrote for that other question, which would send the string "stop\n" to that server process in the question, by sending each of the 5 bytes via an
IOCTL
operation of typeTIOCSTI
.Of course this will only work on systems that support the
TIOCSTI
IOCTL
operation type (like Linux), and only from theroot
user account, as these "files" under/proc/
are "owned" byroot
.There is a more elegant solution that avoids the use of
tail -f
and the forever loops that waste system resources.First create a named pipe to route STDIN through:
mkfifo /data/in
.Then block it for writing, so it does not get closed when your process read all of the current contents:
sleep infinity > /data/in &
. Sleeping forever is better thantailf -f /dev/null
because tailf uses inotify resources and will be triggered each time some app sends data to /dev/null. You can see this by runningstrace
on it. It is also better thancat > /dev/null &
becausecat
will be itself disconnected from STDIN, which in turn will close/data/in
.Start your process in the background with the
/data/in
providing STDIN:application < /data/in &
. This works better than using piping from tailtail -f /data/in | application &
because the pipe will only get terminated if the tail stops, but if your application crashes the pipe will keep running.Halt waiting for the application to finish.
wait $(pidof application)
. This uses no resources and if the application crashes your code after the wait will be executed. You can add an application restart loop around it if you wish.To terminate the application gracefully trap and relay the system signals to it with
trap 'kill -SIGTERM $(pidof app)' SIGTERM
Try this to start:
And this to kill:
Since I cannot comment the most accepted answer of Cristian Ciupitu (of 2010), I have to put this in a separate answer:
This question was already solved in this thread: https://stackoverflow.com/questions/5374255/how-to-write-data-to-existing-processs-stdin-from-external-process
In short:
You have to start your process with a pipe for stdin which does not block nor close when the current input was written through. This can be implemented by a simple endless loop which will be piped to the process in question:
I can confirm that this is different of krissi's way to open a pipe which was not working in my case. The shown solution did work instead.
You can then write to the .../fd/0 file of the process to send instructions to it. The only drawback is that you need to terminate the bash process as well which is executing the endless-loop after the server did shut down.
In case it helps anyone:
I had a similar problem, and as the process I was using wasn't under
screen
ortmux
, I had to take a different approach.I attached
EDITgdb
to thexterm
that my process was running in, and usedcall write(5, "stop\n", 5)
fromgdb
to write to the master pty file descriptor.I found out which file descriptor to send the data to by looking at
/proc/<pid>/fd
for a link to/dev/ptmx
and then trial and error between the two options (sending my string to both matching file descriptors seemed to cause no harm).It turned out that the
END EDITxterm
process I had attached to was spawned with thespawn-new-terminal()
xterm
action from a keybinding, and the secondptmx
file descriptor open was simply theptmx
of the parentxterm
process that hadn't been closed.Hence the trial and error calls had sent output to that other terminal.
Most
xterm
processes don't have twoptmx
file descriptors.This effectively typed that string into the terminal, and hence sent it along to the process running under it.
n.b. you may need to allow attaching to a running process with something like
sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"