First, lets look at a simple case, in which we just have to look after a simple configuration file like the one below. Imagine a script that tells you who is the patch pumpkin of the current Perl release.[30] (Pumpkin is a whimsical term for the person with exclusive access to a virtual "token" representing a certain authority, such as applying patches to a master copy of some source.)
[30]These are the recent pumpkins: Chip Salzenberg for 5.004, Gurusamy Sarathy for 5.005 and 5.6, Jarkko Hietaniemi for 5.8, Hugo van der Sanden for 5.10.
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.
Example 6-24 shows a sample script using our reread_conf( )subroutine.
use vars qw(%MODIFIED $firstname $surname); use CGI ( ); use strict; my $q = CGI->new; print $q->header(-type => 'text/plain'); my $config_file = "./config.pl"; reread_conf($config_file); print $q->p("$firstname $surname holds the patch pumpkin" . "for this Perl release."); sub reread_conf { my $file = shift; return unless defined $file; return unless -e $file and -r _; my $mod = -M _; unless ($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 time } }
You should be using (stat $file)[9] instead of -M $file if you are modifying the $^T variable. This is because -M returns the modification time relative to the Perl interpreter startup time, set in $^T. In some scripts, it can be useful to reset $^T to the time of the script invocation with "local $^T = time( )". That way, -M and other -X file status tests are performed relative to the script invocation time, not the time the process was started.
If your configuration file is more sophisticated—for example, if it declares a package and exports variables—the above code will work just as well. Variables need not be import( )ed again: when do( ) recompiles the script, the originally imported variables will be updated with the values from the reloaded code.
 
Continue to: