Because the majority of the Apache phases supported by mod_perl haven't changed since mod_perl 1.0, in this chapter we will discuss only those phases and corresponding handlers that were added or changed in mod_perl 2.0.

Figure 25-1 depicts the Apache 2.0 server cycle. You can see the mod_perl phases PerlOpenLogsHandler, PerlPostConfigHandler, and PerlChildInitHandler, which we will discuss shortly. Later, we will zoom into the connection cycle depicted in Figure 25-2, which will expose other mod_perl handlers.

Figure 25-1

Figure 25-1. Apache 2.0 server lifecycle

Apache 2.0 starts by parsing the configuration file. After the configuration file is parsed, any PerlOpenLogsHandler handlers are executed. After that, any PerlPostConfigHandler handlers are run. When the post_config phase is finished the server immediately restarts, to make sure that it can survive graceful restarts after starting to serve the clients.

When the restart is completed, Apache 2.0 spawns the workers that will do the actual work. Depending on the MPM used, these can be threads, processes, or a mixture of both. For example, the worker MPM spawns a number of processes, each running a number of threads. When each child process is started PerlChildInitHandlers are executed. Notice that they are run for each starting process, not thread.

From that moment on each working process (or thread) processes connections until it's killed by the server or the server is shut down. When the server is shut down, any registered PerlChildExitHandlers are executed.

Example 25-2 demonstrates all the startup phases.

Example 25-2. Book/StartupLog.pm

package Book::StartupLog;

use strict;
use warnings;

use Apache::Log ( );
use Apache::ServerUtil ( );

use File::Spec::Functions;

use Apache::Const -compile => 'OK';

my $log_file = catfile "logs", "startup_log";
my $log_fh;

sub open_logs {
    my($conf_pool, $log_pool, $temp_pool, $s) = @_;
    my $log_path = Apache::server_root_relative($conf_pool, $log_file);

    $s->warn("opening the log file: $log_path");
    open $log_fh, ">>$log_path" or die "can't open $log_path: $!";
    my $oldfh = select($log_fh); $| = 1; select($oldfh);

    say("process $$ is born to reproduce");
    return Apache::OK;
}

sub post_config {
    my($conf_pool, $log_pool, $temp_pool, $s) = @_;
    say("configuration is completed");
    return Apache::OK;
}

sub child_exit {
    my($child_pool, $s) = @_;
    say("process $$ now exits");
    return Apache::OK;
}

sub child_init {
    my($child_pool, $s) = @_;
    say("process $$ is born to serve");
    return Apache::OK;
}

sub say {
    my($caller) = (caller(1))[3] =~ /([^:]+)$/;
    if (defined $log_fh) {
        printf $log_fh "[%s] - %-11s: %s\n", 
            scalar(localtime), $caller, $_[0];
    }
    else {
        # when the log file is not open
        warn _ _PACKAGE_ _ . " says: $_[0]\n";
    }
}

END {
    say("process $$ is shutdown\n");
}

1;

Here's the httpd.conf configuration section:

PerlModule            Book::StartupLog
PerlOpenLogsHandler   Book::StartupLog::open_logs
PerlPostConfigHandler Book::StartupLog::post_config
PerlChildInitHandler  Book::StartupLog::child_init
PerlChildExitHandler  Book::StartupLog::child_exit

When we perform a server startup followed by a shutdown, the logs/startup_log is created, if it didn't exist already (it shares the same directory with error_log and other standard log files), and each stage appends to it its log information. So when we perform:

panic% bin/apachectl start && bin/apachectl stop

the following is logged to logs/startup_log:

[Thu Mar  6 15:57:08 2003] - open_logs  : process 21823 is born to reproduce
[Thu Mar  6 15:57:08 2003] - post_config: configuration is completed
[Thu Mar  6 15:57:09 2003] - END        : process 21823 is shutdown

[Thu Mar  6 15:57:10 2003] - open_logs  : process 21825 is born to reproduce
[Thu Mar  6 15:57:10 2003] - post_config: configuration is completed
[Thu Mar  6 15:57:11 2003] - child_init : process 21830 is born to serve
[Thu Mar  6 15:57:11 2003] - child_init : process 21831 is born to serve
[Thu Mar  6 15:57:11 2003] - child_init : process 21832 is born to serve
[Thu Mar  6 15:57:11 2003] - child_init : process 21833 is born to serve
[Thu Mar  6 15:57:12 2003] - child_exit : process 21833 now exits
[Thu Mar  6 15:57:12 2003] - child_exit : process 21832 now exits
[Thu Mar  6 15:57:12 2003] - child_exit : process 21831 now exits
[Thu Mar  6 15:57:12 2003] - child_exit : process 21830 now exits
[Thu Mar  6 15:57:12 2003] - END        : process 21825 is shutdown

First, we can clearly see that Apache always restarts itself after the first post_config phase is over. The logs show that the post_config phase is preceded by the open_logs phase. Only after Apache has restarted itself and has completed the open_logs and post_config phases again is the child_init phase run for each child process. In our example we had the setting StartServers=4; therefore, you can see that four child processes were started.

Finally, you can see that on server shutdown, the child_exit phase is run for each child process and the END { } block is executed by the parent process only.

Apache also specifies the pre_config phase, which is executed before the configuration files are parsed, but this is of no use to mod_perl, because mod_perl is loaded only during the configuration phase.

Now let's discuss each of the mentioned startup handlers and their implementation in the Book::StartupLog module in detail.