11

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

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
Bill K
  • 294
  • 2
  • 10
  • Just a comment to anyone coming to read this--the answer here is fantastic. If you don't know about named pipes I highly recommend you at least give them a try. Using this I could run Minecraft as a service and interact with the console from other scripts by writing to the named pipe--so it becomes possible to write scripts to stop the server, send messages to the users, etc while still parsing the output log to look for key lines (such as sending chat messages addressed to a user to him as texts) – Bill K Sep 15 '16 at 16:15

1 Answers1

9

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.

  • Actually, I'd really like to be able to send messages to it's stdin from a script--maybe that's the question I should have asked. Currently I'm just running the server from a script and am able to type to it in a terminal, but it just seems like it should be running as a service... – Bill K Dec 22 '10 at 22:46
  • @Bill: Ok, I see. Then the first thing that comes to my mind is a named pipe. – Gilles 'SO- stop being evil' Dec 22 '10 at 23:43
  • I think this is exactly what I want @Gilles! I need to understand it better though. I'll spend some time sorting through man pages to figure it out--I honestly get very little of that (I get most of what you are doing and almost none of how you did it--and I thought I kind of had a clue with this stuff.) My theory wasn't to connect directly to the process but to create yet another script that joined it's i/o to the deamons o/i to make it seem like the original console is running, but with the ability to do the echo 'FORWARD 10' from another script at the same time. – Bill K Dec 23 '10 at 00:06
  • I think I get much of it. If I break it down I now understand "mkfifo pipe" and "tail -f pipe | command > output", tested those and they work. I think most of the other stuff you had was tricks to get it to run on one line--am I missing anything critical? – Bill K Dec 23 '10 at 00:23
  • @Bill: You could write to the terminal inside screen from the outside (using screen's stuff command). But you don't need the overhead (processing, but most importantly cognitive) of a terminal here, a pipe is almost enough (it's enough with a little end-of-file-ignoring relay process). You may want to experiment a little with <fifo cat or <fifo tail -f | cat in one terminal and echo >fifo; echo >fifo in another terminal; I think you'll be fine. – Gilles 'SO- stop being evil' Dec 23 '10 at 00:43