The forking psychotherapist server has a deficiency. [2] When the server is launched, it doesn't automatically go into the background but instead ties up a terminal where it can be brought down by an inadvertent tap on the interrupt key. Of course, the user launching it from the command line can always background the server, but that is inconvenient and error prone, since the server might be brought back into the foreground inadvertently. [2] This discussion relies heavily on the UNIX process model, and will not translate to Macintosh or Windows systems. Windows NT and 2000 users can turn Perl scripts into background services using a utility called srvany.exe. See the section Background on Windows and Macintosh Systems later in this chapter. Under UNIX, most network servers act as "daemons." When launched, they go into the background, and keep running until they are deliberately killed or the system itself is shut down. They have no access to a terminal or command window. Instead, if they want to issue status messages, they must log them to a file. The word "daemon" was chosen to convey the image of a sorcerer 's magical servant who does his bidding invisibly . In this case, the server is the daemon, and network communications is the magic. On launch, a daemon should put itself into the background automatically and close its standard input, output, and error handles. It should also completely dissociate itself from the "controlling terminal" (the terminal window or console from which the daemon was launched). This has two purposes. One is that the program (or a subprocess launched by it) will not be able to reopen the terminal device and inadvertently intermix its output with that of other programs. The second effect is that the daemon will not receive a HUP (hangup) signal when the user exits the command shell after launching the server. Network daemons should also: -
Change their current working directory to the root directory. This normalizes the environment and avoids problems with unmounting the filesystem from which the daemon was started. -
Change their file creation mask to a known state (rather than inheriting it from the shell). -
Normalize the PATH environment variable. -
Write their process IDs to a file in /var/run or a similar location. -
Optionally, use syslog (or the Windows event logger) to write diagnostic messages to the system log file. -
Optionally, handle the HUP signal by reinitializing themselves or reloading their configuration files. -
Optionally, use the chroot() call to place themselves in a restricted part of the filesystem, and/or set their privileges to those of an unprivileged user on the system. Autobackgrounding In this section, we develop a routine for autobackgrounding network daemons and performing tasks 1 through 4. In Chapter 16 we discuss techniques for implementing items 5 through 7. Figure 10.5 lists the become_daemon() subroutine, which a server process should call very early during its initialization phase. This subroutine uses a standard UNIX trick for backgrounding and dissociating from the controlling terminal. It forks itself (line 2) and the parent process exits, leaving only the child in control. Figure 10.5. The become_daemon() subroutine The child process now starts a new process session by calling the setsid() function, provided by the POSIX module (line 4). A session is a set of processes that share the same terminal. At any given time only one member of the set has the privilege of reading and writing to the terminal and is said to be in the foreground, while other members of the group remain in the background (and if they try to do I/O to the terminal, are suspended until they are brought to the foreground). This system is used by command shells to implement job control. A session group is related , but not identical, to a process group. A process group is the set of subprocesses that have been launched by a single parent, and is an integer corresponding to the PID of the group's shared ancestor . You can use the Perl getpgrp() function to fetch the process group for a particular process, and pass kill() the negative of a process group to send the same signal simultaneously to all members of the group. This is how the shell does it when sending a HUP signal to all its subprocesses just prior to exiting. A newly forked child belongs to the same session group and process group as its parent. setsid() does several things. It creates both a new session and a new process group, and makes the current process the session leader. At the same time, it dissociates the current process from the controlling terminal. The effect is to make the child process completely independent of the shell. setsid() fails if the process is a session leader at the time the function is called (i.e., is in the foreground), but the earlier fork ensures that this is not the case. After calling setsid() , we reopen the STDIN and STDOUT filehandles onto the "do nothing" special device, /dev/null , and make STDERR a copy of STDOUT (lines 5 “7). This maneuver prevents output from the daemon from appearing on the terminal. It then calls chdir() to change the current working directory to the root filesystem, resets the file creation mask to 0, and sets the PATH environment variable to a small number of standard directories (line 10). We return the new process ID from the $$ global. Because we forked, the process ID is now different from its value when the subroutine was called, and returning the new PID explicitly in this way is a good way to remind ourselves of that fact. There are a number of variations on the become_daemon() subroutine. Stevens [1998] recommends forking not once but twice, warning that otherwise it is possible for the first child to reacquire a controlling terminal by deliberately reopening the /dev/tty device. However, this event is unlikely , and few production servers do this. Instead of reopening the standard filehandles onto /dev/null, you may want to simply close them: close $_ foreach (\*STDIN,\*STDOUT,\*STDERR); However, this strategy may confuse subprocesses that expect the standard filehandles to be open, so it is best avoided. Finally, a few older UNIX systems, such as ULTRIX, do not have a working setsid() . On such systems, the call to setsid() returns a run-time error. On such systems, you can use the Proc::Daemon module, available on CPAN, which contains the appropriate workarounds. PID Files Another feature we can add at this time is a PID file for the psychotherapist server. By convention, servers and other system daemons write their process IDs into a file named something like /var/run/ servername .pid . Before exiting, the server removes the file. This allows the system administrator and other users to send signals to the daemon via this shortcut: kill -TERM `cat /var/run/servername.pid` A clever daemon checks for the existence of this file during startup, and refuses to run if the file exists, which might indicate that the server is already running. Very clever daemons go one step further, and check that the process referred to by the PID file is still running. It is possible that a previous server crashed or was killed before it had a chance to remove the file. The open_pid_file() subroutine listed in Figure 10.6 implements this strategy. Figure 10.6. open_pid_file() routine Lines 1 “3: Check whether old PID file exists open_pid_file() is called with the path to the PID file. Our first action is to apply the -e file test to the file to determine whether it already exists. Lines 4 “6: Check whether old PID file is valid If the PID file exists, we go on to check whether the process it indicates is still running. We use IO::File to open the old PID file and read the numeric PID from it. To determine if this process is still running, we use kill() to send signal number 0 to the indicated process. This special signal number doesn't actually send a signal, but instead returns true if the indicated process (or process group) can receive signals. If kill() returns true, we know that the process is still running and exit with an error message. Otherwise, if kill() returns false, then we know that the previous server process either exited uncleanly without cleaning up its PID file, or that it is running under a different user ID and the current process lacks the privileges to send the signal. We ignore this latter case, assuming that the server is always launched by the same user. If this assumption is false, then our attempt to unlink the old PID file in the next step will fail and no harm will be done. Lines 7 “9: Unlink old PID file We write a warning to standard error and attempt to unlink the old PID file, first checking with the -w file test operator that it is writable. If either the -w test or the unlink() fail, we abort. Lines 10 “12: Create new PID file The last two steps are to create a new PID file and open it for writing. We call IO::File->new() with a combination of flags that creates the file and opens it, but only if it does not previously exist. This prevents the file from being clobbered in the event that the server is launched twice in quick succession, both instances check for the PID file and find it absent, and both try to create a new PID file at about the same time. If successful, we return the open filehandle to the caller. open_pid_file() should be invoked before autobackgrounding the server. This gives it a chance to issue error messages before standard error is closed. The caller should then call become_daemon() to get the new process ID, and write that PID to the PID file using the filehandle returned by open_pid_file() . Here's the complete idiom: use constant PID_FILE => '/var/run/servername.pid'; $SIG{TERM} = $SIG{INT} = sub { exit 0; } my $fh = open_pid_file(PID_FILE); my $pid = become_daemon(); print $fh $pid; close $fh; END { unlink PID_FILE if $pid == $$; } By convention, the /var/run directory is used by many UNIX systems to write PID files for running daemons. Solaris systems use /etc or /usr/local/etc. The END{} block guarantees that the server will remove the PID file before it exits. The file is unlinked only if the current process ID matches the process ID returned by become_daemon() . This prevents any of the server's children from inadvertently unlinking the file. The reason for installing signal handlers for the TERM and INT signals is to ensure that the program exits normally when it receives these signals. Otherwise, the END{} block would not be executed and the PID file would remain around after the server had exited. Figure 10.7 puts all these techniques together in a new and improved forking server, eliza_daemon.pl . There should be no surprises in this code, with the minor exception that instead of placing the PID file inside the standard /var/run directory, the example uses /var/tmp/eliza.pid . /var/run is a privileged directory, and to write into it we would have to be running with root privileges. However, this carries security implications that are not discussed until Chapter 16. It is not a particularly good idea for a root process to write into a world-writable directory such as /var/tmp for reasons discussed in that chapter, but there's no problem doing so as an unprivileged user. This script also incorporates the fix to the Chatbot::Eliza::_testquit() subroutine discussed earlier. Figure 10.7. The Eliza server (forking version) with daemon code Another point is that we create the listen socket before calling become_daemon() . This gives us a chance to die with an error message before become_daemon() closes standard error. Chapter 16 discusses how daemons can log errors to a file or via the syslog system. |