Return-Path: Delivered-To: apmail-modperl-cvs-archive@apache.org Received: (qmail 41240 invoked by uid 500); 27 Apr 2001 16:57:31 -0000 Mailing-List: contact modperl-cvs-help@apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: list-post: Reply-To: dev@perl.apache.org Delivered-To: mailing list modperl-cvs@apache.org Received: (qmail 41193 invoked by uid 500); 27 Apr 2001 16:57:30 -0000 Delivered-To: apmail-modperl-site-cvs@apache.org Date: 27 Apr 2001 16:57:30 -0000 Message-ID: <20010427165730.41179.qmail@apache.org> From: sbekman@apache.org To: modperl-site-cvs@apache.org Subject: cvs commit: modperl-site/guide CHANGES browserbugs.html config.html control.html correct_headers.html dbm.html debug.html download.html help.html index.html index_long.html install.html intro.html mod_perl_guide.pdf.gz modules.html multiuser.html performance.html perl.html porting.html scenario.html snippets.html start.html strategy.html troubleshooting.html sbekman 01/04/27 09:57:30 Modified: guide CHANGES browserbugs.html config.html control.html correct_headers.html dbm.html debug.html download.html help.html index.html index_long.html install.html intro.html mod_perl_guide.pdf.gz modules.html multiuser.html performance.html perl.html porting.html scenario.html snippets.html start.html strategy.html troubleshooting.html Log: * dbm.pod: o updated "Flawed Locking Methods Which Must Not Be Used" with notes about lock upgrading (David Harris) * strategy.pod: o added a ref to a light and fast Boa webserver * scenario.pod: o cleared out the section on open proxying with mod_proxy (Eric Cholet) * multiuser.pod: o extended the "Virtual Servers Technologies" section with freevsd, freevmware, vmware and S/390 references. * snippets.pod: o removed the cache control section -- it's covered in the HTTP headers chapter. o subrequests and notes working together (Darren Chamberlain) * performance.pod: o "Interpolation vs List" update: wrongly used the 'concatenation' term instead of interpolation (Mark Summerfield) o "Interpolation, Concatenation or List" was rewritten o new: "Architecture Specific Compile Options" (Tim Bunce, Perrin Harkins, Greg Cope, Owen Williams, Vivek Khera, Steve Fink, James W Walden) * modules.pod: o Extended the docs of Apache::SubProcess module * porting.pod: o "using register_cleanup in startup.pl to register cleanup action at the server shutdown or restart" (Doug) * config.pod: o Cleared the item which was falsely stating that the globals defined in startup.pl cannot be seen by child process. (Richard Chen) * install.pod: o updated "Discovering Whether Some Option Was Configured": added Apache::MyConfig o debian/apt install notes updates (Neil Conway) o some callback hooks aren't enabled by ALL_HOOKS=1 (Neil Conway) * download.pod o update the location of mod_proxy_add_forward.c (Ask Bjorn Hansen) * help.pod o added a link to Andrew Ford's Apache and mod_perl pocket books. o added a link to take23.org o added a XS resources section o added a link to the scalable list archive o remove the mailing list post address, to make it easier of Ask to filter SPAM. * troubleshooting.pod o new: "exit signal Segmentation fault (11)" with mysql: (Matt Sergeant) o improved the docs of fixing a broken /dev/null * debug.pod o updated "gdb says there are no debugging symbols" -- a simpler technique to have the binary unstripped during make install. * Minor corrections: o debug.pod (Alexander Farber) o performance.pod (Marc Lehmann, Kees Vonk) o snippets.pod (Ime Smits) o porting.pod (Michele Beltrame) o config.pod (Surat Singh Bhati, Paul Cotter ) o control.pod (Aaron Johnson, Cliff Rayman, Yann Kerherv�) o modules.pod (Daniel Bohling) o install.pod (Kevin Swope, Jamie) Revision Changes Path 1.29 +105 -1 modperl-site/guide/CHANGES Index: CHANGES =================================================================== RCS file: /home/cvs/modperl-site/guide/CHANGES,v retrieving revision 1.28 retrieving revision 1.29 diff -u -r1.28 -r1.29 --- CHANGES 2001/01/11 13:48:14 1.28 +++ CHANGES 2001/04/27 16:57:09 1.29 @@ -2,7 +2,111 @@ ### mod_perl Guide CHANGES file ### ################################### -01.01.2001 ver 1.28 +04.26.2001 ver 1.29 + +* dbm.pod: + + o updated "Flawed Locking Methods Which Must Not Be Used" with notes + about lock upgrading (David Harris) + +* strategy.pod: + + o added a ref to a light and fast Boa webserver + +* scenario.pod: + + o cleared out the section on open proxying with mod_proxy (Eric Cholet) + +* multiuser.pod: + + o extended the "Virtual Servers Technologies" section with freevsd, + freevmware, vmware and S/390 references. + +* snippets.pod: + + o removed the cache control section -- it's covered in the HTTP + headers chapter. + + o subrequests and notes working together (Darren Chamberlain) + +* performance.pod: + + o "Interpolation vs List" update: wrongly used the 'concatenation' + term instead of interpolation (Mark Summerfield) + + o "Interpolation, Concatenation or List" was rewritten + + o new: "Architecture Specific Compile Options" (Tim Bunce, Perrin + Harkins, Greg Cope, Owen Williams, Vivek Khera, Steve Fink, James + W Walden) + +* modules.pod: + + o Extended the docs of Apache::SubProcess module + +* porting.pod: + + o "using register_cleanup in startup.pl to register cleanup action + at the server shutdown or restart" (Doug) + +* config.pod: + + o Cleared the item which was falsely stating that the globals + defined in startup.pl cannot be seen by child process. (Richard + Chen) + +* install.pod: + + o updated "Discovering Whether Some Option Was Configured": added + Apache::MyConfig + + o debian/apt install notes updates (Neil Conway) + + o some callback hooks aren't enabled by ALL_HOOKS=1 (Neil Conway) + +* download.pod + + o update the location of mod_proxy_add_forward.c (Ask Bjorn Hansen) + +* help.pod + + o added a link to Andrew Ford's Apache and mod_perl pocket books. + + o added a link to take23.org + + o added a XS resources section + + o added a link to the scalable list archive + + o remove the mailing list post address, to make it easier of Ask to + filter SPAM. + +* troubleshooting.pod + + o new: "exit signal Segmentation fault (11)" with mysql: (Matt + Sergeant) + + o improved the docs of fixing a broken /dev/null + +* debug.pod + + o updated "gdb says there are no debugging symbols" -- a simpler + technique to have the binary unstripped during make install. + +* Minor corrections: + + o debug.pod (Alexander Farber) + o performance.pod (Marc Lehmann, Kees Vonk) + o snippets.pod (Ime Smits) + o porting.pod (Michele Beltrame) + o config.pod (Surat Singh Bhati, Paul Cotter ) + o control.pod (Aaron Johnson, Cliff Rayman, Yann Kerherv�) + o modules.pod (Daniel Bohling) + o install.pod (Kevin Swope, Jamie) + + + +01.11.2001 ver 1.28 * Makefile.PL: fixed to generate a correct Makefile on Solaris: removed space/extra new line after some manually created targets 1.14 +1 -1 modperl-site/guide/browserbugs.html Index: browserbugs.html =================================================================== RCS file: /home/cvs/modperl-site/guide/browserbugs.html,v retrieving revision 1.13 retrieving revision 1.14 diff -u -r1.13 -r1.14 --- browserbugs.html 2001/01/11 13:48:14 1.13 +++ browserbugs.html 2001/04/27 16:57:09 1.14 @@ -157,7 +157,7 @@ Written by Stas Bekman.
Last Modified at 06/21/2000 +href="help.html#Contacting_me">Stas Bekman.
Last Modified at 06/22/2000

1.31 +10 -28 modperl-site/guide/config.html Index: config.html =================================================================== RCS file: /home/cvs/modperl-site/guide/config.html,v retrieving revision 1.30 retrieving revision 1.31 diff -u -r1.30 -r1.31 --- config.html 2001/01/11 13:48:14 1.30 +++ config.html 2001/04/27 16:57:09 1.31 @@ -75,7 +75,6 @@
  • The Sample Startup File
  • What Modules You Should Add to the Startup File and Why
  • The Confusion with use() in the Server Startup File -
  • The Confusion with Global Variables in the Startup File
  • Apache Configuration in Perl @@ -1056,19 +1055,21 @@

    -PerlSendHeader On tells the server to send an HTTP header to the browser on every script +PerlSendHeader On tells the server to send an HTTP headers to the browser on every script invocation. You will want to turn this off for nph (non-parsed-headers) scripts.

    The PerlSendHeader On setting invokes the Apache's ap_send_http_header() method after parsing the headers generated by the script. It is only meant -for CGI emulation, and to send the HTTP header it's always better either to -use the $q->header method from the CGI.pm module or to use the $r->send_http_header -method using the Apache Perl API. +for emulation of mod_cgi behavior with regard to headers.

    +To send the HTTP headers it's always better either to use the +$r->send_http_header method using the Apache Perl API or to use the $q->header method from the CGI.pm module. +

    + @@ -1359,7 +1360,7 @@

    PerlInitHandler changes its behaviour depending upon where it is used. In any case it is the first handler to be invoked in serving a request. If found outside any <Location>, -<Directory> or <Files> section, it is an alias for PerlPostReadRequestHandler. When outside any such section it is an alias for PerlHeaderParserHandler. +<Directory> or <Files> section (at the top level), it is an alias for PerlPostReadRequestHandler. When inside any such section it is an alias for PerlHeaderParserHandler.

    Starting from PerlHeaderParserHandler the requested URI has been mapped to a physical server pathname, and thus @@ -2371,9 +2372,8 @@ # Initialize the database connections for each child Apache::DBI->connect_on_init - ("DBI:mysql:$Match::Config::c{db}{DB_NAME}::$Match::Config::c{db}{SERVER}", - $Match::Config::c{db}{USER}, - $Match::Config::c{db}{USER_PASSWD}, + ("DBI:mysql:database=test;host=localhost", + "user","password", { PrintError => 1, # warn() on errors RaiseError => 0, # don't die on error @@ -2750,24 +2750,6 @@

    [ TOC ]


    -

    The Confusion with Global Variables in the Startup File

    -

    -PerlRequire allows you to execute code that preloads modules and performs other -functions. Imported or defined variables are visible in the scope of the -startup file. It is wrong to assume that global variables that were defined -in the startup file will be visible to child processes. - -

    -If you define or import variables in your scripts they will be visible -inside the child process which is running the script: but they will not be -shared between siblings. Remember that every script is running in a -specially (uniquely) named package - so it cannot access variables from -other packages unless it inherits from them or -use()'s them. - -

    -[ TOC ] -


    Apache Configuration in Perl

    With <Perl>...</Perl> sections, it is possible to configure your server entirely in Perl. @@ -4311,7 +4293,7 @@

    @@ -2768,7 +2769,7 @@ @@ -2837,7 +2838,7 @@ @@ -1283,7 +1283,7 @@
    Written by Stas Bekman.
    Last Modified at 01/11/2001 +href="help.html#Contacting_me">Stas Bekman.
    Last Modified at 03/20/2001

    1.31 +4 -3 modperl-site/guide/control.html Index: control.html =================================================================== RCS file: /home/cvs/modperl-site/guide/control.html,v retrieving revision 1.30 retrieving revision 1.31 diff -u -r1.30 -r1.31 --- control.html 2001/01/11 13:48:15 1.30 +++ control.html 2001/04/27 16:57:10 1.31 @@ -879,7 +879,8 @@ Please, bear with us. Thank you! }; return OK; - } + } + 1;
      sub UNIVERSAL::AUTOLOAD {
           my $class = shift;
      -    warn "$class can't \$UNIVERSAL::AUTOLOAD!\n";
      +    warn "$class can't \$UNIVERSAL::AUTOLOAD=$UNIVERSAL::AUTOLOAD!\n";
         }
    Written by Stas Bekman.
    Last Modified at 11/20/2000 +href="help.html#Contacting_me">Stas Bekman.
    Last Modified at 04/19/2001

    1.14 +1 -1 modperl-site/guide/correct_headers.html Index: correct_headers.html =================================================================== RCS file: /home/cvs/modperl-site/guide/correct_headers.html,v retrieving revision 1.13 retrieving revision 1.14 diff -u -r1.13 -r1.14 --- correct_headers.html 2001/01/11 13:48:15 1.13 +++ correct_headers.html 2001/04/27 16:57:10 1.14 @@ -292,7 +292,7 @@ use Date::Parse; # Date::Parse parses RCS format, Apache::Util::parsedate doesn't $Mtime ||= - Date::Parse::str2time(substr q$Date: 2001/01/11 13:48:15 $, 6); + Date::Parse::str2time(substr q$Date: 2001/04/27 16:57:10 $, 6); $r->set_last_modified($Mtime);
    Written by Stas Bekman.
    Last Modified at 11/20/2000 +href="help.html#Contacting_me">Stas Bekman.
    Last Modified at 11/21/2000

    1.18 +115 -6 modperl-site/guide/dbm.html Index: dbm.html =================================================================== RCS file: /home/cvs/modperl-site/guide/dbm.html,v retrieving revision 1.17 retrieving revision 1.18 diff -u -r1.17 -r1.18 --- dbm.html 2001/01/11 13:48:15 1.17 +++ dbm.html 2001/04/27 16:57:11 1.18 @@ -115,12 +115,11 @@

    There is a number of Perl interfaces to the major dbm implementations, to -list a few: DBM, NDBM, ODBM, GDBM, and SDBM. The original Perl module for Berkeley DB was DB_File, which was written +list a few: DB_File, NDBM_File, ODBM_File, GDBM_File, and SDBM_File. The original Perl module for Berkeley DB was DB_File, which was written to interface to Berkeley DB version 1.85. The newer Perl module for Berkeley DB is BerkeleyDB, which was written to interface to version 2.0 and subsequent releases. Because Berkeley DB version 2.X has a compatibility API for version 1.85, -you can (and should!) build -DB_File using version 2.X of Berkeley DB, although DB_File will still only support the 1.85 functionality. +you can (and should!) build DB_File using version 2.X of Berkeley DB, although DB_File will still only support the 1.85 functionality.

    Several different indexing algorithms (known also as access methods) can be @@ -363,6 +362,116 @@ inventing your own.

    +If your dbm file is used only in the read-only mode generally there is no +need for locking at all. If you access the dbm file in read/write mode, the +safest method is to tie() the dbm file after acquiring an +external lock and untie() before the lock is released. So to +access the file in shared mode (FLOCK_SH) one should following this +pseudo-code: + +

    + + + + + + + + + +
    +   + +
      flock FLOCK_SH <===== start critical section
      +  tie()
      +  read...
      +  untie()
      +  flock FLOCK_UN <===== end critical section
    +
    +

    +Similar for the exclusive (EX), write access: + +

    + + + + + + + + + +
    +   + +
      flock FLOCK_EX <===== start critical section
      +  tie()
      +  write...
      +  sync()
      +  untie()
      +  flock FLOCK_UN <===== end critical section
    +
    +

    +However you might want to save a few tie()/untie() calls if +the same request accesses the dbm file more than once. You should be +careful though. Based on the caching effect explained above, a process can +perform an atomic downgrade of an exclusive lock to a shared one without +re-tie()ing the file: + +

    + + + + + + + + + +
    +   + +
      flock FLOCK_EX <===== start critical section
      +  tie()
      +  write...
      +  sync()
      +                 <===== end critical section
      +  flock FLOCK_SH <===== start critical section
      +  read...
      +  untie()
      +  flock FLOCK_UN <===== end critical section
    +
    +

    +because it has the updated data in its cache. By atomic, we mean it's +ensured that the lock status gets changed, without any other process +getting an exclusive access in between. + +

    +If you can ensure that one process safely upgrades a shared lock with an +exclusive lock, one can save on tie()/untie(). But this +operation might lead to a dead-lock if two processes try to upgrade a +shared lock with exclusive at the same time. Remember that in order to +acquire an exclusive lock, all other processes need to release *all* locks. +If your OS locking implementation resolves this deadlock by denying one of +the upgrade requests, make sure your program handles that appropriately. +The process that were denied has to untie() the dbm file and +then ask for an exclusive lock. + +

    +A dbm file has always to be untie()'ed before the locking is +released (unless you do an atomic downgrade from exclusive to shared as we +have just explained). Remember that if at any given moment a process wants +to lock and access the dbm file it has to re-tie() this file, if it was +tied already. If this is not done, the integrity of the dbm file is not +ensured. + +

    +To conclude, the safest method of reading from dbm file is to lock the file +before tie()-ing it, untie() it before releasing +the lock, and in the case of write to call sync() before +untie()-ing it. + +

    [ TOC ]


    Locking Wrappers Overview

    @@ -383,13 +492,13 @@

  • -Tie::DB_LockFile -- DB_File wrapper that has the ability to lock and unlock the database while it is +Tie::DB_FileLock -- DB_File wrapper that has the ability to lock and unlock the database while it is being used. Avoids the tie-before-flock problem by simply re-tie-ing the database when you get or drop a lock. Because of the flexibility in dropping and re-acquiring the lock in the middle of a session, this can be used in a system that will work with long updates and/or reads. Refer to the -Tie::DB_LockFile manpage for more information. +Tie::DB_FileLock manpage for more information.

  • @@ -644,7 +753,7 @@

  • Written by Stas Bekman.
    Last Modified at 12/11/2000 +href="help.html#Contacting_me">Stas Bekman.
    Last Modified at 03/26/2001

    1.28 +103 -88 modperl-site/guide/debug.html Index: debug.html =================================================================== RCS file: /home/cvs/modperl-site/guide/debug.html,v retrieving revision 1.27 retrieving revision 1.28 diff -u -r1.27 -r1.28 --- debug.html 2001/01/11 13:48:15 1.27 +++ debug.html 2001/04/27 16:57:11 1.28 @@ -988,6 +988,48 @@ But in general case, you don't need to use the above setting.

    +If you use this setting and you would like to log when a request was +cancelled by a SIGPIPE in your Apache access_log, you must define a custom LogFormat in your httpd.conf, like so: + +

    + + + + + + + + + +
    +   + +
      PerlFixupHandler Apache::SIG
      +  LogFormat "%h %l %u %t \"%r\" %s %b %{SIGPIPE}e"
    +
    +

    +If the server has noticed that the request was cancelled via a +SIGPIPE, then the log line will end with 1, otherwise it will just be a dash. e.g.: + +

    + + + + + + + + + +
    +   + +
      127.0.0.1 - - [09/Jan/2001:10:27:15 +0100] 
      +  "GET /perl/stopping_detector.pl HTTP/1.0" 200 16 1
      +  127.0.0.1 - - [09/Jan/2001:10:28:18 +0100] 
      +  "GET /perl/test.pl HTTP/1.0"              200 10 -
    +
    +

    [ TOC ]


    Detecting Aborted Connections

    @@ -1215,84 +1257,6 @@ where 17 is a file descriptor of the opened access_log file

    -Let's see how can we make the code more general-purpose: - -

    -Apache::SIG helps us, use this configuration setting in -httpd.conf: - -

    - - - - - - - - - -
    -   - -
      PerlFixupHandler Apache::SIG
    -
    -

    -Now the following script doesn't need to check for aborted connections. - -

    - - - - - - - - - -
    -   - -
      my $r = shift;
      -  $r->send_http_header('text/plain');
      -  
      -  print "PID = $$\n";
      -  $r->rflush;
      -  while(1){
      -    $r->print("\0");
      -    $r->rflush;
      -    $i++;
      -    sleep 1;
      -  }
    -
    -

    -Apache::SIG installs the SIGPIPE handler, which stops the script's execution for us when it sees the broken -pipe. This setting affects all processes of course. - -

    -If you would like to log when a request was cancelled by a SIGPIPE in your -Apache access_log, you must define a custom LogFormat in your httpd.conf, like so: - -

    - - - - - - - - - -
    -   - -
      PerlFixupHandler Apache::SIG
      -  LogFormat "%h %l %u %t \"%r\" %s %b %{SIGPIPE}e"
    -
    -

    -If the server has noticed that the request was cancelled via a -SIGPIPE, then the log line will end with 1, otherwise it will just be a dash. - -

    [ TOC ]


    The Importance of Cleanup Code

    @@ -5268,7 +5232,7 @@ while loop), it gets blocked by some system call or because of a resource deadlock) or for some other reason. In order to fix the problem we need to learn what circumstances the process hangs in (detection), so we can -reproduce the problem and after than to discover why there is problem +reproduce the problem and after that to discover why there is problem (diagnostics).

    @@ -5561,7 +5525,7 @@

    If you get such reports constantly something is wrong with your web service and you should revise your code. Note that it's possible that your server -is being overloaded by more requests that it can handle, so the requests +is being overloaded by more requests than it can handle, so the requests are being queued and not processed for a while, which triggers the watchdog's alarm. If this is a case you may need to add more servers or more memory, or perhaps split your single machine across a cluster of @@ -6400,11 +6364,22 @@ will:

      -

    1. Add `-g' to EXTRA_CFLAGS -

    2. Turn on PERL_TRACE -

    3. Set PERL_DESTRUCT_LEVEL=2 -

    4. Link against libperld if -e -$Config{archlibexp}/CORE/libperld$Config{lib_ext} +

    5. +

      +Add `-g' to EXTRA_CFLAGS + +

    6. +

      +Turn on PERL_TRACE + +

    7. +

      +Set PERL_DESTRUCT_LEVEL=2 + +

    8. +

      +Link against libperld if -e $Config{archlibexp}/CORE/libperld$Config{lib_ext} +

    [ TOC ] @@ -6572,11 +6547,50 @@


    gdb says there are no debugging symbols

    +During make install Apache strips all the debugging symbols. To prevent this you should use --without-execstrip ./configure +option. So if you configure Apache via mod_perl, you should do: + +

    + + + + + + + + + +
    +   + +
      panic% perl Makefile.PL USE_APACI=1 \
      +    APACI_ARGS='--without-execstrip' [other options]
    +
    +

    +Alternatively you can copy the unstripped binary manually. For example we +did: + +

    + + + + + + + + + +
    +   + +
      panic# cp apache_1.3.17/src/httpd /home/httpd/httpd_perl/bin/httpd_perl
    +
    +

    As you know you need an unstripped executable to be able to debug it. While you can compile mod_perl with -g (or PERL_DEBUG=1), the Apache install strips the symbols.

    -Makefile.tmpl contains a line: +Makefile.tmpl contains a line:

    @@ -6594,7 +6608,8 @@

    -Removing the -s does the trick. +Removing the -s does the trick (If you cannot find it in +Makefile.tmpl do it directly in Makefile). Alternatively you rerun make and copy the unstripped httpd binary away.

    [ TOC ] @@ -7181,7 +7196,7 @@ Written by Stas Bekman.
    Last Modified at 01/09/2001 +href="help.html#Contacting_me">Stas Bekman.
    Last Modified at 02/19/2001

    1.16 +2 -2 modperl-site/guide/download.html Index: download.html =================================================================== RCS file: /home/cvs/modperl-site/guide/download.html,v retrieving revision 1.15 retrieving revision 1.16 diff -u -r1.15 -r1.16 --- download.html 2001/01/11 13:48:16 1.15 +++ download.html 2001/04/27 16:57:12 1.16 @@ -226,7 +226,7 @@ available from one of these URLs: http://modules.apache.org/search?id=124, ftp://ftp.netcetera.dk/pub/apache/mod_proxy_add_forward.c +HREF="http://develooper.com/code/mpaf/mod_proxy_add_forward.c">http://develooper.com/code/mpaf/mod_proxy_add_forward.c or http://www.cpan.org/authors/id/ABH/mod_proxy_add_forward.c, complete with instructions on how to compile it and whatnot. @@ -450,7 +450,7 @@ Written by Stas Bekman.
    Last Modified at 12/08/2000 +href="help.html#Contacting_me">Stas Bekman.
    Last Modified at 02/09/2001

    1.28 +65 -15 modperl-site/guide/help.html Index: help.html =================================================================== RCS file: /home/cvs/modperl-site/guide/help.html,v retrieving revision 1.27 retrieving revision 1.28 diff -u -r1.27 -r1.28 --- help.html 2001/01/11 13:48:16 1.27 +++ help.html 2001/04/27 16:57:12 1.28 @@ -140,10 +140,10 @@

    http://perl.apache.org -

  • mod_perl Source Garden project +

  • News and Resources

    -http://modperl.sourcegarden.org +Take23: News and Resources for the mod_perl world http://take23.org

  • mod_perl Books

  • mod_perl Guide

    @@ -221,20 +236,19 @@ HREF="http://perl.apache.org/dist/mod_perl_traps.html">http://perl.apache.org/dist/mod_perl_traps.html . -

  • mod_perl Quick Reference Card -

    -http://www.refcards.com (Reference -cards for Apache and other programs are available from this link) -

  • Articles
  • Imported Symbols and Memory Usage -
  • Concatenation or List +
  • Interpolation, Concatenation or List
  • Using Perl stat() Call's Cached Results @@ -216,6 +216,7 @@
  • -Dusemymalloc Perl Build Option +
  • Architecture Specific Compile Options @@ -3135,7 +3136,7 @@

    -We are going to run memory benchmarks on five different versions of the startup.pl file. We always preload these modules: +We always preload these modules:

    @@ -3153,7 +3154,10 @@ -

    +

    +We are going to run memory benchmarks on five different versions of the startup.pl file. + +

    option 1

    Leave the file unmodified. @@ -3228,8 +3232,13 @@ -

    +

    option 5

    +Options 2 and 4: using connect_on_init() and +install_driver(). + +

    +

    Here is the Apache::Registry test script that we have used:

    @@ -3284,7 +3293,7 @@

    -The script opens a connection to the database 'test' and issues a query to learn what tables the databases has. When the data is +The script opens a opens a connection to the database 'test' and issues a query to learn what tables the databases has. When the data is collected and printed the connection would be closed in the regular case, but Apache::DBI overrides it with empty method. When the data is processed a familiar to you already code to print the memory usage follows. @@ -3311,13 +3320,13 @@ -

      Version     Size   Shared     Diff        Test type
      -  --------------------------------------------------------------------
      -        1  3465216  2621440   843776  install_driver
      -        2  3461120  2609152   851968  install_driver & connect_on_init
      -        3  3465216  2605056   860160  preload driver
      -        4  3461120  2494464   966656  nothing added
      -        5  3461120  2482176   978944  connect_on_init
    +
      Test type                              Size   Shared     Diff
      +  --------------------------------------------------------------
      +  install_driver (2)                   3465216  2621440   843776
      +  install_driver & connect_on_init (5) 3461120  2609152   851968
      +  preload driver (3)                   3465216  2605056   860160
      +  nothing added (1)                    3461120  2494464   966656
      +  connect_on_init (4)                  3461120  2482176   978944
    @@ -3337,13 +3346,13 @@ -
      Version     Size   Shared    Diff         Test type
      -  --------------------------------------------------------------------
      -        1  3469312  2609152   860160  install_driver
      -        2  3481600  2605056   876544  install_driver & connect_on_init
      -        3  3469312  2588672   880640  preload driver
      -        4  3477504  2482176   995328  nothing added
      -        5  3481600  2469888  1011712  connect_on_init
    +
      Test type                              Size   Shared     Diff
      +  --------------------------------------------------------------
      +  install_driver (2)                   3469312  2609152   860160
      +  install_driver & connect_on_init (5) 3481600  2605056   876544
      +  preload driver (3)                   3469312  2588672   880640
      +  nothing added  (1)                   3477504  2482176   995328
      +  connect_on_init (4)                  3481600  2469888  1011712
    @@ -3357,15 +3366,15 @@

    But both tables show the same pattern of memory usage. We can clearly see -that the real winner is the startup.pl file's version where the MySQL driver was installed (1). Since we want to +that the real winner is the startup.pl file's version where the MySQL driver was installed (2). Since we want to have a connection ready for the first request made to the freshly spawned -child process, we generally use the second version (2) which uses somewhat -more memory, but has almost the same number of shared memory pages. The -third version only preloads the driver which results in smaller shared -memory. The last two versions having nothing initialized (4) and having -only the connect_on_init() method used (5). The former is a -little bit better than the latter, but both significantly worse than the -first two versions. +child process, we generally use the version (5) which uses somewhat more +memory, but has almost the same number of shared memory pages. The version +(3) only preloads the driver which results in smaller shared memory. The +last two versions having nothing initialized (1) and having only the +connect_on_init() method used (4). The former is a little bit +better than the latter, but both significantly worse than the first two +versions.

    To remind you why do we look for the smallest value in the column @@ -3391,9 +3400,9 @@ Notice that the smaller the diff is, the bigger the number of processes you can have using the same amount of RAM. Therefore every 100K difference counts, when you multiply it by the number of processes. If we take the -number from the version version (1) vs. (4) and assume that we have 256M of -memory dedicated to mod_perl processes we will get the following numbers -using the formula derived from the above formula: +number from the version (2) vs. (4) and assume that we have 256M of memory +dedicated to mod_perl processes we will get the following numbers using the +formula derived from the above formula:

    @@ -3423,7 +3432,7 @@

                    268435456 - 2609152
      -  (ver 1)  N =  ------------------- = 309
      +  (ver 2)  N =  ------------------- = 309
                             860160
    @@ -3440,7 +3449,7 @@
                    268435456 - 2469888
      -  (ver 5)  N =  ------------------- = 262
      +  (ver 4)  N =  ------------------- = 262
                            1011712
    @@ -4143,7 +4152,7 @@ Note that you cannot localize this setting with local(). If you do, it won't have the desired effect.

    -[META: Anyone can explain why localization doesn't work?] +[META: Can anyone explain why localization doesn't work?]

    So now the code would look like this: @@ -4318,8 +4327,8 @@

    The HTTP header is sent next, with the Content-type of -text/plain. The gets ready to ignore the child, to avoid zombies and the fork is -called. +text/plain. The parent process gets ready to ignore the child, to avoid zombies and +the fork is called.

    The program gets its personality split after fork and the if conditional @@ -5566,7 +5575,7 @@

                     500
      - MaxClients = --------- = 50
      +  MaxClients = --------- = 50
                        10
    @@ -5591,24 +5600,11 @@
      Total_RAM            = 500Mb
         Max_Process_Size     =  10Mb
      -  Shared_RAM_per_Child =   8Mb
    - - - - -

    - - - - - - - @@ -5870,7 +5866,7 @@ You might want to consider enabling this option if the client's browser needs to request more than one object from your server for a single HTML page. If this is the situation the by setting -KeepAliveOff then for each page you save the HTTP connection overhead for all requests +KeepAliveOn then for each page you save the HTTP connection overhead for all requests but the first one.

    @@ -7718,6 +7714,48 @@

    -   - -
                  500 - 8
      - MaxClients = --------- = 246
      -               10 - 8
    + Shared_RAM_per_Child = 8Mb + + 500 - 8 + MaxClients = --------- = 246 + 10 - 8

    +Imported symbols act just like global variables, they can add up quick: + +

    + + + + + + + + + +
    +   + +
      % perlbloat.pl 'use POSIX ()'
      +  use POSIX () added  316k
    +
    +

    + + + + + + + + + +
    +   + +
      % perlbloat.pl 'use POSIX'
      +  use POSIX added  696k
    +
    +

    +That's 380k worth of aliases. Now let's say 6 different +Apache::Registry scripts 'use POSIX;' for strftime() or some other function: 6 * 380k = 2.3Mb + +

    +One could save 2.3Mb per single process with 'use POSIX ();' and using fully qualifying POSIX:: function calls. + +

    [ TOC ]


    Object Methods Calls vs. Function Calls

    @@ -8209,13 +8247,19 @@

    [ TOC ]


    -

    Concatenation or List

    +

    Interpolation, Concatenation or List

    -When the strings are small, it's almost doesn't matter whether a -concatination or a list is used: +Somewhat overlapping with the previous section we want to revisit the +various approaches of mungling with strings, and compare the speed of using +lists of strings compared to interpolatoin. We will add a string +concatenation angle as well.

    +When the strings are small, it almost doesn't matter whether interpolation +or a list is used. Here is a benchmark: +

    + @@ -8232,26 +8276,46 @@ my($one, $two, $three, $four) = ('a'..'d'); timethese(1_000_000, - { - concat => sub { - print $fh "$one$two$three$four"; - }, - list => sub { - print $fh $one, $two, $three, $four; - }, - }); + { + interp => sub { + print $fh "$one$two$three$four"; + }, + list => sub { + print $fh $one, $two, $three, $four; + }, + conc => sub { + print $fh $one.$two.$three.$four; + }, + });

    -Benchmark: timing 1000000 iterations of concat, list... concat: 5 wallclock -secs ( 4.76 usr + 0.00 sys = 4.76 CPU) list: 4 wallclock secs ( 4.30 usr + -0.00 sys = 4.30 CPU) -

    -When the strings are big lists are faster: + + + + + + + +
    +   + +
     Benchmark: timing 1000000 iterations of conc, interp, list...
      +      conc:  3 wallclock secs ( 3.38 usr +  0.00 sys =  3.38 CPU)
      +    interp:  3 wallclock secs ( 3.45 usr + -0.01 sys =  3.44 CPU)
      +      list:  2 wallclock secs ( 2.58 usr +  0.00 sys =  2.58 CPU)
    +
    +

    +The concatenation technique is very similar to interpolation. The list +technique is a little bit faster than interpolation. But when the strings +are large, lists are significantly faster. We have seen this in the +previous section and here is another benchmark to increase our confidence +in our conclusion. This time we use 1000 character long strings: +

    @@ -8270,20 +8334,48 @@ my($one, $two, $three, $four) = map { $_ x 1000 } ('a'..'d'); timethese(500_000, - { - concat => sub { - print $fh "$one$two$three$four"; - }, - list => sub { - print $fh $one, $two, $three, $four; - }, - }); + { + interp => sub { + print $fh "$one$two$three$four"; + }, + list => sub { + print $fh $one, $two, $three, $four; + }, + conc => sub { + print $fh $one.$two.$three.$four; + }, + }); + + + +
    +

    + + + + + + +
    +   +
      Benchmark: timing 500000 iterations of interp, list...
      +      conc:  5 wallclock secs ( 4.47 usr +  0.27 sys =  4.74 CPU)
      +    interp:  4 wallclock secs ( 4.25 usr +  0.26 sys =  4.51 CPU)
      +      list:  4 wallclock secs ( 2.87 usr +  0.16 sys =  3.03 CPU)
    +

    +In this case using a list is about 30% faster than interpolation. +Concatenation is a little bit slower than interpolation. + +

    +Let's look at this code: +

    + @@ -8292,24 +8384,31 @@
    -
      Benchmark: timing 500000 iterations of concat, list...
      -      concat: 10 wallclock secs ( 9.04 usr +  0.53 sys =  9.57 CPU)
      -        list:  6 wallclock secs ( 5.72 usr +  0.56 sys =  6.28 CPU)
    +
        $title = 'My Web Page';
      +    print "<h1>$title</h1>";         # Interpolation (slow)
      +    print '<h1>' . $title . '</h1>'; # Concatenation (slow)
      +    print '<h1>', $title, '</h1>';   # List (fast for long strings)

    -A list is almost 50% faster than concatenation. +When you use "<h1>$title</h1>" Perl does interpolation (since "" is an operator in Perl), which must parse the contents of the string and +replace any variables or expressions it finds with their respective values. +This uses more memory and is slower than using a list. Of course if there +are no variables to interpolate it makes no difference whether to use "string" or +'string'.

    -Also when you use "string" you use interpolation (since "" is an operator in Perl), which turns into concatination, which uses more -memory and is slower than using a list. When you use 'string' there is no interpolation, therefore it's faster and you have to use a list -if you need to pass more than one argument. +Concatenation is also potentially slow since Perl might create a temporary +string which it then prints.

    -There will be exceptions, like "string\n" where you cannot use single quotes. But if you do 'string',"\n" readability gets hurt. And we want want our code to be readable and -maintainable. +Lists are fast because Perl can simply deal with each element in turn. This +is true if you don't run join() on the list at the end to +create a single string from the elements of list. This operation might be +slower than direct append to the string whenever a new string springs into +existance.

    [ReaderMETA]: Please send more mod_perl relevant Perl performance hints @@ -8734,7 +8833,7 @@

      $sth->execute;
      -  while(@row_ary  = $sth->fetchrow_array;) {
      +  while(@row_ary  = $sth->fetchrow_array) {
               # do DB accumulation into some variable
         }
         # print the output using the the data returned from the DB
    @@ -10585,6 +10684,64 @@ configure Perl with -Dusemymalloc. Perl's malloc is not much of a win under linux, but makes a huge difference under Solaris. +

    +[ TOC ] +


    +

    Architecture Specific Compile Options

    +

    +When you build Apache and Perl you can optimize the compiled applications +to take the benefits of your machine's architecture. + +

    +Everything depends on the kind of compiler that you use, the kind of CPU +and + +

    +For example if you use gcc(1) you might want to use: + +

      +

    • +

      +-march=pentium if you have a pentium CPU + +

    • +

      +-march=pentiumpro for pentiumpro and above (but the binary won't run on i386) + +

    • +

      +-fomit-frame-pointer makes extra register available but disables debugging + +

    • +

      +you can try these options were reported to improve the performance: +-ffast-math, -malign-double, -funroll-all-loops, +-fno-rtti, -fno-exceptions. + +

      +see the gcc(1) manpage for the details about these + +

    • +

      +and of course you may want to change the usually default -02 flag with a higher number like -O3. -OX (where X is a number between 1 and 6) defines a collection of various +optimization flags, the higher the number the more flags are bundled. The +gcc man page will tell you what flags are used for each number. + +

    +

    +Test your applications thoroughly when you change the default optimization +flags, especially when you go beyond -02. It's possible that the optimization will make the code work incorrectly +and/or cause segmentation faults. + +

    +See your preferred compiler's man page for detailed information about +optimization. + +

    +Also see: http://members.nbci.com/Alex_Maranda/gnuintel/GNUintel.htm + + [ TOC ]


    @@ -10638,7 +10795,7 @@ Written by Stas Bekman.
    Last Modified at 01/11/2001 +href="help.html#Contacting_me">Stas Bekman.
    Last Modified at 04/28/2001

    1.18 +10 -14 modperl-site/guide/perl.html Index: perl.html =================================================================== RCS file: /home/cvs/modperl-site/guide/perl.html,v retrieving revision 1.17 retrieving revision 1.18 diff -u -r1.17 -r1.18 --- perl.html 2001/01/11 13:48:21 1.17 +++ perl.html 2001/04/27 16:57:20 1.18 @@ -162,11 +162,6 @@ frequent pure Perl questions being asked at the list.

    -Update: I'm moving most of the pure Perl related topics from everywhere in -the Guide to this chapter. From now on other chapters will refer to -sections in this chapter if required. - -

    Before you decide to skip this chapter make sure you know all the information provided here. The rest of the Guide assumes that you have read this chapter and understood it. @@ -182,13 +177,15 @@ fingertips is a great time saver. It always has the up-to-date information for the version of perl you're using. +

    +Of course you can use online Perl documentation at the Web. The two major +sites are http://www.perldoc.com and +http://theoryx5.uwinnipeg.ca/CPAN/perl/. + +

    -Of course you can use online Perl documentation at the Web. I prefer http://theoryx5.uwinnipeg.ca/CPAN/perl/ -to the official URL: http://www.perl.com/pub/v/documentation -is very slow :( . The -perldoc utility provides you with access to the documentation installed on your +The perldoc utility provides you with access to the documentation installed on your system. To find out what Perl manpages are available execute:

    @@ -1832,8 +1829,7 @@

    -The approach given above is generally not recommended because most Perl -programmers will not expect $counter to be changed by the function; the example where we used \$counter, i.e. pass-by-reference would be preferred. +The approach given above should be properly documented of course.

    Here is a solution that avoids the problem entirely by splitting the code @@ -4690,7 +4686,7 @@ Written by Stas Bekman.
    Last Modified at 11/26/2000 +href="help.html#Contacting_me">Stas Bekman.
    Last Modified at 04/10/2001

    1.33 +62 -14 modperl-site/guide/porting.html Index: porting.html =================================================================== RCS file: /home/cvs/modperl-site/guide/porting.html,v retrieving revision 1.32 retrieving revision 1.33 diff -u -r1.32 -r1.33 --- porting.html 2001/01/11 13:48:21 1.32 +++ porting.html 2001/04/27 16:57:20 1.33 @@ -415,11 +415,30 @@ Firstly, the file /tmp/db.out was written, with a complete trace of the code that was executed.

    -Secondly, error_log now contains the real code that was actually executed. This is produced as -a side effect of reporting the -Variable "$counter" will not stay shared at... warning that we saw earlier. +Secondly, if you have loaded the Carp module already, error_log +now contains the real code that was actually executed. This is produced as +a side effect of reporting the Variable "$counter" will +not stay shared at... warning that we saw earlier. To load the Carp module, you can add:

    + + + + + + + + + +
    +   + +
      use Carp;
    +
    +

    +in your startup.pl file or in the executed code. + +

    Here is the code that was actually executed:

    @@ -484,11 +503,10 @@ doesn't occur.

    -For example if we move wrap the code from the script into the subroutine thecode, place the both subroutines into the the -mylib.pl file, save it in the same directory as the script itself and +For example if we move the code from the script into the subroutine +run, place the subroutines into the mylib.pl file, save it in the same directory as the script itself and require() it, there will be no problem at all. (Don't forget -the -1; at the end of the library or the require() might fail.) +the 1; at the end of the library or the require() might fail.)

    @@ -1337,8 +1355,7 @@


    Availability

    -This module is available from CPAN. In one of the next mod_perl releases, it should become a part of the CORE -distribution. +This module is available from CPAN.

    [ TOC ] @@ -4219,8 +4236,8 @@ pass them to $r->send_http_header. This happens in src/modules/perl/Apache.xs (print) and Apache/Apache.pm (send_cgi_header). There is a shortcut in there, namely the assumption that each print statement contains one or more complete headers. If for example you -generate a -Set-Cookie header by multiple print() statements, like this: +generate a Set-Cookie +header by multiple print() statements, like this:

    @@ -4586,6 +4603,32 @@ The last paragraph is very important for handling the case of 'User Pressed the Stop Button'.

    +If you only want something to run once in the parent on shutdown or restart +you can use $r->register_cleanup() in the startup.pl. + +

    + + + + + + + + + +
    +   + +
      #PerlRequire startup.pl
      +  warn "parent pid is $$\n";
      +  Apache->server->register_cleanup
      +    (sub { warn "server cleanup in $$\n"});
    +
    +

    +This is usually useful when some server wide cleanup should be performed +when the server is stopped or restarted. + +

    [ TOC ]


    CHECK Blocks

    @@ -5013,7 +5056,7 @@ -
      local $^T=time;
    +
      local $^T = time;
    @@ -5062,6 +5105,11 @@

    +This technique is better performance-wise as it skips the +time() system call, and uses the already available time of the +request has been started at via $r->request_time method. + +

    [ TOC ]


    Apache and syslog

    @@ -5654,7 +5702,7 @@ Now we are going to modify the script itself. We copy the content to the file Cookie.pm and place it into one of the directories listed in @INC. For example if /home/httpd/perl is a part of @INC and since we want to call this package Test::Cookie, we can put -/Cookie.pm into the /home/httpd/perl/Test/ directory. +Cookie.pm into the /home/httpd/perl/Test/ directory.

    So this is the new code. Notice that all the subroutines were left @@ -6313,7 +6361,7 @@ Written by Stas Bekman.
    Last Modified at 12/15/2000 +href="help.html#Contacting_me">Stas Bekman.
    Last Modified at 04/17/2001

    1.31 +4 -57 modperl-site/guide/scenario.html Index: scenario.html =================================================================== RCS file: /home/cvs/modperl-site/guide/scenario.html,v retrieving revision 1.30 retrieving revision 1.31 diff -u -r1.30 -r1.31 --- scenario.html 2001/01/11 13:48:22 1.30 +++ scenario.html 2001/04/27 16:57:20 1.31 @@ -2260,64 +2260,11 @@

    Security Issues

    Whenever you use mod_proxy you need to make sure that your server will not -become a proxy for free riders. To block this you should have this setting: +become a proxy for free riders. Allowing clients to issue proxy requests is +controlled by the ProxyRequests directive. Its default setting is off, which means proxy requests are handled only if generated internally (by ProxyPass or RewriteRule...[P] +directives.) Do not use the ProxyRequests directive on your reverse proxy servers.

    - - - - - - - - - -
    -   - -
      RewriteRule ^proxy:.*  -  [F]
    -
    -

    -which makes sure that requests of type proxy:http://www.example.com -wouldn't keep your processes busy and will return the status Forbidden. - -

    -Start by testing your own server, by telnetting to the port the server is -listening on and issuing an external proxy request: - -

    - - - - - - - - - -
    -   - -
      % telnet www.example.com 80
      -Trying 128.9.176.32...
      -Connected to www.example.com
      -Escape character is '^]'.
      -HEAD http://www.example.org/ HTTP/1.1
      -Host: www.example.org
    -
    -

    -HTTP/1.0 403 Forbidden Date: Mon, 10 Apr 2000 08:42:31 GMT Server: -Apache/1.3.13-dev (Unix) Connection: close Content-Type: text/html; -charset=iso-8859-1 - -

    -Connection closed by foreign host. - -

    -As you can see we are not allowed to make a proxy request to a different -server, so our lock down has worked. It means that this particular hole has been secured on our box. - -

    [ TOC ]


    Buffering Feature

    @@ -3396,7 +3343,7 @@ Written by Stas Bekman.
    Last Modified at 12/25/2000 +href="help.html#Contacting_me">Stas Bekman.
    Last Modified at 03/11/2001

    1.29 +317 -308 modperl-site/guide/snippets.html Index: snippets.html =================================================================== RCS file: /home/cvs/modperl-site/guide/snippets.html,v retrieving revision 1.28 retrieving revision 1.29 diff -u -r1.28 -r1.29 --- snippets.html 2001/01/11 13:48:23 1.28 +++ snippets.html 2001/04/27 16:57:21 1.29 @@ -35,12 +35,8 @@
    • Redirecting Errors to the Client Instead of error_log -
    • Emulating the Authentication Mechanism -
    • Caching POSTed Data -
    • Cache Control for Regular and Error Modes -
    • Convert a POST Request into a GET Request -
    • Redirect a POST Request, Forwarding the Content -
    • Reading POST Data, then Redirecting or Doing Something Else +
    • Reusing Data from POST request +
    • Redirecting POST Requests
    • Redirecting While Maintaining Environment Variables
    • Terminating a Child Process on Request Completion
    • More on Relative Paths @@ -56,12 +52,13 @@
    • Subclassing Apache::Request
    • Sending Email from mod_perl
    • A Simple Handler To Print The Environment Variables -
    • mod_rewrite Based On Query String and URI Implemented in Perl -
    • PerlTransHandler example +
    • mod_rewrite in Perl +
    • URI Rewrite in PerlTransHandler
    • Setting PerlHandler Based on MIME Type
    • SSI and Embperl - Doing Both
    • Getting the Front-end Server's Name in the Back-end Server
    • Authentication Snippets +
    • Emulating the Authentication Mechanism
    • An example of using Apache::Session::DBI with cookies
    • Using DESTROY to Finalize Output
    • Setting Environment Variables For Scripts Called From CGI. @@ -302,9 +299,22 @@ Have you pressed a 'STOP' button?<BR> Please try again!<P> Thank you! - }; - - } else { + }; + + + + +

      + + + + + + + + + +
      +   + +
          } else {
           
               $message = qq{
                           <B>You need take no action since
        @@ -335,10 +345,23 @@
             An error has happened:
           
             $orig_why
        -  
        -      |;
           
        -        # send error reports to admin 
        +      |;
      +
      +

      + + + + + + + - - -
      +   + +
              # send error reports to admin 
               send_mail($admin_email,$admin_email,$subject,$body);
               print STDERR "[".scalar localtime()."] [SIGDIE] Sending Error Email\n";
             }
        @@ -366,53 +389,24 @@
         

      [ TOC ]


      -

      Emulating the Authentication Mechanism

      -

      -You can provide your own mechanism to authenticate users, instead of the -standard one. If you want to make Apache think that the user was -authenticated by the standard mechanism, set the username with: - +

      Reusing Data from POST request

      - - - - - - - - - -
      -   - -
        $r->connection->user('username');
      -
      -

      -Now you can use this information for example during the logging, so that -you can have your ``username'' passed as if it was transmitted to Apache -through HTTP authentication. - -

      -[ TOC ] -


      -

      Caching POSTed Data

      -

      What happens if you need to access the POSTed data more than once, say to reuse it on subsequent requests? POSTed data comes directly from the socket, and at the low level data can only be read from a socket once. So you have to store it to make it available for reuse.

      -There is an experimental option for Makefile.PL called -PERL_STASH_POST_DATA. If you turn it on, you can get at it again with $r->subprocess_env("POST_DATA"). This is not on by default because it adds overhead. +There is an experimental option for Makefile.PL called +PERL_STASH_POST_DATA. If you turn it on, you can get at it again with $r->subprocess_env("POST_DATA"). This is not enabled +by default because it adds a processing overhead for each POST request.

      -And what do we do with large multipart file uploads? Because POST +But what do we do with large multipart file uploads? Because POST data is not all read in one clump, it's a problem that's not easy to solve -in a general way. You might try the following approach: - -

      -In httpd.conf: +in a general way. A transparent way to do this is to switch the request +method from POST to GET, and store the POST data in the query string. This +handler does exactly this:

      @@ -424,51 +418,30 @@

      -
        <Limit POST>
        -     PerlFixupHandler    My::fixup_handler
        -  </Limit>
      -
      -

      -In your script: - -

      - - - - - - -
      -   - -
        use Apache::Constants;
        -  sub My::fixup_handler {
        +	  
        Apache/POST2GET.pm
        +  ------------------
        +  package Apache::POST2GET;
        +  use Apache::Constants qw(M_GET OK DECLINED);
        +  
        +  sub handler {
             my $r = shift;
             return DECLINED unless $r->method eq "POST";
             $r->args(scalar $r->content);
        -    $r->method("GET");
        +    $r->method('GET');
             $r->method_number(M_GET);
             $r->headers_in->unset('Content-length');
        -    OK;
        -  }
      + return OK; + } + 1; + __END__

      -Now when CGI.pm, Apache::Request or whatever parses the client data, it can do so more than once since $r->args doesn't go away (unless you make it go away). +In httpd.conf add:

      -[ TOC ] -


      -

      Cache Control for Regular and Error Modes

      -

      -To disable caching you should use the headers: - -

      @@ -478,14 +451,13 @@
      -
        Pragma: no-cache
        -  Cache-control: no-cache
      +
        PerlInitHandler Apache::POST2GET

      -For normally generated response use: +or even this:

      @@ -497,45 +469,31 @@ -

        $r->header_out("Pragma","no-cache");
        -  $r->header_out("Cache-control","no-cache");
        -  $r->no_cache(1);
      +
        <Limit POST>
        +    PerlInitHandler Apache::POST2GET
        +  </Limit>

      -If for some reason you need to use them in Error control code use: +To save a few more cycles, so the handler will be called only for POST +requests.

      +Effectively, this trick turns the POST request into a GET request +internally. Now when CGI.pm, Apache::Request or whatever module parses the client data, it can do so more than once +since +$r->args doesn't go away (unless you make it go away by resetting it). - - - - - - - - -
      -   - -
        $r->err_header_out("Pragma","no-cache");
        -  $r->err_header_out("Cache-control","no-cache");
      -
      -

      -META: $r->no_cache(1); ? -

      [ TOC ]


      -

      Convert a POST Request into a GET Request

      -

      -Remember that you can only read POST data from the socket once. If you need to use it more than once, you need to save it somewhere. - +

      Redirecting POST Requests

      -A transparent way to do this is to switch the request method from POST to -GET, and store the POST data in the query string: +Under mod_cgi it's not easy to redirect POST requests to some other +location. With mod_perl you can easily redirect POST requests. All you have +to do is read in the content, set the method to GET, populate args() with the content to be forwarded and finally do the redirect:

      @@ -547,61 +505,8 @@ -

        package Apache::POST2GET;
        -  use Apache::Constants qw(M_GET);
        -  
        -  sub handler {
        -    my $r = shift;
        -    if ($r->method eq 'POST') { 
        -       my $content = $r->content;
        -       $r->args($content);
        -       $r->method('GET');
        -       $r->method_number(M_GET);
        -       $r->headers_in->unset('Content-length');
        -    }
        -  }
        -  __END__
      - - - - -

      -Then add this directive to httpd.conf: - -

      - - - - - - - - - -
      -   - -
        PerlInitHandler Apache::POST2GET
      -
      -

      -[ TOC ] -


      -

      Redirect a POST Request, Forwarding the Content

      -

      -With mod_perl you can easily redirect a POST request to some other -location. All you have to do is read in the contents, set the method to GET, populate args() with the content to be forwarded and finally do the redirect: - -

      - - - - - - - @@ -1016,7 +890,9 @@
      -   - -
        my $r = shift;
        +	  
        use Apache::Constants qw(M_GET);
        +  my $r = shift;
           my $content = $r->content;
           $r->method("GET");
           $r->method_number(M_GET);
        @@ -618,38 +523,6 @@
         

      [ TOC ]


      -

      Reading POST Data, then Redirecting or Doing Something Else

      -

      -If you read POST data, then redirect, you need to do this before the -redirect or apache will hang: - -

      - - - - - - - - - -
      -   - -
        $r->method_number(M_GET);
        -  $r->method('GET');
        -  $r->headers_in->unset('Content-length');
        -  $r->header_out('Location' => $ENV{SCRIPT_NAME});
        -  $r->status(REDIRECT);
        -  $r->send_http_header;
      -
      -

      -After the first time you read POST data, you need the code above to prevent -somebody else from trying to read post data that's already been read. - -

      -[ TOC ] -


      Redirecting While Maintaining Environment Variables

      Let's say you have a module that sets some environment variables. @@ -660,9 +533,9 @@ are preserved.

      -However, if you're using internal_redirect(), then -subprocess_env() should do the trick, but the %ENV keys will be prefixed with -REDIRECT_. +However, if you're using internal_redirect(), you can make the +environment variables seen in the sub-process via +subprocess_env(). The only nuance is that the %ENV keys will be prefixed with REDIRECT_.

      [ TOC ] @@ -917,7 +790,8 @@


      Handling Cookies

      -Unless you use some well known module like CGI.pm, handle cookies yourself. +Unless you use some well known module like CGI::Cookie or +Apache::Cookie, you need to handle cookies yourself.

      Cookies come in the $ENV{HTTP_COOKIE} variable. You can print the raw cookie string as $ENV{HTTP_COOKIE}. @@ -979,7 +853,7 @@


      Sending Multiple Cookies with the Perl API

      -Given that you have prepared your cookies in @cookies, the following would do: +Given that you have prepared your cookies in @cookies, the following code will submit all the cookies:

      @@ -991,9 +865,9 @@

      -
        for(@cookies){
        +	  
        for (@cookies){
            $r->headers_out->add( 'Set-Cookie' => $_ );
        - }
      + }
      -
        my $r = shift;
        +	  
        use Apache::Constants qw(REDIRECT OK);
        +  my $r = shift;
        +  # prepare the cookie in $cookie
           $r->err_headers_out->add('Set-Cookie' => $cookie);
           $r->headers_out(Location => $location);
           $r->status(REDIRECT);
        @@ -1033,8 +909,7 @@
         

      Let's say that you wrote a few handlers to process a request, and they all need to share some custom Perl data structure. The pnotes() -method comes to your rescue. Given that one of the handlers stored some -data in a hash %my_data, then before it terminates: +method comes to your rescue.

      @@ -1046,15 +921,17 @@

      -
         # First handler:
        -   my %my_data = (foo => 1, bar => 2);
        +	  
         # a handler that gets executed first
        +   my %my_data = (foo => 'mod_perl', bar => 'rules');
            $r->pnotes('my_data' => \%my_data);

      -All the subsequent handlers will be able to retrieve the stored data with: +The handler prepares the data in hash %my_data and calls pnotes() method to store the data internally for +other handlers to re-use. All the subsequently called handlers can retrieve +the stored data in this way:

      @@ -1066,30 +943,41 @@ -

         # Later handler:
        -   my $info = $r->pnotes('my_data');
        +	  
         my $info = $r->pnotes('my_data');
            print $info->{foo};

      +prints: + +

      + + + + + + + + + +
      +   + +
        mod_perl
      +
      +

      The stored information will be destroyed at the end of the request.

      [ TOC ]


      Passing Notes Between mod_perl and other (non-Perl) Apache Modules

      -

      -the notes() method can be used to make various Apache modules -talk to each other. In this snippet the php application calls the mod_perl -application by marking up a bunch of notes in its own request and then -issuing a sub-request to a mod_perl page. The mod_perl request handler that -gets this internal sub-request reads those notes and writes its replies in -the same place. -

      -First you read the request with (the following code is in PHP): +The notes() method can be used to make various Apache modules +talk to each other. In the following snippet the PHP module talks to the +mod_perl code (PHP code):

      @@ -1102,12 +990,11 @@

        if (isset($user) && substr($user,0,1) == "+") {
        -    apache_note("imp_euser", substr($user,1));
        +    apache_note("user", substr($user,1));
             virtual("/internal/getquota");
        -    $quota      = apache_note("imp_quota");
        -    $quota_pp   = apache_note("imp_quota_pp");
        -    $usage_pp   = apache_note("imp_usage_pp");
        -    $percent_pp = apache_note("imp_percent_pp");
        +    $quota      = apache_note("quota");
        +    $usage_pp   = apache_note("usage_pp");
        +    $percent_pp = apache_note("percent_pp");
             if ($quota)
               $message .= " | Using $percent_pp% of $quota_pp limit";
           }
      @@ -1116,15 +1003,93 @@

      -and then you read and write the notes with $r->main->notes -from mod_perl. +The PHP code sets the user and the username pair using the notes mechanism. Then issuing a sub-request +to a perl handler:

      + + + + + + + + + +
      +   + +
        use Apache::Constants qw(REDIRECT OK);
        +  my $r = shift;
        +  my $notes = $r->main->notes();
        +  my ($quota,usage_pp,percent_pp) = getquota($notes->{user}||'');
        +  $r->notes('quota',$quota);
        +  $r->notes('usage_pp',$usage_pp);
        +  $r->notes('percent_pp',$percent_pp);
        +  return OK;
      +
      +

      +which retrieves the username from the notes (using +$r->main->notes), uses some getquota() function to get the quota related data +and then sets the acquired data in the notes for the PHP code. Now the PHP +code reads the data from the notes and proceeds with setting $message if $quota is set. + +

      +So any Apache modules can communicate with each other over the Apache +notes() mechanism. + +

      +You can use notes along with the sub-request methods +lookup_uri() and lookup_filename() too. To make +it work, you need to set a note in the sub-request. For example if you want +to call a php sub-request from within mod_perl and pass it a note, you can +do it in the following way: + +

      + + + + + + + + + +
      +   + +
          my $subr = $r->lookup_uri('wizard.php3');
        +    $subr->notes('answer' => 42);
        +    $subr->run;
      +
      +

      +As of the time of this writing you cannot access the parent request tables +from a PHP handler, therefore you must set this note for the sub-request. +Whereas if the sub-request is running in the mod_perl domain, you can +always keep the notes in the parent request notes table and access them via +the method main(): + +

      + + + + + + + + + +
      +   + +
        $r->main->notes('answer');
      +
      +

      [ TOC ]


      Passing Environment Variables Between Handlers

      -A simple example of passing environment variables between handlers: +This is a simple example of passing environment variables between handlers:

      Having a configuration: @@ -1182,7 +1147,8 @@


      CGI::params in the mod_perl-ish Way

      -Extracting request parameters in the mod_perl-ish way: +You can retrieve the request parameters in a similar to CGI::params +way using this technique:

      @@ -1201,7 +1167,11 @@

      -Also take a look at Apache::Request which has the same API for extracting and setting parameters. +assuming that all your variables are single key-value pairs. + +

      +Also take a look at Apache::Request which has the same API as +CGI.pm for extracting and setting request parameters.

      [ TOC ] @@ -1236,7 +1206,7 @@ my ($self, $key) = @_; my $apr = $self->{_r}; # Here we are calling the Apache::Request object's param method - $apr->param($key) . '42'; + $apr->param($key); } sub sum { @@ -1262,15 +1232,15 @@

      There is nothing special about sending email from mod_perl, it's just that we do it a lot. There are a few important issues. The most widely used -approach is starting a sendmail process and piping the headers and the body to it. The problem is that -sendmail is a very heavy process and it makes mod_perl processes less efficient. +approach is starting a sendmail process and piping the headers and the body to it. The problem is that sendmail is a very heavy process and it makes mod_perl processes less efficient.

      If you don't want your process to wait until delivery is complete, you can tell sendmail not to deliver the email straight away, but either do it in the background or just queue the job until the next queue run. This can significantly reduce the delay for the mod_perl process which would otherwise have to -wait for the sendmail process to complete. This can be specified for all deliveries in sendmail.cf or on each invocation on the sendmail command line: +wait for the sendmail process to complete. This can be specified for all deliveries in +sendmail.cf or on each invocation on the sendmail command line:

      • @@ -1287,12 +1257,16 @@

      -Some people prefer using lighter mail delivery programs like -qmail. +The trend is to move away from sendmail(1) and switch to using +lighter mail delivery programs like qmail(1) or +postfix(1). You should check the manpage of your favorite +mailer application for equivalent configuration presented for +sendmail(1).

      The most efficient approach is to talk directly to the SMTP server. Luckily Net::SMTP modules makes this very easy. The only problem is when <Net::SMTP> fails to deliver the mail, because the destination peer -server is temporarily down. But from the other side Net::SMTP allows you to send email much much faster, since you don't have to invoke a +server is temporarily down. But from the other side Net::SMTP +allows you to send email much much faster, since you don't have to invoke a dedicated process. Here is an example of a subroutine that sends email.

      @@ -1439,14 +1413,12 @@

      [ TOC ]


      -

      mod_rewrite Based On Query String and URI Implemented in Perl

      -

      -The task: we need to perform a redirect based on the query string and the -logical path (URI). - +

      mod_rewrite in Perl

      -The solution: we write a PerlTransHandler that does what mod_rewrite does. -You can get the query string from $r->args and send redirect headers. +We can easily implement everything mod_rewrite does in Perl. We do this +with help of PerlTransHandler, which is invoked at the beginning of request +processing. For example consider that we need to perform a redirect based +on query string and URI, the following handler does that.

      @@ -1458,20 +1430,18 @@ -

        package Apache::Redirect::Based::On::Query::String::Plus::URI;
        -  use Apache::Constants 'OK','REDIRECT';
        -  use constant DEFAULT_URI => 'http://www.boston.com'; # shameless plug!
        +	  
        package Apache::MyRedirect;
        +  use Apache::Constants qw(OK REDIRECT);
        +  use constant DEFAULT_URI => 'http://www.example.org';
           
           sub handler {
             my $r    = shift;
             my %args = $r->args;
             my $path = $r->uri;
           
        -    # $uri holds something like 'http://www.mysite.com/news/' if the initial
        -    # request was 'http://www.yoursite.com/news/?uri=http://www.mysite.com/'
             my $uri = (($args{'uri'}) ? $args{'uri'} : DEFAULT_URI) . $path;
           
        -    $r->header_out->add('Location' => $uri);
        +    $r->header_out(Location => $uri);
             $r->status(REDIRECT);
             $r->send_http_header;
           
        @@ -1493,18 +1463,44 @@
         	   
                 
         
        +	
        +	  
        PerlTransHandler Apache::MyRedirect
      + + + + +

      +The code consists of three parts: request data retrieval, deciding what to +do based on this data and finally setting the headers and the status and +issuing redirect. + +

      +So if a client submits a request of this kind: + +

      + + + + + +
      +   + -
        PerlTransHandler Apache::Redirect::Based::On::Query::String::Plus::URI
      +
        http://www.example.com/news/?uri=http://www.example-2.com/

      +$uri will hold http://www.example-2.com/news/ and that's where the request will be redirected. + +

      [ TOC ]


      -

      PerlTransHandler example

      +

      URI Rewrite in PerlTransHandler

      -Suppose that before a content handler is invoked you want make this +Suppose that before a content handler is invoked you want to make this translation:

      @@ -1572,26 +1568,26 @@

      -Notice the technique to set the args. By the time the apache-request object has been created, args are handled -in a separate slot, so you cannot just push them into the original URI. +The handler code retrieves the request object and the URI. Then it +retrieves the id using the regular expression. Finally it sets the new value of the URI and +the arguments string. The handler returns +DECLINED so the default Apache transhandler will take care of URI to filename +remapping.

      -Also notice that the handler returns DECLINED so the default Apache transhandler will take care of URI to filename -remapping. +Notice the technique to set the arguments. By the time the Apache-request +object has been created, arguments are handled in a separate slot, so you +cannot just push them into the original URI. Therefore the +args() method should be used.

      [ TOC ]


      Setting PerlHandler Based on MIME Type

      -

      -Q: Is there a way to set a PerlHandler for a specific MIME type? Something -like "PerlTypeHandler text/html HTML::Template"? (One can use a <Files> section. Not quite as slick, and that mucks up -$r->location.) -

      -A: There's no built-in configuration directive like that, though you could -do magic with directive handlers. Otherwise, something like this should -work: +It's very easy to implement a dispatching module based on the MIME type of +request. So a different content handler will be called for a different MIME +type. This is an example of such a dispatcher:

      @@ -1603,52 +1599,30 @@ -

        package My::MimeTypeDispatch;
      - - - - -

      - - - - - - - - - -
      -   - -
        my %mime_types = (
        -    'text/html' => \&HTML::Template::handler,
        -  );
      -
      -

      - - - - - - -
      -   - -
        sub handler {
        +	  
        package My::MimeTypeDispatch;
        +  use Apache::Constants qw(DECLINED);
        +  
        +  my %mime_types = (
        +    'text/html'  => \&HTML::Template::handler,
        +    'text/plain' => \&My::Text::handler,
        +  );
        +  
        +  sub handler {
             my $r = shift;
             if (my $h = $mime_types{$r->content_type}) {
               $r->push_handlers(PerlHandler => $h);
               $r->handler('perl-script');
             }
        +  return DECLINED;
           }
        +  1;
           __END__

      -And in httpd.conf: +And in httpd.conf we add:

      @@ -1666,6 +1640,13 @@

      +After declaring the package name and importing constants, we set a +translation table of MIME types and corresponding handlers to be called. +Then comes the handler, where the request object is retrieved and if its +MIME type is found in our translation table we set the handler that should +handle this request. Otherwise we do nothing. At the end we return DECLINED so some other fixup handler could take over. + +

      [ TOC ]


      SSI and Embperl -- Doing Both

      @@ -1762,7 +1743,7 @@

      Compile apache with both mod_proxy and mod_rewrite, then use a directive -something like this +something like this:

      @@ -1794,8 +1775,7 @@


      Authentication Snippets

      -Getting the authenticated username: $r->connection->user(), or -$ENV{REMOTE_USER} if you're in a CGI emulation. +Getting the authenticated username: $r->connection->user(), or $ENV{REMOTE_USER} if you're in a CGI emulation.

      Example: @@ -1823,6 +1803,35 @@

      [ TOC ]


      +

      Emulating the Authentication Mechanism

      +

      +You can provide your own mechanism to authenticate users, instead of the +standard one. If you want to make Apache think that the user was +authenticated by the standard mechanism, set the username with: + +

      + + + + + + + + + +
      +   + +
        $r->connection->user('username');
      +
      +

      +Now you can use this information for example during the logging, so that +you can have your ``username'' passed as if it was transmitted to Apache +through HTTP authentication. + +

      +[ TOC ] +


      An example of using Apache::Session::DBI with cookies

      META: should be annotated at some point. (an example was posted to the @@ -2329,7 +2338,7 @@ Written by Stas Bekman.
      Last Modified at 12/13/2000 +href="help.html#Contacting_me">Stas Bekman.
      Last Modified at 03/21/2001

      1.26 +1 -1 modperl-site/guide/start.html Index: start.html =================================================================== RCS file: /home/cvs/modperl-site/guide/start.html,v retrieving revision 1.25 retrieving revision 1.26 diff -u -r1.25 -r1.26 --- start.html 2001/01/11 13:48:23 1.25 +++ start.html 2001/04/27 16:57:21 1.26 @@ -257,7 +257,7 @@ Written by Stas Bekman.
      Last Modified at 06/19/2000 +href="help.html#Contacting_me">Stas Bekman.
      Last Modified at 06/20/2000

      1.19 +13 -13 modperl-site/guide/strategy.html Index: strategy.html =================================================================== RCS file: /home/cvs/modperl-site/guide/strategy.html,v retrieving revision 1.18 retrieving revision 1.19 diff -u -r1.18 -r1.19 --- strategy.html 2001/01/11 13:48:23 1.18 +++ strategy.html 2001/04/27 16:57:21 1.19 @@ -452,10 +452,6 @@ does not spawn child processes.

      -Meta: Hey, No personal experience here, only rumours. Please let me know if -I have missed some pros/cons here. Thanks! - -

      The Advantages:

        @@ -502,6 +498,10 @@ HREF="http://www.fenrus.demon.nl/.">http://www.fenrus.demon.nl/.

        +Also check out the Boa webserver: http://www.boa.org/ + +

        [ TOC ]


        Adding a Proxy Server in http Accelerator Mode

        @@ -544,7 +544,7 @@

      • And the extra functionality provided by the http-acceleration mode, which -make the proxy server act as a sort of output buffer for the dynamic +makes the proxy server act as a sort of output buffer for the dynamic content. The mod_perl server sends the entire response to the proxy and is then free to deal with other requests. The proxy server is responsible for sending the response to the browser. So if the transfer is over a slow @@ -557,17 +557,17 @@

        First let's explain the abbreviation used in the networking world. If someone claims to have a 56 kbps connection -- it means that the connection -is of 56 killo-bits per second (~56000 bits/sec). It's not 56 killo-bytes -per second, but 7 killo-bytes per second, because 1 byte equals to 8 bits. -So don't let the merchants fool you--your modem gives you 7 killo-bytes per -second connection at most and not 56 killo-bytes per second as one might +is of 56 kilo-bits per second (~56000 bits/sec). It's not 56 kilo-bytes per +second, but 7 kilo-bytes per second, because 1 byte equals to 8 bits. So +don't let the merchants fool you--your modem gives you 7 kilo-bytes per +second connection at most and not 56 kilo-bytes per second as one might think.

        Another convention used in computer literature is that if you see 10Kb it -usually means 10 killo-bits and 10KB is 10 killo-bytes. So if you see upper +usually means 10 kilo-bits and 10KB is 10 kilo-bytes. So if you see upper case B it generally refers to bytes, and lower case b -to bits (and K of course means killo and equals to 1024 or to 1000 depending on the field +to bits (and K of course means kilo and equals to 1024 or to 1000 depending on the field it's used in). Remember that the latter convention is not followed everywhere, so use this knowledge with care. This document is following this convention. @@ -591,7 +591,7 @@ -

          42KB / 0.5s * 7KB/s = 12
        +
          42KB / (0.5s * 7KB/s) = 12
        @@ -1550,7 +1550,7 @@ Written by Stas Bekman.
        Last Modified at 12/25/2000 +href="help.html#Contacting_me">Stas Bekman.
        Last Modified at 04/04/2001

        1.11 +56 -10 modperl-site/guide/troubleshooting.html Index: troubleshooting.html =================================================================== RCS file: /home/cvs/modperl-site/guide/troubleshooting.html,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- troubleshooting.html 2001/01/11 13:48:23 1.10 +++ troubleshooting.html 2001/04/27 16:57:21 1.11 @@ -60,6 +60,7 @@
      • Runtime
          +
        • "exit signal Segmentation fault (11)" with mysql
        • foo ... at /dev/null line 0
        • Preventing mod_perl Processes From Going Wild
        • Segfaults when using XML::Parser @@ -354,10 +355,10 @@

          -This warning shows up when RegistryLoader fails to translate the URI into the corresponding filesystem path. Most +This error shows up when Apache::RegistryLoader fails to translate the URI into the corresponding filesystem path. Most failures happen when one passes a file path instead of URI. (A reminder: /home/httpd/perl/test.pl is a file path, while /perl/test.pl is a URI). In most cases all you have to do is to pass something that -RegistryLoader expects to get - the URI, but there are more complex cases. RegistryLoader's man page shows how to handle these cases as well (look for the +Apache::RegistryLoader expects to get - the URI, but there are more complex cases. Apache::RegistryLoader's man page shows how to handle these cases as well (look for the trans() sub).

          @@ -474,11 +475,8 @@

          Most often you will find that you really do have a syntax error. However the other reason might be that a script running under -Apache::Registry is using <__DATA__> or <__END__> tokens. See -Learn why +Apache::Registry is using __DATA__ or __END__ tokens. Learn why. - -

          [ TOC ]


          @@ -574,6 +572,33 @@

          [ TOC ]


          +

          "exit signal Segmentation fault (11)" with mysql

          +

          +If you build mod_perl and php in the same binary, you might get +Segmentation fault followed by this error: + +

          + + + + + + + + + +
          +   + +
            exit signal Segmentation fault (11)
          +
          +

          +Solution: re-compile PHP without the built-in MySQL support (you can still +connect to MySQL). + +

          +[ TOC ] +


          foo ... at /dev/null line 0

          Under mod_perl you may receive a warning or an error in the @@ -849,8 +874,9 @@

          -In the second case, $param will always be defined, either with -$q->param('test')'s return value, or if the parameter doesn't exist or is undefined then the empty string, '', will be assigned to $param. +In the second case, $param will always be defined, either with +$q->param('test')'s return value or the default value ('' +empty string in our example).

          Also read about Finding the Line Which Triggered the Error or Warning. @@ -1022,8 +1048,28 @@   + +

            % echo > /dev/null
          + + + + +

          +Alternatively you should try to remove this special file and recreate it: + +

          + + + + + + @@ -1341,7 +1387,7 @@
          +   + -
            % sudo echo > /dev/null
          +
            # rm /dev/null
            +  # mknod /dev/null c 1 3
            +  # chmod a+rw /dev/null
          Written by Stas Bekman.
          Last Modified at 11/20/2000 +href="help.html#Contacting_me">Stas Bekman.
          Last Modified at 03/29/2001