The biggest single “gotcha” for
scripts running under
Apache::Registry
is caused by global variables.
The mod_cgi environment is rather kind to the
slack programmer. Your scripts, which tend to be short and simple,
get loaded, run, and then thrown away. Perl rather considerately
initializes all variables to undef
at startup, so
one tends to forget about the dangers they represent.
Unhappily, under mod_perl and
Apache::Registry
, scripts effectively run as
subroutines. Global variables get initialized at startup as usual,
but not again, so if you don’t explicitly initialize
them at each call, they will carry forward whatever value they had
after the last call. What makes these bugs more puzzling is that as
the Apache child processes start, each one of them has its variables
set to 0. The errant behavior will not begin to show until a child
process is used a second time — and maybe not even then.
There are several lines of attack:
Do away with every global variable that isn’t absolutely necessary
Make sure that every global variable that survives is initialized
Put your code into modules as subroutines and call it from the main script — for some reason global variables in the module will be initialized
To illustrate this tiresome behavior we created a new directory /usr/www/APACHE3/APACHE3/site.mod_perl/mod_perl and copied everything across into it from.../mod_cgi. The startup file go was now:
httpd.perl -d /usr/www/APACHE3/APACHE3/site.mod_perl/mod_perl
The Config file is as follows:
User webuser Group webuser ServerName www.butterthlies.com LogLevel debug DocumentRoot /usr/www/APACHE3/APACHE3/site.mod_perl/mod_cgi/htdocs TransferLog /usr/www/APACHE3/APACHE3/site.mod_perl/logs/access_log ErrorLog /usr/www/APACHE3/APACHE3/site.mod_perl/logs/error_log LogLevel debug #change to AliasMatch from ScriptAliasMatch AliasMatch /(.*) /usr/www/APACHE3/APACHE3/site.mod_perl/cgi-bin/$1 DirectoryIndex /bin/home Alias /bin /usr/www/APACHE3/APACHE3/site.mod_perl/cgi-bin SetHandler perl-script PerlHandler Apache::Registry #PerlHandler Apache::PerlRun
Notice that the convenient directives ScriptAlias
and ScriptAliasMatch
, which effectively
encapsulate an Alias
directive followed by
SetHandler
cgi-script
for use
under mod_cgi, are no longer available.
You have to declare an Alias
, then that you are
running perl-script
, and then what flavor, or
intensity of mod_perl you want.
The script home is now:
#! /usr/local/bin/perl -w use strict; print qq(content-type: text/html\n\n); my $global=0; for(1 .. 5) { &inc_g(); } print qq(<HTML><HEAD><TITLE>Demo CGI Home Page</TITLE></HEAD> <BODY>Hi: I'm a demo home page. Global = $global<BR> <A HREF="/AA_next">Click here to run my mate</A> </BODY></HTML>); sub inc_g() { $global+=1; print qq(global = $global<BR>); }
If you fire up Apache and watch the output, you don’t have to reload it many times (having turned off caching in your browser, of course) before you see the following unnerving display:
content-type: text/html global = 21 global = 22 global = 23 global = 24 global = 25 Hi: I'm a demo home page. Global = 0 Click here to run my mate
This unpleasant behavior is accompanied by the following message in the error_log file:
Variable "$global" will not stay shared at /usr/www/APACHE3/APACHE3/site.mod_perl/ cgi-bin/home
which should give you a pretty good warning that all is not well. If
you start Apache up using the -X
flag — to
prevent child processes — then the bad behavior begins on the
first reload.
It will not happen at all if you use the line:
PerlHandler Apache::PerlRun
because under PerlRun
, although Perl itself stays
loaded, your scripts are reloaded at each call — and, of course,
all the variables are initialized. There is a performance penalty, of
course.