Some years ago, before the Net::Server and Net::Daemon modules were on CPAN, a friend asked me to write him a simple daemon that would sniff out servers for Quake, Unreal, etc. That is, one tool to do all of these types, and provide a way for him to connect and get the list, etc. etc. Of course, since then people have written much more feature-ful tools like this, some of them even in C :-). What I took away from the experience, though, is the following subroutine. Since he planned on having this thing run for days at a time, I wanted it to daemon-ize itself just as cleanly as sendmail, inetd, or any other legacy daemon written in C. So I turned to my trusty copy of "Advanced UNIX Programming" by the (late) great W. R. Stevens, and churned out this piece.

What you see here is a much more literate version, one that has some commentary sprinkled around, and some better attention to indentation and such, for the sake of readability. The one (optional) parameter it takes at invocation is a simple boolean of whether to die on errors or not. Unless explicitly specified, the default is to die when an error occurs. When the function returns (error-free), the caller may feel confident that they are now well and properly backgrounded. STDIN, STDOUT and STDERR are no longer available to you, but they are available to be reopened onto any files you like.

This could maybe be made more efficient or more compact, but soon after this was when Net::Daemon came out, and soon after that Net::Server. If you have real server-implementation needs, I recommend one of those (I provide support for Net::Server natively in my RPC::XML package). But if you have a simpler task for which you want to background and not worry about terminal-related and process-group-related signals and issues, this may hopefully be of use. Enjoy.

Update: Added a chdir to root.

--rjray

sub become_daemon { my $dont_die = shift || 0; my ($child, $sig); $child = fork; if (defined $child) { exit 0 if $child; } else { $dont_die && return "Error in fork: $!"; die "$0 died in fork: $!, dead"; } # # First-generation child. # setpgrp; close(STDIN); close(STDOUT); close(STDERR); chdir '/'; umask 0; for (qw(TSTP TTIN TTOU)) { $SIG{$_} = 'IGNORE' if (exists $SIG{$_} +) } # # In case of SysV, re-spawn to avoid danger of re-acquiring a cont +rol # terminal. Now the child is not the pgrp leader. # $sig = $SIG{HUP}; $SIG{HUP} = 'IGNORE'; $child = fork; if (defined $child) { exit 0 if $child; } elsif ($child) { $dont_die && return "Error in (1st-generation child) fork: $!" +; die "$0 (1st-generation child) died in fork: $!, dead"; } # # Restore HUP (which will probably be set later on, anyway) and re +turn. # $SIG{HUP} = $sig; return; }

Replies are listed 'Best First'.
Re: Forking the polite way
by rob_au (Abbot) on Feb 12, 2002 at 12:19 UTC
    It might be worth taking a look at the source to Proc::Daemon which also follows the general rules of daemon processes described by W. Richard Stevens - That is, this module performs the following:

    • Forks a child and exits the parent process.

    • Becomes a session leader (which detaches the program from the controlling terminal).

    • Forks another child process and exits first child. This prevents the potential of acquiring a controlling terminal.

    • Changes the current working directory to "/" - The importance of which is outlined by Zaxo above.

    • Clears the file creation mask.

    • Closes all open file descriptors (rather than just STDIN, STDOUT and STDERR - Important if these have been redirected or duplicated within the parent process).

     

    perl -e 's&&rob@cowsnet.com.au&&&split/[@.]/&&s&.com.&_&&&print'

      Thanks for the feedback. I've added the chdir as suggested. It was already clearing the umask and setting itself as a process group leader with setpgrp(). After giving it some thought though, I'm not going to add a use of POSIX because it's just too heavy of a module to saddle someone with. Ditto on closing all files. I am considering having it re-open STDIN, STDOUT and STDERR onto /dev/null, though.

      --rjray

Re: Forking the polite way
by Zaxo (Archbishop) on Feb 12, 2002 at 09:23 UTC

    You forgot one courtesy. chdir '/'; , so that the former $ENV{PWD}'s filesystem can be umounted or remounted cleanly.

    Also, use POSIX; will permit a range of system calls that are useful for this kind of thing.

    After Compline,
    Zaxo