Inconvenient behaviour from daemon(1)
I recently came across daemon(1), a neat little GPL utility that will turn any program into a proper daemon. Doing this usually requires a series of calls to semi-obscure POSIX API functions. This is not hard, but only fun the first few times you do it.
Say I have random Perl script foo.pl that I want to run in the background as a daemon. This can be done with a simple command like:
Presuming, of course, that foo.pl is in my $PATH. daemon will fork(), become a session leader, chdir() to /, and do a bunch of other stuff that is well described in Chapter 13 of Advanced Programming in the Unix Environment. daemon then runs foo.pl and waits for it to terminate.
Things now get interesting because I can do:
Which will exit immediately with an exit status of 0 if foo.pl is running and 1 if it’s not. Once foo.pl has served my nefarious purposes, it can be easily stopped:
daemon can do many other useful things that are well described in the man page.
I liked daemon so much I thought I’d use it to run a daemon on a remote host from a Perl script. Doing this requires a bit of Perl that looks something like:
chomp $dir;
`ssh remotehost daemon –pidfile $dir/foo.pid
–name foo-daemon $dir/foo.pl`;
The only problem with this is that it doesn’t work. daemon decides that it’s not going to fork(), so my Perl script waits for foo.pl to exit rather than running it in the background. A bit of rumaging around the source soon revealed the reason why. daemon plays nice with init(8) and inetd(8), and it knows not to become a daemon if started by either of these programs- essentially because such things will have already been taken care of.
The check to see if daemon has been started by inetd(8) is actually done in libslack and is implemented by the following function:
{
size_t optlen = sizeof(int);
int optval;
return (getsockopt(STDIN_FILENO, SOL_SOCKET,
SO_TYPE, &optval, (void *)&optlen) == 0);
}
So if standard input is a socket which is apparently the case when daemon has been run by sshd(8), daemon assumes it has been started from inetd(8) and doesn’t do its whole daemonization routine. Annoying. I tested this by modifying daemon_started_by_inetd() so that it always returns 0, and sure enough things worked fine.
Personally I think this is a perfect excuse for a new option for daemon. I’ll have to ask if they will accept a patch. I am a little surprised that no-one has run into this before- I doubt that I’m the only guy writing grubby little Perl scripts that need to run stuff on multiple hosts. More likely there are other ways to do it just as well, or better.