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. The fd subdirectory contains the descriptors of all the opened files and file descriptor 0 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:

[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
  • 3
    @James Lawrie: then have a look at proc(5) and proc.txt. – Cristian Ciupitu Sep 6 '10 at 12:46
  • 2
    +2 no matter how much you think you know, there's always more to learn :) slick. – troyengel Sep 6 '10 at 14:55
  • 3
    Be aware though that the proc fd only redirects to what is used as a source of stdin. In your example, if you enter something in terminal 1 it will print it out again (it is send to cats stdin and cat prints it), thus resulting in you seeing it twice. On the other hand if you send something to fd/0 it will be sent to the console but not to cat, and thus only displayed once. As cat simply prints input out again with this example you can not really see if your input or output is being printed, thus this misconception. /fd/0 points to the console /pts; see ls -l /proc/7417/fd/0. – Kissaki Jul 2 '13 at 10:09
  • 4
    real-world example: I have started gphoto2 --get-all-files and it asks for a confirmation 100 times. When I echo "y" >/proc/PID/fd/0, gphoto2 does not proceed, however, "y" is printed in the terminal. – Thorsten Staerk May 25 '15 at 7:50
  • 2
    @ThorstenStaerk, I know, that's why I added that note. You're writing only to the device file corresponding to the terminal on which the gphoto2 runs (e.g. /dev/pts/19), the y character doesn't reach the application itself. It's the similar to what happens when you use the write(1) command. Anyway, either try my other answer or a graphical automation tool like xdotool. – Cristian Ciupitu May 25 '15 at 20:05

Screen based solution

Start the server like this:

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

screen will start in detached mode, so if you want to see what's going on, run:

# screen -r ServerFault

Control the server like this:

# 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

(this answer is based on sending text input to a detached screen from the Unix & Linux sibling site)

Explanation of the parameters:

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

tmux based solution

Start the server like this:

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

tmux will start in detached mode, so if you want to see what's going on, run:

# tmux attach-session -t ServerFault

Control the server like this:

# 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

Explanation of the parameters:

 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.

Try this to start:

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

And this to kill:

# cd /path/to/wd
# echo "stop" > cmd
# rm cmd
  • 3
    This is good, but it might have the disadvantage of not being able to send other commands while the program is running. If the program stops when it hits EOF on stdin then on the first echo "xxx" > cmd the program will stop (because the pipe will be closed). Though some programs are smart enough to reopen (rewind(3)) their stdin when they encounter EOF. – Cristian Ciupitu Sep 6 '10 at 12:30

In case it helps anyone:
I had a similar problem, and as the process I was using wasn't under screen or tmux, I had to take a different approach.

I attached gdb to the xterm that my process was running in, and used call write(5, "stop\n", 5) from gdb 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).

EDIT

It turned out that the xterm process I had attached to was spawned with the spawn-new-terminal() xterm action from a keybinding, and the second ptmx file descriptor open was simply the ptmx of the parent xterm 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 two ptmx file descriptors.

END EDIT

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"

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.