It's a good idea to reread one of the previous articles that gives a Perl knowledge refreshment, particularly the section about use(), require(), do(), %INC and @INC.
When you develop plain CGI scripts, you can just change the code, and rerun the CGI from your browser. Since the script isn't cached in memory, the next time you call it the server starts up a new perl process, which recompiles it from scratch. The effects of any modifications you've applied are immediately present.
The situation is different with Apache::Registry, since the whole idea is to get maximum performance from the server. By
default, the server won't spend time checking whether any included library
modules have been changed. It assumes that they weren't, thus saving a few
milliseconds to stat() the source file (multiplied by however
many modules/libraries you use() and/or require()
in your script.)
The only check that is done is to see whether your main script has been
changed. So if you have only scripts which do not use() or
require() other perl modules or packages, there is nothing to
worry about. If, however, you are developing a script that includes other
modules, the files you use() or require() aren't
checked for modification and you need to do something about that.
So how do we get our modperl-enabled server to recognize changes in library modules? Well, there are a couple of techniques:
The simplest approach is to restart the server each time you apply some change to your code.
After restarting the server about 100 times, you will tire of it and you will look for other solutions.
Help comes from the Apache::StatINC module. When Perl pulls a file via require(), it stores the
full pathname as a value in the global hash %INC with the file name as the key. Apache::StatINC looks through %INC and it immediately reloads any files it finds in there if it sees that they
have been updated on disk.
To enable this module just add two lines to httpd.conf.
PerlModule Apache::StatINC PerlInitHandler Apache::StatINC
To be sure it really works, turn on debug mode on your development box by
adding PerlSetVar StatINCDebug On to your config file. You end up with something like this:
PerlModule Apache::StatINC
<Location /perl>
SetHandler perl-script
PerlHandler Apache::Registry
Options ExecCGI
PerlSendHeader On
PerlInitHandler Apache::StatINC
PerlSetVar StatINCDebug On
</Location>
Be aware that only the modules located in @INC are reloaded on change, and you can change @INC only before the server has been started (in the startup file).
Nothing you do in your scripts and modules which are pulled in with
require() after server startup will have any effect on @INC.
When you write:
use lib qw(foo/bar);
@INC is changed only for the time the code is being parsed and compiled. When
that's done, @INC is reset to its original value.
To make sure that you have set @INC correctly, configure
/perl-status location (Apache::Status, fetch http://www.example.com/perl-status?inc
and look at the bottom of the page, where the contents of @INC will be shown.
Notice the following trap:
While ``.'' is in @INC, perl knows to require() files with pathnames given relative
to the current (script) directory. After the script has been parsed, the
server doesn't remember the path!
So you can end up with a broken entry in %INC like this:
$INC{bar.pl} eq "bar.pl"
If you want Apache::StatINC to reload your script--modify @INC at server startup, or use a full path in the require() call.
Checking all the modules in %INC on every request can add a large overhead to server response times, and you
certainly would not want the Apache::StatINC module to be enabled in your production site's configuration. But sometimes
you want a configuration file reloaded when it is updated, without
restarting the server.
This is an especially important feature if for example you have a person that is allowed to modify some of the tool configuration, but for security reasons it's undesirable for him to telnet to the server to restart it.
Since we are talking about configuration files, I would like to show you some good and bad approaches to configuration file writing.
If you have a configuration file of just a few variables, it doesn't really matter how you do it. But generally this is not the case. Configuration files tend to grow as a project grows. It's very relevant to projects that generate HTML files, since they tend to demand many easily configurable parameters, like headers, footers, colors and so on.
So let's start with the approach that is most often taken by CGI scripts writers. All configuration variables are defined in a separate file.
For example:
$cgi_dir = "/home/httpd/perl"; $cgi_url = "/perl"; $docs_dir = "/home/httpd/docs"; $docs_url = "/"; $img_dir = "/home/httpd/docs/images"; $img_url = "/images"; ... many more config params here ... $color_hint = "#777777"; $color_warn = "#990066"; $color_normal = "#000000";
The use strict; pragma demands all the variables be declared. When we want to use these
variables in a mod_perl script we must declare them with use vars in the script.
So we start the script with:
use strict;
use vars qw($cgi_dir $cgi_url $docs_dir $docs_url
... many more config params here ....
$color_hint $color_warn $color_normal
);
It is a nightmare to maintain such a script, especially if not all the features have been coded yet. You have to keep adding and removing variable names. But that's not a big deal.
Since we want our code clean, we start the configuration file with
use strict; as well, so we have to list the variables with use
vars pragma here as well. A second list of variables to maintain.
If you have many scripts, you may get collisions between configuration files. One of the best solutions is to declare packages, with unique names of course. For example for our configuration file we might declare the following package name:
package My::Config;
The moment you add a package declaration and think that you are done, you
realize that the nightmare has just begun. When you have declared the
package, you cannot just require() the file and use the
variables, since they now belong to a different package. So you have either
to modify all your scripts to use a fully qualified notation like
$My::Config::cgi_url instead of just $cgi_url or to import the needed variables into any script that is going to use
them.
Since you don't want to do the extra typing to make the variables fully qualified, you'd go for importing approach. But your configuration package has to export them first. That means that you have to list all the variables again and now you have to keep at least three variable lists updated when you make some changes in the naming of the configuration variables. And that's when you have only one script that uses the configuration file, in the general case you have many of them. So now our example configuration file looks like this:
package My::Config;
use strict;
BEGIN {
use Exporter ();
@My::HTML::ISA = qw(Exporter);
@My::HTML::EXPORT = qw();
@My::HTML::EXPORT_OK = qw($cgi_dir $cgi_url $docs_dir $docs_url
... many more config params here ....
$color_hint $color_warn $color_normal);
}
use vars qw($cgi_dir $cgi_url $docs_dir $docs_url
... many more config params here ....
$color_hint $color_warn $color_normal
);
$cgi_dir = "/home/httpd/perl";
$cgi_url = "/perl";
$docs_dir = "/home/httpd/docs";
$docs_url = "/";
$img_dir = "/home/httpd/docs/images";
$img_url = "/images";
... many more config params here ...
$color_hint = "#777777";
$color_warn = "#990066";
$color_normal = "#000000";
And in the code:
use strict;
use My::Config qw($cgi_dir $cgi_url $docs_dir $docs_url
... many more config params here ....
$color_hint $color_warn $color_normal
);
use vars qw($cgi_dir $cgi_url $docs_dir $docs_url
... many more config params here ....
$color_hint $color_warn $color_normal
);
This approach is especially bad in the context of mod_perl, since exported variables add a memory overhead. The more variables exported the more memory you use. If we multiply this overhead by the number of servers we are going to run, we get a pretty big number which could be used to run a few more servers instead.
As a matter of fact things aren't so bad. You can group your variables, and
call the groups by special names called tags, which can later be used as
arguments to the import() or use() calls. You are
probably familiar with:
use CGI qw(:standard :html);
We can implement it quite easily, with help of
export_ok_tags() from
Exporter. For example:
BEGIN {
use Exporter ();
use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
@ISA = qw(Exporter);
@EXPORT = qw();
@EXPORT_OK = qw();
%EXPORT_TAGS = (
vars => [qw($fname $lname)],
subs => [qw(reread_conf untaint_path)],
);
Exporter::export_ok_tags('vars');
Exporter::export_ok_tags('subs');
}
You export subroutines exactly like variables, since what's actually being exported is a symbol. The definition of these subroutines is not shown here.
Notice that we didn't use export_tags(), as it exports the
variables automatically without user asking for them in first place, which
is considered a bad style. If a module automatically exports variables with
export_tags() you can avoid this by not exporting at all:
use My::Config ();
In your code you can now write:
use My::Config qw(:subs :vars);
Groups of group tags:
The :all tag from CGI.pm is a group tag of all other groups. It will require a little more effort
from you, but you can always save time by looking at the solution in the
code of CGI.pm. It's just a matter of a little code to expand all the groups recursively.
After going through the pain of maintaining a list of variables in a big project with a huge configuration file (more than 100 variables) and many files actually using them, I came up with a much simpler solution: keeping all the variables in a single hash, which is built from references to other anonymous scalars, arrays and hashes.
Now my configuration file looks like this:
package My::Config;
use strict;
BEGIN {
use Exporter ();
@My::Config::ISA = qw(Exporter);
@My::Config::EXPORT = qw();
@My::Config::EXPORT_OK = qw(%c);
}
use vars qw(%c);
%c =
(
dir => {
cgi => "/home/httpd/perl",
docs => "/home/httpd/docs",
img => "/home/httpd/docs/images",
},
url => {
cgi => "/perl",
docs => "/",
img => "/images",
},
color => {
hint => "#777777",
warn => "#990066",
normal => "#000000",
},
);
Good perl style suggests keeping a comma at the end of lists. That's because additional items tend to be added to the end of the list. If you keep that last comma in place, you don't have to remember to add one when you add a new item.
So now the script looks like this:
use strict;
use My::Config qw(%c);
use vars qw(%c)
print "Content-type: text/plain\r\n\r\n";
print "My url docs root: $c{url}{docs}\n";
Do you see the difference? The whole mess has gone, there is only one variable to worry about.
So far so good, but let's make it even better. I would like to get rid of
the Exporter stuff completely. I remove all the exporting code so my config file now
looks like:
package My::Config;
use strict;
use vars qw(%c);
%c =
(
dir => {
cgi => "/home/httpd/perl",
docs => "/home/httpd/docs",
img => "/home/httpd/docs/images",
},
url => {
cgi => "/perl",
docs => "/",
img => "/images",
},
color => {
hint => "#777777",
warn => "#990066",
normal => "#000000",
},
);
And the code:
use strict;
use My::Config ();
print "Content-type: text/plain\r\n\r\n";
print "My url docs root: $My::Config::c{url}{docs}\n";
Since we still want to save lots of typing, and since now we need to use a
fully qualified notation like $My::Config::c{url}{docs}, let's use a magical perl aliasing feature. I'll modify the code to be:
use strict;
use My::Config ();
use vars qw(%c);
*c = \%My::Config::c;
print "Content-type: text/plain\r\n\r\n";
print "My url docs root: $c{url}{docs}\n";
I have aliased the *c glob with \%My::Config::c, a reference to a hash. From now on, %My::Config::c and %c are the same hash and you can read from or modify either of them.
Just one last little point. Sometimes you see a lot of redundancy in the configuration variables, for example:
$cgi_dir = "/home/httpd/perl"; $docs_dir = "/home/httpd/docs"; $img_dir = "/home/httpd/docs/images";
Now if you want to move the base path "/home/httpd" into a new place, it demands lots of typing. Of course the solution is:
$base = "/home/httpd"; $cgi_dir = "$base/perl"; $docs_dir = "$base/docs"; $img_dir = "$docs_dir/images";
You cannot do the same trick with a hash, since you cannot refer to its values before the definition is finished. So this wouldn't work:
%c =
(
base => "/home/httpd",
dir => {
cgi => "$c{base}/perl",
docs => "$c{base}/docs",
img => "$c{base}{docs}/images",
},
);
But nothing stops us from adding additional variables, which are lexically
scoped with my(). The following code is correct.
my $base = "/home/httpd";
%c =
(
dir => {
cgi => "$base/perl",
docs => "$base/docs",
img => "$base/docs/images",
},
);
You have just learned how to make configuration files easily maintainable, and how to save memory by avoiding the export of variables into a script's namespace.
First, lets look at a simple case, when we just have to look after a simple configuration file like the one below. Imagine a script that tells you who is patch pumpkin of the current perl release.
Sidenote: <Pumpkin> A humorous term for the token (notional or real) that gives its possessor (the ``pumpking'' or the ``pumpkineer'') exclusive access to something, e.g. applying patches to a master copy of some source (for which the token is called the ``patch pumpkin'').
use CGI ();
use strict;
my $fname = "Larry";
my $lname = "Wall";
my $q = new CGI;
print $q->header(-type=>'text/html');
print $q->p("$fname $lname holds the patch pumpkin
for this perl release.");
The script has a hardcoded value for the name. It's very simple: initialize the CGI object, print the proper HTTP header and tell the world who is the current patch pumpkin.
When the patch pumpkin changes we don't want to modify the script.
Therefore, we put the $fname and $lname variables into a configuration file.
$fname = "Gurusamy"; $lname = "Sarathy"; 1;
Please 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 $fname
and $lname will override (or initialize if they weren't yet) 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()) by this technique.
You have started the server and everything is working properly. After a
while you decide to modify the configuration. How do you let your running
server know that the configuration was modified without restarting it?
Remember we are in production and server restarting can be quite expensive
for us. 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 force a reconfiguration of
the variables located in this file. We will call the function that reloads
the configuration reread_conf() and it accepts a single
argument, which is a relative path to the configuration file.
Apache::Registry calls 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. 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 $file;
return unless -e $file and -r _;
unless ($MODIFIED{$file} and $MODIFIED{$file} == -M _){
my $return;
unless ($return = do $file) {
warn "couldn't parse $file: $@" if $@;
warn "couldn't do $file: $!" unless defined $return;
warn "couldn't run $file" unless $return;
}
$MODIFIED{$file} = -M _; # Update the MODIFICATION times
}
} # end of reread_conf
When require(), use() or do()
operators successfully return, the file that was passed as an argument is
inserted into %INC (the key is the name of the file and the value the path to it).
Specifically, when Perl sees require() or use()
in the code, it first tests %INC
to see whether it's already there and thus loaded. If the test returns
true, Perl saves the overhead of code re-reading and re-compiling.
You generally don't notice with plain perl scripts, but in mod_perl it's
used all the time; after the first request served by a process all the
files loaded by require() stay in memory. If the file is
preloaded at server startup, even the first request doesn't have the
loading overhead.
We use do() to reload the code in this file and not
require() because while do() behaves almost
indentically 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. We don't want the whole service that uses that file to be broken, just
because of that. We trap the possible failure to do() the file
and ignore the changes, by the resetting the modification time. If
do() fails to load the file it might be a good idea to send an
email to the system administrator about the problem.
Notice however, that 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. So if the file wouldn't compile, the request will be aborted. Apache::StatINC shouldn't be used in production (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, as 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 occurence of
return() and warn() with die().
I used the above approach when I had 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 so stop the proper execution of the programs. Soon we will see a simple web interface which allows us to modify the configuration file without actually breaking it.
A sample script using the presented subroutine would be:
use vars qw(%MODIFIED $fname $lname);
use CGI ();
use strict;
my $q = new CGI;
print $q->header(-type=>'text/plain');
my $config_file = "./config.pl";
reread_conf($config_file);
print $q->p("$fname $lname holds the patch pumpkin
for this perl release.");
sub reread_conf{
my $file = shift;
return unless $file;
return unless -e $file and -r _;
unless ($MODIFIED{$file} and $MODIFIED{$file} == -M _){
my $return;
unless ($return = do $file) {
warn "couldn't parse $file: $@" if $@;
warn "couldn't do $file: $!" unless defined $return;
warn "couldn't run $file" unless $return;
}
$MODIFIED{$file} = -M _; # Update the MODIFICATION times
}
} # end of reread_conf
Remember that you should be using (stat $file)[9] instead of -M
$file if you are modifying the $^M variable. In some of my scripts, I reset $^M to the time of the script invocation with
"$^M = time()". That way I can perform -M and the similar (-A, -C) file status tests relative to the script invocation time, and not the
time the process was started.
If your configuration file is more sophisticated and it declares a package
and exports variables, the above code will work just as well. Even if you
think that you will have to import() variables again, when
do() recompiles the script the originally imported variables
get updated with the values from the reloaded code.
The CGI script below allows a system administrator to dynamically update a configuration file through the web interface. Combining this with the code we have just seen to reload the modified files, you get a system which is dynamically reconfigurable without server restart. Configuration can be performed from any machine having just a web interface (a simple browser connected to the Internet).
Let's say you have a configuration file like this:
package MainConfig;
use strict;
use vars qw(%c);
%c = (
name => "Larry Wall",
release => "5.000",
comments => "Adding more ways to do the same thing :)",
other => "More config values",
hash => { foo => "bar",
fooo => "barr",
},
array => [qw( a b c)],
);
You want to make the variables name, release and comments
dynamically configurable. You want to have a web interface with an input
form that allows you to modify these variables. Once modified you want to
update the configuration file and propagate the changes to all the
currently running processes. Quite a simple task.
Let's look at the main stages of the implementation. Create a form with preset current values of the variables. Let the administrator modify it and submit the changes. Validate the submitted information (numeric fields should carry numbers, literals--words, etc). Update the configuration file. Update the modified value in the memory of the current process. Present the form as before but with updated fields if any.
The only part that seems to be complicated to implement is a configuration file update, for a couple of reasons. If updating the file breaks it, the whole service won't work. If the file is very big and includes comments and complex data structures, parsing the file can be quite a challenge.
So let's simplify the task. If all we want is to updated a few variables, why don't we create a tiny configuration file with just those variables? It can be modified through the web interface and overwritten each time there is something to be changed. This way we don't have to parse the file before updating it. If the main configuration file is changed we don't care, we don't depend on it any more.
The dynamically updated variables are duplicated, they will be in the main file and in the dynamic file. We do this to simplify maintainance. When a new release is installed the dynamic configuration file won't exist at all. It will be created only after the first update. As we just saw, the only change in the main code is to add a snippet to load this file if it exists and was changed.
This additional code must be executed after the main configuration file has been loaded. That way the updated variables will override the default values in the main file.
# remember to run this code under taint mode
use strict;
use vars qw($q %c $dynamic_config_file %vars_to_change %validation_rules);
use CGI ();
use lib qw(.);
use MainConfig ();
*c = \%MainConfig::c;
$dynamic_config_file = "./config.pl";
# load the dynamic configuration file if exists, and override the
# default values from the main configuration file
do $dynamic_config_file if -e $dynamic_config_file and -r _;
# fields that can be changed and their titles
%vars_to_change =
(
'name' => "Patch Pumpkin's Name",
'release' => "Current Perl Release",
'comments' => "Release Comments",
);
%validation_rules =
(
'name' => sub { $_[0] =~ /^[\w\s\.]+$/; },
'release' => sub { $_[0] =~ /^\d+\.[\d_]+$/; },
'comments' => sub { 1; },
);
$q = new CGI;
print $q->header(-type=>'text/html'),
$q->start_html();
my %updates = ();
# We always rewrite the dynamic config file, so we want all the
# vars to be passed, but to save time we will only do checking
# of vars that were changed. The rest will be retrieved from
# the 'prev_foo' values.
foreach (keys %vars_to_change) {
# copy var so we can modify it
my $new_val = $q->param($_) || '';
# strip a possible ^M char (DOS/WIN)
$new_val =~ s/\cM//g;
# push to hash if was changed
$updates{$_} = $new_val
if defined $q->param("prev_".$_) and $new_val ne $q->param("prev_".$_);
}
# Note that we cannot trust the previous values of the variables
# since they were presented to the user as hidden form variables,
# and the user can mangle those. We don't care: it cannot do any
# damage, as we verify each variable by rules which we define.
# Process if there is something to process. Will be not called if
# it's invoked a first time to display the form or when the form
# was submitted but the values weren't modified (we know that by
# comparing with the previous values of the variables, which are
# the hidden fields in the form)
# process and update the values if valid
process_change_config(%updates) if %updates;
# print the update form
conf_modification_form();
# update the config file but first validate that the values are correct ones
#########################
sub process_change_config{
my %updates = @_;
# we will list here all the malformatted vars
my %malformatted = ();
print $q->b("Trying to validate these values<BR>");
foreach (keys %updates) {
print "<DT><B>$_</B> => <PRE>$updates{$_}</PRE>";
# now we have to handle each var to be changed very very carefully
# since this file goes immediately into production!
$malformatted{$_} = delete $updates{$_}
unless $validation_rules{$_}->($updates{$_});
} # end of foreach my $var (keys %updates)
# print warnings if there are any invalid changes
print $q->hr,
$q->p($q->b(qq{Warning! These variables were changed
but found malformed, thus the original
values will be preserved.})
),
join(",<BR>",
map { $q->b($vars_to_change{$_}) . " : $malformatted{$_}\n"
} keys %malformatted)
if %malformatted;
# Now complete the vars that weren't changed from the
# $q->param('prev_var') values
map { $updates{$_} = $q->param('prev_'.$_) unless exists $updates{$_}
} keys %vars_to_change;
# Now we have all the data that should be written into the dynamic
# config file
# escape single quotes "'" while creating a file
my $content = join "\n",
map { $updates{$_} =~ s/(['\])/\$1/g;
'$c{' . $_ . "} = '" . $updates{$_} . "';\n"
} keys %updates;
# now add '1;' to make require() happy
$content .= "\n1;";
# keep the dummy result in $r so it won't complain
eval {my $res = $content};
if ($@) {
print qq{Warning! Something went wrong with config file
generation!<P> The error was : <BR><PRE>$@</PRE>};
return;
}
print $q->hr;
# overwrite the dynamic config file
use Symbol ();
my $fh = Symbol::gensym();
open $fh, ">$dynamic_config_file.bak"
or die "Can't open $dynamic_config_file.bak for writing :$! \n";
flock $fh,2; # exclusive lock
seek $fh,0,0; # rewind to the start
truncate $fh, 0; # the file might shrink!
print $fh $content;
close $fh;
# OK, now we make a real file
rename "$dynamic_config_file.bak",$dynamic_config_file;
# rerun it to update variables in the current process! Note that
# it won't update the variables in other processes. A special
# code that watches the timestamps on the config file will do this
# work for each process. Since the next invocation will update the
# configuration anyway, why do we need to load it here? The reason
# is simple: we are going to fill the form's input fields with
# the updated data.
do $dynamic_config_file;
} # end sub process_change_config
##########################
sub conf_modification_form{
print $q->center($q->h3("Update Form"));
print $q->hr,
$q->p(qq{This form allows you to dynamically update the current
configuration. You don\'t need to restart the server in
order for changes to take an effect}
);
# set the previous settings in the form's hidden fields, so we
# know whether we have to do some changes or not
map {$q->param("prev_$_",$c{$_}) } keys %vars_to_change;
# raws for the table, go into the form
my @configs = ();
# prepare one textfield entries
push @configs,
map {
$q->td(
$q->b("$vars_to_change{$_}:"),
),
$q->td(
$q->textfield(-name => $_,
-default => $c{$_},
-override => 1,
-size => 20,
-maxlength => 50,
)
),
} qw(name release);
# prepare multiline textarea entries
push @configs,
map {
$q->td(
$q->b("$vars_to_change{$_}:"),
),
$q->td(
$q->textarea(-name => $_,
-default => $c{$_},
-override => 1,
-rows => 10,
-columns => 50,
-wrap => "HARD",
)
),
} qw(comments);
print $q->startform('POST',$q->url),"\n",
$q->center($q->table(map {$q->Tr($_),"\n",} @configs),
$q->submit('','Update!'),"\n",
),
map ({$q->hidden("prev_".$_, $q->param("prev_".$_))."\n" }
keys %vars_to_change), # hidden previous values
$q->br,"\n",
$q->endform,"\n",
$q->hr,"\n",
$q->end_html;
} # end sub conf_modification_form
Once updated the script generates a file like:
$c{release} = '5.6';
$c{name} = 'Gurusamy Sarathy';
$c{comments} = 'Perl rules the world!';
1;
If you want to reload a perlhandler on each invocation, the following trick will do it:
PerlHandler "sub { do 'MyTest.pm'; MyTest::handler(shift) }"
do() reloads MyTest.pm on every request.
Next month we will talk about name collisions with modules and libraries,
package name related issues, __END__ and __DATA__
tokens , output from system calls under mod_perl, using
format() and write() functions. And finally we
will talk about terminating requests and processes, mainly about the
exit() and child_terminate() functions.