243 lines
7.1 KiB
Perl
243 lines
7.1 KiB
Perl
#!/usr/bin/perl -wT
|
||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||
#
|
||
# This Source Code Form is "Incompatible With Secondary Licenses", as
|
||
# defined by the Mozilla Public License, v. 2.0.
|
||
|
||
package Bugzilla::ModPerl;
|
||
|
||
use 5.10.1;
|
||
use strict;
|
||
use warnings;
|
||
|
||
# This sets up our libpath without having to specify it in the mod_perl
|
||
# configuration.
|
||
use File::Basename;
|
||
use lib dirname(__FILE__);
|
||
use Bugzilla::Constants ();
|
||
use lib Bugzilla::Constants::bz_locations()->{'ext_libpath'};
|
||
|
||
# If you have an Apache2::Status handler in your Apache configuration,
|
||
# you need to load Apache2::Status *here*, so that any later-loaded modules
|
||
# can report information to Apache2::Status.
|
||
#use Apache2::Status ();
|
||
|
||
# We don't want to import anything into the global scope during
|
||
# startup, so we always specify () after using any module in this
|
||
# file.
|
||
|
||
use Apache2::Log ();
|
||
use Apache2::ServerUtil;
|
||
use ModPerl::RegistryLoader ();
|
||
use File::Basename ();
|
||
use DateTime ();
|
||
|
||
# This loads most of our modules.
|
||
use Bugzilla ();
|
||
# Loading Bugzilla.pm doesn't load this, though, and we want it preloaded.
|
||
use Bugzilla::BugMail ();
|
||
use Bugzilla::CGI ();
|
||
use Bugzilla::Extension ();
|
||
use Bugzilla::Install::Requirements ();
|
||
use Bugzilla::Util ();
|
||
use Bugzilla::RNG ();
|
||
|
||
# Make warnings go to the virtual host's log and not the main
|
||
# server log.
|
||
BEGIN { *CORE::GLOBAL::warn = \&Apache2::ServerRec::warn; }
|
||
|
||
# Pre-compile the CGI.pm methods that we're going to use.
|
||
Bugzilla::CGI->compile(qw(:cgi :push));
|
||
|
||
my ($sizelimit, $maxrequests) = ('', '');
|
||
if (Bugzilla::Constants::ON_WINDOWS) {
|
||
$maxrequests = "MaxRequestsPerChild 25";
|
||
}
|
||
elsif (0) {
|
||
require Apache2::SizeLimit;
|
||
# This means that every httpd child will die after processing
|
||
# a CGI if it is taking up more than 70MB of RAM all by itself.
|
||
$Apache2::SizeLimit::MAX_UNSHARED_SIZE = 70000;
|
||
$sizelimit = "PerlCleanupHandler Apache2::SizeLimit";
|
||
}
|
||
my $cgi_path = Bugzilla::Constants::bz_locations()->{'cgi_path'};
|
||
|
||
# Set up the configuration for the web server
|
||
my $server = Apache2::ServerUtil->server;
|
||
my $conf = <<EOT;
|
||
# Make sure each httpd child receives a different random seed (bug 476622).
|
||
# Bugzilla::RNG has one srand that needs to be called for
|
||
# every process, and Perl has another. (Various Perl modules still use
|
||
# the built-in rand(), even though we never use it in Bugzilla itself,
|
||
# so we need to srand() both of them.)
|
||
PerlChildInitHandler "sub { Bugzilla::RNG::srand(); srand(); }"
|
||
<Directory "$cgi_path">
|
||
AddHandler perl-script .cgi
|
||
# No need to PerlModule these because they're already defined in mod_perl.pl
|
||
PerlResponseHandler Bugzilla::ModPerl::ResponseHandler
|
||
PerlCleanupHandler Bugzilla::ModPerl::CleanupHandler
|
||
$sizelimit
|
||
PerlOptions +ParseHeaders
|
||
Options +ExecCGI
|
||
AllowOverride Limit FileInfo Indexes Options
|
||
DirectoryIndex index.cgi index.html
|
||
</Directory>
|
||
EOT
|
||
|
||
$server->add_config([split("\n", $conf)]);
|
||
|
||
# Pre-load all extensions
|
||
$Bugzilla::extension_packages = Bugzilla::Extension->load_all();
|
||
|
||
# Have ModPerl::RegistryLoader pre-compile all CGI scripts.
|
||
my $rl = new ModPerl::RegistryLoader();
|
||
# If we try to do this in "new" it fails because it looks for a
|
||
# Bugzilla/ModPerl/ResponseHandler.pm
|
||
$rl->{package} = 'Bugzilla::ModPerl::ResponseHandler';
|
||
my $feature_files = Bugzilla::Install::Requirements::map_files_to_features();
|
||
|
||
# Prevent "use lib" from doing anything when the .cgi files are compiled.
|
||
# This is important to prevent the current directory from getting into
|
||
# @INC and messing things up. (See bug 630750.)
|
||
no warnings 'redefine';
|
||
local *lib::import = sub {};
|
||
use warnings;
|
||
|
||
foreach my $file (glob "$cgi_path/*.cgi") {
|
||
my $base_filename = File::Basename::basename($file);
|
||
if (my $feature = $feature_files->{$base_filename}) {
|
||
next if !Bugzilla->feature($feature);
|
||
}
|
||
Bugzilla::Util::trick_taint($file);
|
||
$rl->handler($file, $file);
|
||
}
|
||
|
||
package Bugzilla::ModPerl::ResponseHandler;
|
||
|
||
use 5.10.1;
|
||
use strict;
|
||
|
||
use parent qw(ModPerl::Registry);
|
||
use Bugzilla;
|
||
use Bugzilla::Constants qw(USAGE_MODE_REST);
|
||
|
||
sub handler : method {
|
||
my $class = shift;
|
||
|
||
# $0 is broken under mod_perl before 2.0.2, so we have to set it
|
||
# here explicitly or init_page's shutdownhtml code won't work right.
|
||
$0 = $ENV{'SCRIPT_FILENAME'};
|
||
|
||
# Prevent "use lib" from modifying @INC in the case where a .cgi file
|
||
# is being automatically recompiled by mod_perl when Apache is
|
||
# running. (This happens if a file changes while Apache is already
|
||
# running.)
|
||
no warnings 'redefine';
|
||
local *lib::import = sub {};
|
||
use warnings;
|
||
|
||
if ($Bugzilla::RELOAD_MODULES)
|
||
{
|
||
reload();
|
||
}
|
||
Bugzilla::init_page();
|
||
my $result = $class->SUPER::handler(@_);
|
||
|
||
# When returning data from the REST api we must only return 200 or 304,
|
||
# which tells Apache not to append its error html documents to the
|
||
# response.
|
||
return Bugzilla->usage_mode == USAGE_MODE_REST && $result != 304
|
||
? Apache2::Const::OK
|
||
: $result;
|
||
}
|
||
|
||
sub error_check
|
||
{
|
||
my $self = shift;
|
||
if ($@ && !(ref $@ eq 'APR::Error' && $@ == ModPerl::EXIT))
|
||
{
|
||
die $@;
|
||
}
|
||
return $self->SUPER::error_check(@_);
|
||
}
|
||
|
||
my $STATS;
|
||
|
||
# To reload Perl modules on-the-fly (debug purposes),
|
||
# add the following to Apache config before "PerlConfigRequire ......./bugzilla3/mod_perl.pl;"
|
||
# <Perl>
|
||
# $Bugzilla::RELOAD_MODULES = 1;
|
||
# $^P |= 0x10;
|
||
# </Perl>
|
||
sub reload
|
||
{
|
||
my ($file, $mtime);
|
||
for my $key (keys %INC)
|
||
{
|
||
$file = $INC{$key} or next;
|
||
$file =~ /\.p[ml]$/i or next; # do not reload *.cgi
|
||
|
||
$mtime = (stat $file)[9];
|
||
# Startup time as default
|
||
$STATS->{$file} = $^T unless defined $STATS->{$file};
|
||
|
||
# Modified
|
||
if ($mtime > $STATS->{$file})
|
||
{
|
||
print STDERR __PACKAGE__ . ": $key -> $file modified, reloading\n";
|
||
unload($key) or next;
|
||
eval { require $key };
|
||
if ($@)
|
||
{
|
||
warn $@;
|
||
}
|
||
$STATS->{$file} = $mtime;
|
||
}
|
||
}
|
||
}
|
||
|
||
sub unload
|
||
{
|
||
my ($key) = @_;
|
||
my $file = $INC{$key} or return;
|
||
my @subs = grep { index($DB::sub{$_}, "$file:") == 0 } keys %DB::sub;
|
||
for my $sub (@subs)
|
||
{
|
||
eval { undef &$sub };
|
||
if ($@)
|
||
{
|
||
# TODO не выгружать то, что не можем выгрузить, ибо
|
||
# иначе часть выгружается, а часть нет, и потом всё
|
||
# равно всё дохнет.
|
||
warn "Can't unload sub '$sub' in '$file': $@";
|
||
return undef;
|
||
}
|
||
delete $DB::sub{$sub};
|
||
}
|
||
delete $INC{$key};
|
||
return 1;
|
||
}
|
||
|
||
package Bugzilla::ModPerl::CleanupHandler;
|
||
|
||
use 5.10.1;
|
||
use strict;
|
||
|
||
use Apache2::Const -compile => qw(OK);
|
||
|
||
sub handler {
|
||
my $r = shift;
|
||
|
||
# Sometimes mod_perl doesn't properly call DESTROY on all
|
||
# the objects in pnotes()
|
||
foreach my $key (keys %{$r->pnotes}) {
|
||
delete $r->pnotes->{$key};
|
||
}
|
||
|
||
return Apache2::Const::OK;
|
||
}
|
||
|
||
1;
|