Writing to stdin of background process

48,298

Solution 1

You could try writing to it's /proc pid directory. Say your daemons' pid is 2000, try writing to /proc/2000/fd/0

Solution 2

You could start you server with a named pipe (fifo) as its input:

mkfifo /tmp/srv-input
cat > /tmp/srv-input &
echo $! > /tmp/srv-input-cat-pid
cat /tmp/srv-input | myserver &

The cat > /tmp/srv-input & is important to avoid your server to receive a EOF. At least one process must have the fifo opened in writing so your server does not receive a EOF. The PID of this command is saved in the /tmp/srv-input-cat-pid file for latter kill.

In your case where you've already started your server, you have to use a debugger such as gdb to attach to your process to redirect its stdin to the fifo:

gdb -p PID
call close(0)
call open(0, "/tmp/srv-input", 0600)

And then do something like bellow to send input to your server (in another terminal window if necessary):

echo "command" > /tmp/srv-input

To send a EOF to your server, you need to kill the cat > /tmp/srv-input process which PID has been saved in the /tmp/srv-input-cat-pid file.

In the case of GDB, just quit GDB and EOF will be sent.

Solution 3

Same as above, but 'cat' did not work for me. The file got EOF and ended after sending one command.

This worked for me:

#!/bin/bash

mkfifo /tmp/srv-input
tail -f /tmp/srv-input | myserver &

Solution 4

There is a more elegant solution that solves the issues with tail -f and cat

  • Create a named pipe to route STDIN through: mkfifo /data/in.

  • 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 than tailf -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 running strace on it. It is also better than cat > /dev/null & because cat 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 tail tail -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 script 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
Share:
48,298

Related videos on Youtube

Kinghizzzzz
Author by

Kinghizzzzz

Updated on September 17, 2022

Comments

  • Kinghizzzzz
    Kinghizzzzz over 1 year

    I'm on an Ubuntu 10.04 box, and started a server in the background (myserver &) over ssh. It's been running fine, but I need a way to get at the server's stdin, as the only way to control the server is through this method.

    Is there some way to get at the stdin of an already-running process so I can write to it (and hopefully read its stdout)? Obviously, if I were going to be doing this now, I'd start it with a FIFO redirecting to stdin, but unfortunately it's a little late for that now.

    Any ideas?

    • symcbean
      symcbean about 8 years
      Couldn't you just bring it back to the foreground? ('jobs' will list your current background process, 'fg $X' will bring the job back to the foreground, ctrl+b will pause the job and return you to your shell, while 'bg' will continue the paused process in the background)
  • Kinghizzzzz
    Kinghizzzzz over 13 years
    Thanks... I found that right after I posted this (after a day of looking--typical). That seems to work (as far as actually sending data to the program). Unfortunately, the program doesn't accept the commands. I tested it running the server on my local computer, and sure enough, I see the data appear, but the program doesn't recognize the commands. I have to manually press enter on the server terminal, and then it just says unrecognized command. Maybe some java weirdness? I'm stuck...
  • katriel
    katriel over 13 years
    how about echo -e "something\n" > /proc/2000/fd/0?
  • Prior99
    Prior99 about 9 years
    this is a much more portable approach than the one from @katriel as /proc/2000/fd/0 is not stdin on all systems.
  • bk138
    bk138 about 9 years
    What about mkfifo /tmp/srv-input; tail -f /tmp/srv-input | myserver & ? This'll keep the pipe open as well...
  • jfg956
    jfg956 about 9 years
    @bk138: it looks to me as tail should work, but there is only one way to know for sure: test.
  • Admin
    Admin over 8 years
    The first answer to serverfault.com/questions/178457/… notes that this approach doesn't actually work.
  • Thiago Macedo
    Thiago Macedo over 8 years
    tail doesn't work, but appended this to finish the job: cat /tmp/srv-input | myserver; kill -9 cat /tmp/srv-input-cat-pid` && rm /tmp/srv-input-cat*`
  • Feuermurmel
    Feuermurmel over 7 years
    This does not actually work. Your shell normally (when no pipes or redirections are used) starts a command with file descriptors 0 through 2 set to the same file, which normally is a virtual terminal (something like /dev/pty/...). The command then reads from FD 0 and writes to FD 1 and 2 to communicate with the virtual terminal (e.g. over SSH or directly with your terminal emulator). If any other process accesses that file (e.g. through /proc), exactly the same thing happens, i.e. writing to it writes to the terminal and not to the command.
  • Christian Reall-Fluharty
    Christian Reall-Fluharty over 3 years
    I know the edit that added it is a community one, but what version of open is this using that allows passing 0 (presumably the STDIN fd?) as the first argument? Both gdb and gcc return -1 when I make that call (though I suppose that could be for any number of reasons).