use CGI ( );
use strict;
my $firstname = "Jarkko";
my $surname = "Hietaniemi";
my $q = CGI->new;
print $q->header(-type=>'text/html');
print $q->p("$firstname $surname holds the patch pumpkin" .
"for this Perl release.");
The script is very simple: it initializes the CGI object, prints the
proper HTTP header, and tells the world who the current patch pumpkin
is. The name of the patch pumpkin is a hardcoded value.
We don't want to modify the script every time the
patch pumpkin changes, so we put the $firstname
and $surname variables into a configuration file:
$firstname = "Jarkko";
$surname = "Hietaniemi";
1;
Note that there is no package declaration in the above file, so the
code will be evaluated in the caller's package or in
the main:: package if none was declared. This
means that the variables $firstname and
$surname will override (or initialize) the
variables with the same names in the caller's
namespace. This works for global variables only—you cannot
update variables defined lexically (with my( ))
using this technique.
Let's say we have started the server and everything
is working properly. After a while, we decide to modify the
configuration. How do we let our running server know that the
configuration was modified without restarting it? Remember, we are in
production, and a server restart can be quite expensive. One of the
simplest solutions is to poll the file's
modification time by calling stat( ) before the
script starts to do real work. If we see that the file was updated,
we can force a reconfiguration of the variables located in this file.
We will call the function that reloads the configuration
reread_conf( ) and have it accept the relative
path to the configuration file as its single argument.
Apache::Registry executes a chdir(
) to the script's directory before it
starts the script's execution. So if your CGI script
is invoked under the Apache::Registry handler, you
can put the configuration file in the same directory as the script.
Alternatively, you can put the file in a directory below that and use
a path relative to the script directory. However, you have to make
sure that the file will be found, somehow. Be aware that do(
)searches the libraries in the directories in
@INC.
use vars qw(%MODIFIED);
sub reread_conf {
my $file = shift;
return unless defined $file;
return unless -e $file and -r _;
my $mod = -M _;
unless (exists $MODIFIED{$file} and $MODIFIED{$file} = = $mod) {
unless (my $result = do $file) {
warn "couldn't parse $file: $@" if $@;
warn "couldn't read $file: $!" unless defined $result;
warn "couldn't run $file" unless $result;
}
$MODIFIED{$file} = $mod; # Update the MODIFICATION times
}
}
Notice that we use the = = comparison operator
when checking the file's modification timestamp,
because all we want to know is whether the file was changed or not.
When the require( ), use( ),
and do( ) operators successfully return, the file
that was passed as an argument is inserted into
%INC. The hash element key is the name of the
file, and the element's value is the
file's path. When Perl sees require(
) or use( ) in the code, it first tests
%INC to see whether the file is already there and
thus loaded. If the test returns true, Perl saves the overhead of
code rereading and recompiling; however, calling do(
) will load or reload the file regardless of whether it has
been previously loaded.
We use do( ), not require( ),
to reload the code in this file because although do(
) behaves almost identically to require(
), it reloads the file unconditionally. If do(
) cannot read the file, it returns undef
and sets $! to report the error. If do(
) can read the file but cannot compile it, it returns
undef and sets an error message in
$@. If the file is successfully compiled,
do( ) returns the value of the last expression
evaluated.
The configuration file can be broken if someone has incorrectly
modified it. Since we don't want the whole service
using that file to be broken that easily, we trap the possible
failure to do( ) the file and ignore the changes
by resetting the modification time. If do( ) fails
to load the file, it might be a good idea to send an email about the
problem to the system administrator.
However, since do( ) updates
%INC like require( ) does, if
you are using Apache::StatINC it will attempt to
reload this file before the reread_conf( ) call.
If the file doesn't compile, the request will be
aborted. Apache::StatINC
shouldn't be used in production anyway (because it
slows things down by stat( ) ing all the files
listed in %INC), so this
shouldn't be a problem.
Note that we assume that the entire purpose of this function is to
reload the configuration if it was changed. This is fail-safe,
because if something goes wrong we just return without modifying the
server configuration. The script should not be used to initialize the
variables on its first invocation. To do that, you would need to
replace each occurrence of return( ) and
warn( ) with die( ).
We've used the above approach with a huge
configuration file that was loaded only at server startup and another
little configuration file that included only a few variables that
could be updated by hand or through the web interface. Those
variables were initialized in the main configuration file. If the
webmaster breaks the syntax of this dynamic file while updating it by
hand, it won't affect the main (write-protected)
configuration file and won't stop the proper
execution of the programs. In the next section, we will see a simple
web interface that allows us to modify the configuration file without
the risk of breaking it.