
![]() | ![]() |
17.17. Making a Daemon Server
17.17.1. Problem
You want your program to run as a
daemon.
17.17.2. Solution
If you are paranoid and running as root, chroot to
a safe directory:chroot("/var/daemon")
or die "Couldn't chroot to /var/daemon: $!";
Fork once, and let the parent exit:$pid = fork;
exit if $pid;
die "Couldn't fork: $!" unless defined($pid);
Close the three standard filehandles by reopening them to
/dev/null:for my $handle (*STDIN, *STDOUT, *STDERR) {
open($handle, "+<", "/dev/null")
|| die "can't reopen $handle to /dev/null: $!";
}
Dissociate from the controlling terminal that started us and stop
being part of whatever process group we had been a member of:use POSIX;
POSIX::setsid( )
or die "Can't start a new session: $!";
Trap fatal signals, setting a flag to indicate that we need to
gracefully exit:$time_to_die = 0;
sub signal_handler {
$time_to_die = 1;
}
$SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&signal_handler;
# trap or ignore $SIG{PIPE}
Wrap your actual server code in a loop:until ($time_to_die) {
# ...
}
17.17.3. Discussion
Before POSIX, every operating system had its own way for a process to
tell the operating system "I'm going it alone, please interfere with
me as little as possible." POSIX makes it much cleaner. That said,
you can still take advantage of any operating system-specific calls
if you want to.The chroot call is one of those non-POSIX calls.
It makes a process change where it thinks the directory
/ is. For instance, after
chroot "/var/daemon", if the
process tries to read the file /etc/passwd, it
will read /var/daemon/etc/passwd. A
chroot ed process needs copies of any files it will
run made available inside its new /, of course.
For instance, our chrooted process would need
/var/daemon/bin/csh if it were going to glob
files. For security reasons, only the superuser may
chroot. This is done by FTP servers if you log
into them anonymously. It isn't really necessary to become a daemon.The operating system expects a child's parent to wait when the child
dies. Our daemon process has no particular parent to do this, so we
need to disinherit it. This we do by fork ing once
and having our parent exit, so that the child is not associated with
the process that started the parent. The child then closes the
filehandles it got from its parent (STDIN,
STDERR, and STDOUT) by
reopening them to /dev/null, and then calls
POSIX::setsid to ensure that it is completely dissociated from its
parent's
terminal.If you want to make sure any higher numbered file descriptors are
also closed, you can use the
+<&=NUMBER notation to
connect up an existing system file descriptor to a Perl filehandle,
and then call close on that handle. Here we'll hit
all descriptors above 2 and below 256:for (my $fd = 3; $fd < 256; $fd++) {
open(my $handle, "+<&=$fd"); # XXX: no error checking
close $handle; # XXX: no error checking
}
Instead of guessing the highest possible file descriptor number, the
"correct" way to handle that would be to write a C extension that
called getdtablesize(3). This is an exercise we
leave up to the user.Now we're almost ready to begin. We don't want signals like SIGINT to
kill us immediately (its default behavior), so we use
%SIG to catch them and set a flag saying it's time
to exit. Then our main program simply becomes: "While we weren't
killed, do something."The signal SIGPIPE is a special case. It's easy to get (by writing to
a filehandle whose other end is closed) and has unforgiving default
behavior (it terminates your process). You probably want to either
ignore it ($SIG{PIPE} =
'IGNORE') or define your own signal handler to
deal with it appropriately.
17.17.4. See Also
Your system's setsid(2) and
chroot(1) manpage (if you have them); the
chroot function in Chapter 29 of
Programming Perl and in
perlfunc(1); the Unix Socket FAQ at http://www.ibrado.com/sock-faq/;
UNIX Network Programming
![]() | ![]() | ![]() |
17.16. Writing a Multihomed Server | ![]() | 17.18. Restarting a Server on Demand |

Copyright © 2003 O'Reilly & Associates. All rights reserved.