Shell – How to make a shell script that sends output to a process

scriptingshell-scripttail

I'm currently running a server console program in a screen because I need to both read it and occasionally send commands.

I'd like to run the app as a daemon in the background (start/stop it with init).

I could tail -f the log, but that won't let me send input to the process.

Is there any way to set this up so that I can both read and send input, but still have it running in the background?

I'd also like to be able to send input to the daemon from different processes as well (a shell script that could send a "Stop\n" command, for instance).

Best Answer

Read from a pipe, write to a file

If you want the daemon to read input produced by some arbitrary process, you need to connect that process to a pipe. Here the arbitrary process is you echoing commands, and it's going to run in a different context. So create a named pipe (often called a fifo in unix contexts).

mkfifo /var/run/daemon.fifo
</var/run/daemon.fifo /path/to/daemond --option >daemon.log

And just write commands to the pipe:

echo 'FORWARD 10' >/var/run/daemon.fifo
echo 'LEFT 72' >/var/run/daemon.fifo

This is unlikely to work as is however: there's a good chance that the daemon will exit when it sees an end of file on its standard input, which happens as soon as the first process that writes to the pipe terminates. You can use tail -f to avoid that problem.

</var/run/daemon.fifo tail -c +1 -f | {
  echo $$ >/var/run/daemon.pid
  exec /path/to/daemond --option >daemon.log
}

With some tail implementations, you may get bitten by buffering: the tail process will wait until it has amassed enough bytes to emit some output. I don't think this is solvable in the POSIX toolbox; if that's a problem, use a trivial C or Perl or Python program. As far as I can tell the tail from GNU coreutils (as found on Linux and elsewhere) is safe on this respect.

When you stop the daemon, echo >/var/run/daemon.fifo will kill the tail process.


Starting the program inside screen

Instead of invoking the daemon directly from your service manager (are you really using just SysV init, or something additional like wrapper scripts or Upstart?), invoke

screen -c daemon.screenrc -L -d -m -S daemon_name /path/to/daemond --option

Since the daemon won't be a child process of the service manager, you need to make sure to send a signal to the right process. How to do that depends on exactly how the daemon is started and by what.

It's technically possible to attach a running process to a terminal, but there's a risk you'll crash the program, so this is definitely out for a production system.

The -L option makes screen write everything that appears in its window to a file. The file name is given in daemon.screenrc with the logfile directive.

Related Question