home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / TestConfig.pm < prev    next >
Encoding:
Perl POD Document  |  2003-11-12  |  46.7 KB  |  1,779 lines

  1. package Apache::TestConfig;
  2.  
  3. use strict;
  4. use warnings FATAL => 'all';
  5.  
  6. use constant WIN32   => $^O eq 'MSWin32';
  7. use constant CYGWIN  => $^O eq 'cygwin';
  8. use constant NETWARE => $^O eq 'NetWare';
  9. use constant WINFU   => WIN32 || CYGWIN || NETWARE;
  10. use constant COLOR   => ($ENV{APACHE_TEST_COLOR} && -t STDOUT) ? 1 : 0;
  11.  
  12. use constant DEFAULT_PORT => 8529;
  13.  
  14. use constant IS_MOD_PERL_2       =>
  15.     eval { require mod_perl } && $mod_perl::VERSION >= 1.99;
  16.  
  17. use constant IS_MOD_PERL_2_BUILD => IS_MOD_PERL_2 &&
  18.     require Apache::Build && Apache::Build::IS_MOD_PERL_BUILD();
  19.  
  20. use Symbol ();
  21. use File::Copy ();
  22. use File::Find qw(finddepth);
  23. use File::Basename qw(dirname);
  24. use File::Path ();
  25. use File::Spec::Functions qw(catfile abs2rel splitdir canonpath
  26.                              catdir file_name_is_absolute devnull);
  27. use Cwd qw(fastcwd);
  28.  
  29. use Apache::TestConfigPerl ();
  30. use Apache::TestConfigParse ();
  31. use Apache::TestTrace;
  32. use Apache::TestServer ();
  33. use Socket ();
  34.  
  35. use vars qw(%Usage);
  36.  
  37. %Usage = (
  38.    top_dir         => 'top-level directory (default is $PWD)',
  39.    t_dir           => 'the t/ test directory (default is $top_dir/t)',
  40.    t_conf          => 'the conf/ test directory (default is $t_dir/conf)',
  41.    t_logs          => 'the logs/ test directory (default is $t_dir/logs)',
  42.    t_conf_file     => 'test httpd.conf file (default is $t_conf/httpd.conf)',
  43.    src_dir         => 'source directory to look for mod_foos.so',
  44.    serverroot      => 'ServerRoot (default is $t_dir)',
  45.    documentroot    => 'DocumentRoot (default is $ServerRoot/htdocs',
  46.    port            => 'Port [port_number|select] (default ' . DEFAULT_PORT . ')',
  47.    servername      => 'ServerName (default is localhost)',
  48.    user            => 'User to run test server as (default is $USER)',
  49.    group           => 'Group to run test server as (default is $GROUP)',
  50.    bindir          => 'Apache bin/ dir (default is apxs -q BINDIR)',
  51.    sbindir         => 'Apache sbin/ dir (default is apxs -q SBINDIR)',
  52.    httpd           => 'server to use for testing (default is $bindir/httpd)',
  53.    target          => 'name of server binary (default is apxs -q TARGET)',
  54.    apxs            => 'location of apxs (default is from Apache::BuildConfig)',
  55.    startup_timeout => 'seconds to wait for the server to start (default is 60)',
  56.    httpd_conf      => 'inherit config from this file (default is apxs derived)',
  57.    maxclients      => 'maximum number of concurrent clients (default is 1)',
  58.    perlpod         => 'location of perl pod documents (for testing downloads)',
  59.    proxyssl_url    => 'url for testing ProxyPass / https (default is localhost)',
  60.    sslca           => 'location of SSL CA (default is $t_conf/ssl/ca)',
  61.    sslcaorg        => 'SSL CA organization to use for tests (default is asf)',
  62.    libmodperl      => 'path to mod_perl\'s .so (full or relative to LIBEXECDIR)',
  63.    (map { $_ . '_module_name', "$_ module name"} qw(cgi ssl thread access auth)),
  64. );
  65.  
  66. sub usage {
  67.     for my $hash (\%Usage) {
  68.         for (sort keys %$hash){
  69.             printf "  -%-18s %s\n", $_, $hash->{$_};
  70.         }
  71.     }
  72. }
  73.  
  74. sub filter_args {
  75.     my($args, $wanted_args) = @_;
  76.     my(@pass, %keep);
  77.  
  78.     my @filter = @$args;
  79.  
  80.     if (ref($filter[0])) {
  81.         push @pass, shift @filter;
  82.     }
  83.  
  84.     while (@filter) {
  85.         my $key = shift @filter;
  86.         # optinal - or -- prefix
  87.         if (defined $key && $key =~ /^-?-?(.+)/ && exists $wanted_args->{$1}) {
  88.             if (@filter) {
  89.                 $keep{$1} = shift @filter;
  90.             }
  91.             else {
  92.                 die "key $1 requires a matching value";
  93.             }
  94.         }
  95.         else {
  96.             push @pass, $key;
  97.         }
  98.     }
  99.  
  100.     return (\@pass, \%keep);
  101. }
  102.  
  103. my %passenv = map { $_,1 } qw{
  104. APXS APACHE APACHE_GROUP APACHE_USER APACHE_PORT
  105. };
  106.  
  107. sub passenv {
  108.     \%passenv;
  109. }
  110.  
  111. sub passenv_makestr {
  112.     my @vars;
  113.  
  114.     for (keys %passenv) {
  115.         push @vars, "$_=\$($_)";
  116.     }
  117.  
  118.     "@vars";
  119. }
  120.  
  121. sub server { shift->{server} }
  122.  
  123. sub modperl_2_inc_fixup {
  124.     (IS_MOD_PERL_2 && !IS_MOD_PERL_2_BUILD) ? "use Apache2;\n" : '';
  125. }
  126.  
  127. sub modperl_build_config {
  128.     eval {
  129.         require Apache::Build;
  130.     } or return undef;
  131.     return Apache::Build->build_config;
  132. }
  133.  
  134. sub new_test_server {
  135.     my($self, $args) = @_;
  136.     Apache::TestServer->new($args || $self)
  137. }
  138.  
  139. sub new {
  140.     my $class = shift;
  141.     my $args;
  142.  
  143.     $args = shift if $_[0] and ref $_[0];
  144.  
  145.     $args = $args ? {%$args} : {@_}; #copy
  146.  
  147.     #see Apache::TestMM::{filter_args,generate_script}
  148.     #we do this so 'perl Makefile.PL' can be passed options such as apxs
  149.     #without forcing regeneration of configuration and recompilation of c-modules
  150.     #as 't/TEST apxs /path/to/apache/bin/apxs' would do
  151.     while (my($key, $val) = each %Apache::TestConfig::Argv) {
  152.         $args->{$key} = $val;
  153.     }
  154.  
  155.     my $thaw = {};
  156.  
  157.     #thaw current config
  158.     for (qw(conf t/conf)) {
  159.         last if eval {
  160.             require "$_/apache_test_config.pm";
  161.             $thaw = 'apache_test_config'->new;
  162.             delete $thaw->{save};
  163.             #incase class that generated the config was
  164.             #something else, which we can't be sure how to load
  165.             bless $thaw, 'Apache::TestConfig';
  166.         };
  167.     };
  168.  
  169.     if ($args->{thaw} and ref($thaw) ne 'HASH') {
  170.         #dont generate any new config
  171.         $thaw->{vars}->{$_} = $args->{$_} for keys %$args;
  172.         $thaw->{server} = $thaw->new_test_server;
  173.         $thaw->add_inc;
  174.         return $thaw;
  175.     }
  176.  
  177.     #regenerating config, so forget old
  178.     if ($args->{save}) {
  179.         for (qw(vhosts inherit_config modules inc cmodules)) {
  180.             delete $thaw->{$_} if exists $thaw->{$_};
  181.         }
  182.     }
  183.  
  184.     my $self = bless {
  185.         clean => {},
  186.         vhosts => {},
  187.         inherit_config => {},
  188.         modules => {},
  189.         inc => [],
  190.         %$thaw,
  191.         mpm => "",
  192.         httpd_defines => {},
  193.         vars => $args,
  194.         postamble => [],
  195.         preamble => [],
  196.         postamble_hooks => [],
  197.         preamble_hooks => [],
  198.     }, ref($class) || $class;
  199.  
  200.     my $vars = $self->{vars}; #things that can be overridden
  201.  
  202.     for (qw(save verbose)) {
  203.         next unless exists $args->{$_};
  204.         $self->{$_} = delete $args->{$_};
  205.     }
  206.  
  207.     $vars->{top_dir} ||= fastcwd;
  208.     $vars->{top_dir} = pop_dir($vars->{top_dir}, 't');
  209.  
  210.     $self->add_inc;
  211.  
  212.     #help to find libmodperl.so
  213.     my $src_dir = catfile $vars->{top_dir}, qw(src modules perl);
  214.     $vars->{src_dir}      ||= $src_dir if -d $src_dir;
  215.  
  216.     $vars->{t_dir}        ||= catfile $vars->{top_dir}, 't';
  217.     $vars->{serverroot}   ||= $vars->{t_dir};
  218.     $vars->{documentroot} ||= catfile $vars->{serverroot}, 'htdocs';
  219.     $vars->{perlpod}      ||= $self->find_in_inc('pods') ||
  220.                               $self->find_in_inc('pod');
  221.     $vars->{perl}         ||= $^X;
  222.     $vars->{t_conf}       ||= catfile $vars->{serverroot}, 'conf';
  223.     $vars->{sslca}        ||= catfile $vars->{t_conf}, 'ssl', 'ca';
  224.     $vars->{sslcaorg}     ||= 'asf';
  225.     $vars->{t_logs}       ||= catfile $vars->{serverroot}, 'logs';
  226.     $vars->{t_conf_file}  ||= catfile $vars->{t_conf},   'httpd.conf';
  227.  
  228.     if (WINFU) {
  229.         for (keys %$vars) {
  230.             $vars->{$_} =~ s|\\|\/|g;
  231.         }
  232.     }
  233.  
  234.     $vars->{scheme}       ||= 'http';
  235.     $vars->{servername}   ||= $self->default_servername;
  236.     $vars->{port}           = $self->select_port;
  237.     $vars->{remote_addr}  ||= $self->our_remote_addr;
  238.  
  239.     $vars->{user}         ||= $self->default_user;
  240.     $vars->{group}        ||= $self->default_group;
  241.     $vars->{serveradmin}  ||= $self->default_serveradmin;
  242.     $vars->{maxclients}   ||= 1;
  243.     $vars->{proxy}        ||= 'off';
  244.     $vars->{proxyssl_url} ||= '';
  245.  
  246.     $self->configure_apxs;
  247.     $self->configure_httpd;
  248.     $self->inherit_config; #see TestConfigParse.pm
  249.     $self->configure_httpd_eapi; #must come after inherit_config
  250.  
  251.     $self->default_module(cgi    => [qw(mod_cgi mod_cgid)]);
  252.     $self->default_module(thread => [qw(worker threaded)]);
  253.     $self->default_module(ssl    => [qw(mod_ssl)]);
  254.     $self->default_module(access => [qw(mod_access mod_authz_host)]);
  255.     $self->default_module(auth   => [qw(mod_auth mod_auth_basic)]);
  256.  
  257.     $self->{hostport} = $self->hostport;
  258.  
  259.     $self->{server} = $self->new_test_server;
  260.  
  261.     $self;
  262. }
  263.  
  264. sub default_module {
  265.     my($self, $name, $choices) = @_;
  266.  
  267.     my $mname = $name . '_module_name';
  268.  
  269.     unless ($self->{vars}->{$mname}) {
  270.         ($self->{vars}->{$mname}) = grep {
  271.             $self->{modules}->{"$_.c"};
  272.         } @$choices;
  273.  
  274.         $self->{vars}->{$mname} ||= $choices->[0];
  275.     }
  276.  
  277.     $self->{vars}->{$name . '_module'} =
  278.       $self->{vars}->{$mname} . '.c'
  279. }
  280.  
  281. sub configure_apxs {
  282.     my $self = shift;
  283.  
  284.     $self->{APXS} = $self->default_apxs;
  285.  
  286.     return unless $self->{APXS};
  287.  
  288.     $self->{APXS} =~ s{/}{\\}g if WIN32;
  289.  
  290.     my $vars = $self->{vars};
  291.  
  292.     $vars->{bindir}   ||= $self->apxs('BINDIR', 1);
  293.     $vars->{sbindir}  ||= $self->apxs('SBINDIR');
  294.     $vars->{target}   ||= $self->apxs('TARGET');
  295.     $vars->{conf_dir} ||= $self->apxs('SYSCONFDIR');
  296.  
  297.     if ($vars->{conf_dir}) {
  298.         $vars->{httpd_conf} ||= catfile $vars->{conf_dir}, 'httpd.conf';
  299.     }
  300. }
  301.  
  302. sub configure_httpd {
  303.     my $self = shift;
  304.     my $vars = $self->{vars};
  305.  
  306.     $vars->{target} ||= (WIN32 ? 'Apache.exe' : 'httpd');
  307.  
  308.     unless ($vars->{httpd}) {
  309.         #sbindir should be bin/ with the default layout
  310.         #but its eaiser to workaround apxs than fix apxs
  311.         for my $dir (map { $vars->{$_} } qw(sbindir bindir)) {
  312.             next unless defined $dir;
  313.             my $httpd = catfile $dir, $vars->{target};
  314.             next unless -x $httpd;
  315.             $vars->{httpd} = $httpd;
  316.             last;
  317.         }
  318.  
  319.         $vars->{httpd} ||= $self->default_httpd;
  320.     }
  321.  
  322.     if ($vars->{httpd}) {
  323.         my @chunks = splitdir $vars->{httpd};
  324.         #handle both $prefix/bin/httpd and $prefix/Apache.exe
  325.         for (1,2) {
  326.             pop @chunks;
  327.             last unless @chunks;
  328.             $self->{httpd_basedir} = catfile @chunks;
  329.             last if -d "$self->{httpd_basedir}/bin";
  330.         }
  331.     }
  332.  
  333.     #cleanup httpd droppings
  334.     my $sem = catfile $vars->{t_logs}, 'apache_runtime_status.sem';
  335.     unless (-e $sem) {
  336.         $self->clean_add_file($sem);
  337.     }
  338. }
  339.  
  340. sub configure_httpd_eapi {
  341.     my $self = shift;
  342.     my $vars = $self->{vars};
  343.  
  344.     #deal with EAPI_MM_CORE_PATH if defined.
  345.     if (defined($self->{httpd_defines}->{EAPI_MM_CORE_PATH})) {
  346.         my $path = $self->{httpd_defines}->{EAPI_MM_CORE_PATH};
  347.  
  348.         #ensure the directory exists
  349.         my @chunks = splitdir $path;
  350.         pop @chunks; #the file component of the path
  351.         $path = catdir @chunks;
  352.         unless (file_name_is_absolute $path) {
  353.             $path = catdir $vars->{serverroot}, $path;
  354.         }
  355.         $self->gendir($path);
  356.     }
  357. }
  358.  
  359. sub configure_proxy {
  360.     my $self = shift;
  361.     my $vars = $self->{vars};
  362.  
  363.     #if we proxy to ourselves, must bump the maxclients
  364.     if ($vars->{proxy} =~ /^on$/i) {
  365.         $vars->{maxclients}++;
  366.         $vars->{proxy} = $self->{vhosts}->{'mod_proxy'}->{hostport};
  367.         return $vars->{proxy};
  368.     }
  369.  
  370.     return undef;
  371. }
  372.  
  373. sub add_config {
  374.     my $self = shift;
  375.     my $where = shift;
  376.     my($directive, $arg, $data) = @_;
  377.     my $args = "";
  378.  
  379.     if ($data) {
  380.         $args = "<$directive $arg>\n";
  381.         if (ref($data) eq 'HASH') {
  382.             while (my($k,$v) = each %$data) {
  383.                 $args .= "    $k $v\n";
  384.             }
  385.         }
  386.         elsif (ref($data) eq 'ARRAY') {
  387.             # balanced (key=>val) list
  388.             my $pairs = @$data / 2;
  389.             for my $i (0..($pairs-1)) {
  390.                 $args .= sprintf "    %s %s\n", $data->[$i*2], $data->[$i*2+1];
  391.             }
  392.         }
  393.         else {
  394.             $args .= "    $data";
  395.         }
  396.         $args .= "</$directive>\n";
  397.     }
  398.     elsif (ref($directive) eq 'ARRAY') {
  399.         $args = join "\n", @$directive;
  400.     }
  401.     else {
  402.         $args = "$directive " .
  403.           (ref($arg) && (ref($arg) eq 'ARRAY') ? "@$arg" : $arg || "");
  404.     }
  405.  
  406.     push @{ $self->{$where} }, $args;
  407. }
  408.  
  409. sub postamble {
  410.     shift->add_config(postamble => @_);
  411. }
  412.  
  413. sub preamble {
  414.     shift->add_config(preamble => @_);
  415. }
  416.  
  417. sub postamble_register {
  418.     push @{ shift->{postamble_hooks} }, @_;
  419. }
  420.  
  421. sub preamble_register {
  422.     push @{ shift->{preamble_hooks} }, @_;
  423. }
  424.  
  425. sub add_config_hooks_run {
  426.     my($self, $where, $out) = @_;
  427.  
  428.     for (@{ $self->{"${where}_hooks"} }) {
  429.         if ((ref($_) and ref($_) eq 'CODE') or $self->can($_)) {
  430.             $self->$_();
  431.         }
  432.         else {
  433.             error "cannot run configure hook: `$_'";
  434.         }
  435.     }
  436.  
  437.     for (@{ $self->{$where} }) {
  438.         $self->replace;
  439.         print $out "$_\n";
  440.     }
  441. }
  442.  
  443. sub postamble_run {
  444.     shift->add_config_hooks_run(postamble => @_);
  445. }
  446.  
  447. sub preamble_run {
  448.     shift->add_config_hooks_run(preamble => @_);
  449. }
  450.  
  451. sub default_group {
  452.     return if WINFU;
  453.  
  454.     my $gid = $);
  455.  
  456.     #use only first value if $) contains more than one
  457.     $gid =~ s/^(\d+).*$/$1/;
  458.  
  459.     my $group = $ENV{APACHE_GROUP} || (getgrgid($gid) || "#$gid");
  460.  
  461.     if ($group eq 'root') {
  462.         # similar to default_user, we want to avoid perms problems,
  463.         # when the server is started with group 'root'. When running
  464.         # under group root it may fail to create dirs and files,
  465.         # writable only by user
  466.         my $user = default_user();
  467.         my $gid = $user ? (getpwnam($user))[3] : '';
  468.         $group = (getgrgid($gid) || "#$gid") if $gid;
  469.     }
  470.  
  471.     $group;
  472. }
  473.  
  474. sub default_user {
  475.     return if WINFU;
  476.  
  477.     my $uid = $>;
  478.  
  479.     my $user = $ENV{APACHE_USER} || (getpwuid($uid) || "#$uid");
  480.  
  481.     if ($user eq 'root') {
  482.         my $other = (getpwnam('nobody'))[0];
  483.         if ($other) {
  484.             $user = $other;
  485.         }
  486.         else {
  487.             die "cannot run tests as User root";
  488.             #XXX: prompt for another username
  489.         }
  490.     }
  491.  
  492.     $user;
  493. }
  494.  
  495. sub default_serveradmin {
  496.     my $vars = shift->{vars};
  497.     join '@', ($vars->{user} || 'unknown'), $vars->{servername};
  498. }
  499.  
  500. sub default_apxs {
  501.     my $self = shift;
  502.  
  503.     return $self->{vars}->{apxs} if $self->{vars}->{apxs};
  504.  
  505.     if (my $build_config = modperl_build_config()) {
  506.         return $build_config->{MP_APXS};
  507.     }
  508.  
  509.     $ENV{APXS} || which('apxs');
  510. }
  511.  
  512. sub default_httpd {
  513.     my $vars = shift->{vars};
  514.  
  515.     if (my $build_config = modperl_build_config()) {
  516.         if (my $p = $build_config->{MP_AP_PREFIX}) {
  517.             for my $bindir (qw(bin sbin)) {
  518.                 my $httpd = "$p/$bindir/$vars->{target}";
  519.                 return $httpd if -e $httpd;
  520.             }
  521.         }
  522.     }
  523.  
  524.     $ENV{APACHE} || which($vars->{target});
  525. }
  526.  
  527. my $localhost;
  528.  
  529. sub default_localhost {
  530.     my $localhost_addr = pack('C4', 127, 0, 0, 1);
  531.     gethostbyaddr($localhost_addr, Socket::AF_INET()) || 'localhost';
  532. }
  533.  
  534. sub default_servername {
  535.     my $self = shift;
  536.     $localhost ||= $self->default_localhost;
  537.     die "Can't figure out the default localhost's server name"
  538.         unless $localhost;
  539. }
  540.  
  541. # memoize the selected value (so we make sure that the same port is used
  542. # via select). The problem is that select_port() is called 3 times after
  543. # -clean, and it's possible that a lower port will get released
  544. # between calls, leading to various places in the test suite getting a
  545. # different base port selection.
  546. #
  547. # XXX: There is still a problem if two t/TEST's configure at the same
  548. # time, so they both see the same port free, but only the first one to
  549. # bind() will actually get the port. So there is a need in another
  550. # check and reconfiguration just before the server starts.
  551. #
  552. sub select_port {
  553.     my $self = shift;
  554.  
  555.     my $port ||= $ENV{APACHE_PORT} || $self->{vars}{port} || DEFAULT_PORT;
  556.  
  557.     # memoize
  558.     $ENV{APACHE_PORT} = $port;
  559.  
  560.     return $port unless $port eq 'select';
  561.  
  562.     # port select mode: try to find another available port, take into
  563.     # account that each instance of the test suite may use more than
  564.     # one port for virtual hosts, therefore try to check ports in big
  565.     # steps (20?).
  566.     my $step  = 20;
  567.     my $tries = 20;
  568.     $port = DEFAULT_PORT;
  569.     until (Apache::TestServer->port_available($port)) {
  570.         unless (--$tries) {
  571.             error "no ports available";
  572.             error "tried ports @{[DEFAULT_PORT]} - $port in $step increments";
  573.             return 0;
  574.         }
  575.         $port += $step;
  576.     }
  577.  
  578.     info "the default base port is used, using base port $port instead"
  579.         unless $port == DEFAULT_PORT;
  580.  
  581.     # memoize
  582.     $ENV{APACHE_PORT} = $port;
  583.  
  584.     return $port;
  585. }
  586.  
  587. my $remote_addr;
  588.  
  589. sub our_remote_addr {
  590.     my $self = shift;
  591.     my $name = $self->default_servername;
  592.     $remote_addr ||= Socket::inet_ntoa((gethostbyname($name))[-1]);
  593. }
  594.  
  595. sub default_loopback {
  596.     '127.0.0.1';
  597. }
  598.  
  599. sub port {
  600.     my($self, $module) = @_;
  601.  
  602.     unless ($module) {
  603.         my $vars = $self->{vars};
  604.         return $self->select_port() unless $vars->{scheme} eq 'https';
  605.         $module = $vars->{ssl_module_name};
  606.     }
  607.     return $self->{vhosts}->{$module}->{port};
  608. }
  609.  
  610. sub hostport {
  611.     my $self = shift;
  612.     my $vars = shift || $self->{vars};
  613.     my $module = shift || '';
  614.  
  615.     my $name = $vars->{servername};
  616.     my $resolve = \$self->{resolved}->{$name};
  617.  
  618.     unless ($$resolve) {
  619.         if (gethostbyname $name) {
  620.             $$resolve = $name;
  621.         }
  622.         else {
  623.             $$resolve = $self->default_loopback;
  624.             warn "lookup $name failed, using $$resolve for client tests\n";
  625.         }
  626.     }
  627.  
  628.     join ':', $$resolve || 'localhost', $self->port($module || '');
  629. }
  630.  
  631. #look for mod_foo.so
  632. sub find_apache_module {
  633.     my($self, $module) = @_;
  634.  
  635.     die "find_apache_module: module name argument is required" unless $module;
  636.  
  637.     my $vars = $self->{vars};
  638.     my $sroot = $vars->{serverroot};
  639.  
  640.     my @trys = grep { $_ }
  641.       ($vars->{src_dir},
  642.        $self->apxs('LIBEXECDIR'),
  643.        catfile($sroot, 'modules'),
  644.        catfile($sroot, 'libexec'));
  645.  
  646.     for (@trys) {
  647.         my $file = catfile $_, $module;
  648.         if (-e $file) {
  649.             debug "found $module => $file";
  650.             return $file;
  651.         }
  652.     }
  653.  
  654.     # if the module wasn't found try to lookup in the list of modules
  655.     # inherited from the system-wide httpd.conf
  656.     my $name = $module;
  657.     $name =~ s/\.s[ol]$/.c/;  #mod_info.so => mod_info.c
  658.     $name =~ s/^lib/mod_/; #libphp4.so => mod_php4.c
  659.     return $self->{modules}->{$name} if $self->{modules}->{$name};
  660.  
  661. }
  662.  
  663. #generate files and directories
  664.  
  665. my %warn_style = (
  666.     html    => sub { "<!-- @_ -->" },
  667.     c       => sub { "/* @_ */" },
  668.     default => sub { join '', grep {s/^/\# /gm} @_ },
  669. );
  670.  
  671. my %file_ext = (
  672.     map({$_ => 'html'} qw(htm html)),
  673.     map({$_ => 'c'   } qw(c h)),
  674. );
  675.  
  676. # return the passed file's extension or '' if there is no one
  677. # note: that '/foo/bar.conf.in' returns an extension: 'conf.in';
  678. # note: a hidden file .foo will be recognized as an extension 'foo'
  679. sub filename_ext {
  680.     my ($self, $filename) = @_;
  681.     my $ext = (File::Basename::fileparse($filename, '\..*'))[2] || '';
  682.     $ext =~ s/^\.(.*)/lc $1/e;
  683.     $ext;
  684. }
  685.  
  686. sub warn_style_sub_ref {
  687.     my ($self, $filename) = @_;
  688.     my $ext = $self->filename_ext($filename);
  689.     return $warn_style{ $file_ext{$ext} || 'default' };
  690. }
  691.  
  692. sub genwarning {
  693.     my($self, $filename, $from_filename) = @_;
  694.     return unless $filename;
  695.     my $warning = "WARNING: this file is generated";
  696.     $warning .= " (from $from_filename)" if defined $from_filename;
  697.     $warning .= ", do not edit\n";
  698.     $warning .= calls_trace();
  699.     return $self->warn_style_sub_ref($filename)->($warning);
  700. }
  701.  
  702. sub calls_trace {
  703.     my $frame = 1;
  704.     my $trace = '';
  705.  
  706.     while (1) {
  707.         my($package, $filename, $line) = caller($frame);
  708.         last unless $filename;
  709.         $trace .= sprintf "%02d: %s:%d\n", $frame, $filename, $line;
  710.         $frame++;
  711.     }
  712.  
  713.     return $trace;
  714. }
  715.  
  716. sub clean_add_file {
  717.     my($self, $file) = @_;
  718.  
  719.     $self->{clean}->{files}->{ rel2abs($file) } = 1;
  720. }
  721.  
  722. sub clean_add_path {
  723.     my($self, $path) = @_;
  724.  
  725.     $path = rel2abs($path);
  726.  
  727.     # remember which dirs were created and should be cleaned up
  728.     while (1) {
  729.         $self->{clean}->{dirs}->{$path} = 1;
  730.         $path = dirname $path;
  731.         last if -e $path;
  732.     }
  733. }
  734.  
  735. sub genfile_trace {
  736.     my($self, $file, $from_file) = @_;
  737.     my $name = abs2rel $file, $self->{vars}->{t_dir};
  738.     my $msg = "generating $name";
  739.     $msg .= " from $from_file" if defined $from_file;
  740.     debug $msg;
  741. }
  742.  
  743. sub genfile_warning {
  744.     my($self, $file, $from_file, $fh) = @_;
  745.  
  746.     if (my $msg = $self->genwarning($file, $from_file)) {
  747.         print $fh $msg, "\n";
  748.     }
  749. }
  750.  
  751. # $from_file == undef if there was no templates used
  752. sub genfile {
  753.     my($self, $file, $from_file, $nowarning) = @_;
  754.  
  755.     # create the parent dir if it doesn't exist yet
  756.     my $dir = dirname $file;
  757.     $self->makepath($dir);
  758.  
  759.     $self->genfile_trace($file, $from_file);
  760.  
  761.     my $fh = Symbol::gensym();
  762.     open $fh, ">$file" or die "open $file: $!";
  763.  
  764.     $self->genfile_warning($file, $from_file, $fh) unless $nowarning;
  765.  
  766.     $self->clean_add_file($file);
  767.  
  768.     return $fh;
  769. }
  770.  
  771. # gen + write file
  772. sub writefile {
  773.     my($self, $file, $content, $nowarning) = @_;
  774.  
  775.     my $fh = $self->genfile($file, undef, $nowarning);
  776.  
  777.     print $fh $content if $content;
  778.  
  779.     close $fh;
  780. }
  781.  
  782. sub perlscript_header {
  783.  
  784.     require FindBin;
  785.  
  786.     my @dirs = ();
  787.  
  788.     # mp2 needs its modper-2.0/lib before blib was created
  789.     if (IS_MOD_PERL_2_BUILD || $ENV{APACHE_TEST_LIVE_DEV}) {
  790.         # the live 'lib/' dir of the distro
  791.         # (e.g. modperl-2.0/ModPerl-Registry/lib)
  792.         my $dir = canonpath catdir $FindBin::Bin, "lib";
  793.         push @dirs, $dir if -d $dir;
  794.  
  795.         # the live dir of the top dir if any  (e.g. modperl-2.0/lib)
  796.         if (-e catfile($FindBin::Bin, "..", "Makefile.PL")) {
  797.             my $dir = canonpath catdir $FindBin::Bin, "..", "lib";
  798.             push @dirs, $dir if -d $dir;
  799.         }
  800.     }
  801.  
  802.     for (qw(. ..)) {
  803.         my $dir = canonpath catdir $FindBin::Bin, $_ , "Apache-Test", "lib";
  804.         if (-d $dir) {
  805.             push @dirs, $dir;
  806.             last;
  807.         }
  808.     }
  809.  
  810.     my $dirs = join("\n    ", '', @dirs) . "\n";;
  811.  
  812.     return <<"EOF";
  813.  
  814. use strict;
  815. use warnings FATAL => 'all';
  816.  
  817. use lib qw($dirs);
  818.  
  819. EOF
  820. }
  821.  
  822. # gen + write executable perl script file
  823. sub write_perlscript {
  824.     my($self, $file, $content) = @_;
  825.  
  826.     my $fh = $self->genfile($file, undef, 1);
  827.  
  828.     # shebang
  829.     print $fh "#!$Config{perlpath}\n";
  830.  
  831.     $self->genfile_warning($file, undef, $fh);
  832.  
  833.     print $fh $content if $content;
  834.  
  835.     close $fh;
  836.     chmod 0755, $file;
  837. }
  838.  
  839. sub cpfile {
  840.     my($self, $from, $to) = @_;
  841.     File::Copy::copy($from, $to);
  842.     $self->clean_add_file($to);
  843. }
  844.  
  845. sub symlink {
  846.     my($self, $from, $to) = @_;
  847.     CORE::symlink($from, $to);
  848.     $self->clean_add_file($to);
  849. }
  850.  
  851. sub gendir {
  852.     my($self, $dir) = @_;
  853.     $self->makepath($dir);
  854. }
  855.  
  856. # returns a list of dirs successfully created
  857. sub makepath {
  858.     my($self, $path) = @_;
  859.  
  860.     return if !defined($path) || -e $path;
  861.  
  862.     $self->clean_add_path($path);
  863.  
  864.     return File::Path::mkpath($path, 0, 0755);
  865. }
  866.  
  867. sub open_cmd {
  868.     my($self, $cmd) = @_;
  869.     # untaint some %ENV fields
  870.     local @ENV{ qw(PATH IFS CDPATH ENV BASH_ENV) };
  871.  
  872.     my $handle = Symbol::gensym();
  873.     open $handle, "$cmd|" or die "$cmd failed: $!";
  874.  
  875.     return $handle;
  876. }
  877.  
  878. sub clean {
  879.     my $self = shift;
  880.     $self->{clean_level} = shift || 2; #2 == really clean, 1 == reconfigure
  881.  
  882.     $self->new_test_server->clean;
  883.     $self->cmodules_clean;
  884.     $self->sslca_clean;
  885.  
  886.     for (keys %{ $self->{clean}->{files} }) {
  887.         if (-e $_) {
  888.             debug "unlink $_";
  889.             unlink $_;
  890.         }
  891.         else {
  892.             debug "unlink $_: $!";
  893.         }
  894.     }
  895.  
  896.     # if /foo comes before /foo/bar, /foo will never be removed
  897.     # hence ensure that sub-dirs are always treated before a parent dir
  898.     for (reverse sort keys %{ $self->{clean}->{dirs} }) {
  899.         if (-d $_) {
  900.             my $dh = Symbol::gensym();
  901.             opendir($dh, $_);
  902.             my $notempty = grep { ! /^\.{1,2}$/ } readdir $dh;
  903.             closedir $dh;
  904.             next if $notempty;
  905.             debug "rmdir $_";
  906.             rmdir $_;
  907.         }
  908.     }
  909. }
  910.  
  911. sub replace {
  912.     my $self = shift;
  913.     my $file = $Apache::TestConfig::File
  914.         ? "in file $Apache::TestConfig::File" : '';
  915.  
  916.     s[@(\w+)@]
  917.      [ my $key = lc $1;
  918.       exists $self->{vars}->{$key}
  919.       ? $self->{vars}->{$key}
  920.       : die "invalid token: \@$1\@ $file\n";
  921.      ]ge;
  922. }
  923.  
  924. #need to configure the vhost port for redirects and $ENV{SERVER_PORT}
  925. #to have the correct values
  926. my %servername_config = (
  927.     1 => sub {
  928.         my($name, $port) = @_;
  929.         [ServerName => $name], [Port => $port];
  930.     },
  931.     2 => sub {
  932.         my($name, $port) = @_;
  933.         [ServerName => "$name:$port"];
  934.     },
  935. );
  936.  
  937. sub servername_config {
  938.     my $self = shift;
  939.     $self->server->version_of(\%servername_config)->(@_);
  940. }
  941.  
  942. sub parse_vhost {
  943.     my($self, $line) = @_;
  944.  
  945.     my($indent, $module, $namebased);
  946.     if ($line =~ /^(\s*)<VirtualHost\s+(?:_default_:|([^:]+):(?!:))?(.*?)\s*>\s*$/) {
  947.         $indent    = $1 || "";
  948.         $namebased = $2 || "";
  949.         $module    = $3;
  950.     }
  951.     else {
  952.         return undef;
  953.     }
  954.  
  955.     my $vars = $self->{vars};
  956.     my $mods = $self->{modules};
  957.     my $have_module = "$module.c";
  958.     my $ssl_module = $vars->{ssl_module};
  959.  
  960.     #if module ends with _ssl and it is not the module that implements ssl,
  961.     #then assume this module is a vhost with SSLEngine On (or similar)
  962.     #see mod_echo in extra.conf.in for example
  963.     if ($module =~ /^(mod_\w+)_ssl$/ and $have_module ne $ssl_module) {
  964.         $have_module = "$1.c"; #e.g. s/mod_echo_ssl.c/mod_echo.c/
  965.         return undef unless $mods->{$ssl_module};
  966.     }
  967.  
  968.     #don't allocate a port if this module is not configured
  969.     #assumes the configuration is inside an <IfModule $have_module>
  970.     if ($module =~ /^mod_/ and not $mods->{$have_module}) {
  971.         return undef;
  972.     }
  973.  
  974.     #allocate a port and configure this module into $self->{vhosts}
  975.     my $port = $self->new_vhost($module, $namebased);
  976.  
  977.     #extra config that should go *inside* the <VirtualHost ...>
  978.     my @in_config = $self->servername_config($namebased
  979.                                                  ? $namebased
  980.                                                  : $vars->{servername},
  981.                                              $port);
  982.  
  983.     my @out_config = ();
  984.     if ($self->{vhosts}->{$module}->{namebased} < 2) {
  985.         #extra config that should go *outside* the <VirtualHost ...>
  986.         @out_config = ([Listen => $port]);
  987.  
  988.         if ($self->{vhosts}->{$module}->{namebased}) {
  989.             push @out_config => [NameVirtualHost => "*:$port"];
  990.         }
  991.     }
  992.  
  993.     #there are two ways of building a vhost
  994.     #first is when we parse test .pm and .c files
  995.     #second is when we scan *.conf.in
  996.     my $form_postamble = sub {
  997.         my $indent = shift;
  998.         for my $pair (@_) {
  999.             $self->postamble("$indent@$pair");
  1000.         }
  1001.     };
  1002.  
  1003.     my $form_string = sub {
  1004.         my $indent = shift;
  1005.         join "\n", map { "$indent@$_\n" } @_;
  1006.     };
  1007.  
  1008.     my $double_indent = $indent ? $indent x 2 : ' ' x 4;
  1009.     return {
  1010.         port          => $port,
  1011.         #used when parsing .pm and .c test modules
  1012.         in_postamble  => sub { $form_postamble->($double_indent, @in_config) },
  1013.         out_postamble => sub { $form_postamble->($indent, @out_config) },
  1014.         #used when parsing *.conf.in files
  1015.         in_string     => $form_string->($double_indent, @in_config),
  1016.         out_string    => $form_string->($indent, @out_config),
  1017.         line          => "$indent<VirtualHost " . ($namebased ? '*' : '_default_') .
  1018.                          ":$port>",
  1019.     };
  1020. }
  1021.  
  1022. sub replace_vhost_modules {
  1023.     my $self = shift;
  1024.  
  1025.     if (my $cfg = $self->parse_vhost($_)) {
  1026.         $_ = '';
  1027.         for my $key (qw(out_string line in_string)) {
  1028.             next unless $cfg->{$key};
  1029.             $_ .= "$cfg->{$key}\n";
  1030.         }
  1031.     }
  1032. }
  1033.  
  1034. sub replace_vars {
  1035.     my($self, $in, $out) = @_;
  1036.  
  1037.     local $_;
  1038.     while (<$in>) {
  1039.         $self->replace;
  1040.         $self->replace_vhost_modules;
  1041.         print $out $_;
  1042.     }
  1043. }
  1044.  
  1045. sub index_html_template {
  1046.     my $self = shift;
  1047.     return "welcome to $self->{server}->{name}\n";
  1048. }
  1049.  
  1050. sub generate_index_html {
  1051.     my $self = shift;
  1052.     my $dir = $self->{vars}->{documentroot};
  1053.     $self->gendir($dir);
  1054.     my $file = catfile $dir, 'index.html';
  1055.     return if -e $file;
  1056.     my $fh = $self->genfile($file);
  1057.     print $fh $self->index_html_template;
  1058. }
  1059.  
  1060. sub types_config_template {
  1061.     return <<EOF;
  1062. text/html  html htm
  1063. image/gif  gif
  1064. image/jpeg jpeg jpg jpe
  1065. image/png  png
  1066. text/plain asc txt
  1067. EOF
  1068. }
  1069.  
  1070. sub generate_types_config {
  1071.     my $self = shift;
  1072.  
  1073.     # handle the case when mod_mime is built as a shared object
  1074.     # but wasn't included in the system-wide httpd.conf
  1075.     my $mod_mime = $self->find_apache_module('mod_mime.so');
  1076.     if ($mod_mime && -e $mod_mime) {
  1077.         $self->preamble(IfModule => '!mod_mime.c',
  1078.                         qq{LoadModule mime_module "$mod_mime"\n});
  1079.     }
  1080.  
  1081.     unless ($self->{inherit_config}->{TypesConfig}) {
  1082.         my $types = catfile $self->{vars}->{t_conf}, 'mime.types';
  1083.         unless (-e $types) {
  1084.             my $fh = $self->genfile($types);
  1085.             print $fh $self->types_config_template;
  1086.             close $fh;
  1087.         }
  1088.         $self->postamble(TypesConfig => qq("$types"));
  1089.     }
  1090. }
  1091.  
  1092. sub httpd_conf_template {
  1093.     my($self, $try) = @_;
  1094.  
  1095.     my $in = Symbol::gensym();
  1096.     if (open $in, $try) {
  1097.         return $in;
  1098.     }
  1099.     else {
  1100.         return \*DATA;
  1101.     }
  1102. }
  1103.  
  1104. #certain variables may not be available until certain config files
  1105. #are generated.  for example, we don't know the ssl port until ssl.conf.in
  1106. #is parsed.  ssl port is needed for proxyssl testing
  1107.  
  1108. sub check_vars {
  1109.     my $self = shift;
  1110.     my $vars = $self->{vars};
  1111.  
  1112.     unless ($vars->{proxyssl_url}) {
  1113.         my $ssl = $self->{vhosts}->{ $vars->{ssl_module_name} };
  1114.         if ($ssl) {
  1115.             $vars->{proxyssl_url} ||= $ssl->{hostport};
  1116.         }
  1117.  
  1118.         if ($vars->{proxyssl_url}) {
  1119.             $vars->{maxclients}++;
  1120.         }
  1121.     }
  1122. }
  1123.  
  1124. sub generate_extra_conf {
  1125.     my $self = shift;
  1126.  
  1127.     my(@extra_conf, @conf_in, @conf_files);
  1128.  
  1129.     finddepth(sub {
  1130.         return unless /\.in$/;
  1131.         push @conf_in, catdir $File::Find::dir, $_;
  1132.     }, $self->{vars}->{t_conf});
  1133.  
  1134.     #make ssl port always be 8530 when available
  1135.     for my $file (@conf_in) {
  1136.         if (basename($file) =~ /^ssl/) {
  1137.             unshift @conf_files, $file;
  1138.         }
  1139.         else {
  1140.             push @conf_files, $file;
  1141.         }
  1142.     }
  1143.  
  1144.     for my $file (@conf_files) {
  1145.         local $Apache::TestConfig::File = $file;
  1146.  
  1147.         (my $generated = $file) =~ s/\.in$//;
  1148.         push @extra_conf, $generated;
  1149.  
  1150.         debug "Including $generated config file";
  1151.  
  1152.         next if -e $generated
  1153.             && -M $generated < -M $file;
  1154.  
  1155.         my $in = Symbol::gensym();
  1156.         open($in, $file) or next;
  1157.  
  1158.         my $out = $self->genfile($generated, $file);
  1159.         $self->replace_vars($in, $out);
  1160.  
  1161.         close $in;
  1162.         close $out;
  1163.  
  1164.         $self->check_vars;
  1165.     }
  1166.  
  1167.     #we changed order to give ssl the first port after DEFAULT_PORT
  1168.     #but we want extra.conf Included first so vhosts inherit base config
  1169.     #such as LimitRequest*
  1170.     return [ sort @extra_conf ];
  1171. }
  1172.  
  1173. sub sslca_can {
  1174.     my($self, $check) = @_;
  1175.  
  1176.     my $vars = $self->{vars};
  1177.     return 0 unless $self->{modules}->{ $vars->{ssl_module} };
  1178.     return 0 unless -d "$vars->{t_conf}/ssl";
  1179.  
  1180.     require Apache::TestSSLCA;
  1181.  
  1182.     if ($check) {
  1183.         my $openssl = Apache::TestSSLCA::openssl();
  1184.         if (which($openssl)) {
  1185.             return 1;
  1186.         }
  1187.  
  1188.         error "cannot locate '$openssl' program required to generate SSL CA";
  1189.         exit(1);
  1190.     }
  1191.  
  1192.     return 1;
  1193. }
  1194.  
  1195. sub sslca_generate {
  1196.     my $self = shift;
  1197.  
  1198.     my $ca = $self->{vars}->{sslca};
  1199.     return if $ca and -d $ca; #t/conf/ssl/ca
  1200.  
  1201.     return unless $self->sslca_can(1);
  1202.  
  1203.     Apache::TestSSLCA::generate($self);
  1204. }
  1205.  
  1206. sub sslca_clean {
  1207.     my $self = shift;
  1208.  
  1209.     return unless $self->sslca_can;
  1210.  
  1211.     Apache::TestSSLCA::clean($self);
  1212. }
  1213.  
  1214. #XXX: just a quick hack to support t/TEST -ssl
  1215. #outside of httpd-test/perl-framework
  1216. sub generate_ssl_conf {
  1217.     my $self = shift;
  1218.     my $vars = $self->{vars};
  1219.     my $conf = "$vars->{t_conf}/ssl";
  1220.     my $httpd_test_ssl = "../httpd-test/perl-framework/t/conf/ssl";
  1221.     my $ssl_conf = "$vars->{top_dir}/$httpd_test_ssl";
  1222.  
  1223.     if (-d $ssl_conf and not -d $conf) {
  1224.         $self->gendir($conf);
  1225.         for (qw(ssl.conf.in)) {
  1226.             $self->cpfile("$ssl_conf/$_", "$conf/$_");
  1227.         }
  1228.         for (qw(certs keys crl)) {
  1229.             $self->symlink("$ssl_conf/$_", "$conf/$_");
  1230.         }
  1231.     }
  1232. }
  1233.  
  1234. sub find_in_inc {
  1235.     my($self, $dir) = @_;
  1236.     for my $path (@INC) {
  1237.         my $location = "$path/$dir";
  1238.         return $location if -d $location;
  1239.     }
  1240.     return "";
  1241. }
  1242.  
  1243. sub prepare_t_conf {
  1244.     my $self = shift;
  1245.     $self->gendir($self->{vars}->{t_conf});
  1246. }
  1247.  
  1248. my %aliases = (
  1249.     "perl-pod"     => "perlpod",
  1250.     "binary-httpd" => "httpd",
  1251.     "binary-perl"  => "perl",
  1252. );
  1253. sub generate_httpd_conf {
  1254.     my $self = shift;
  1255.     my $vars = $self->{vars};
  1256.  
  1257.     #generated httpd.conf depends on these things to exist
  1258.     $self->generate_types_config;
  1259.     $self->generate_index_html;
  1260.  
  1261.     $self->gendir($vars->{t_logs});
  1262.  
  1263.     my @very_last_postamble = ();
  1264.     if (my $extra_conf = $self->generate_extra_conf) {
  1265.         for my $file (@$extra_conf) {
  1266.             my $entry;
  1267.             if ($file =~ /\.conf$/) {
  1268.                 next if $file =~ m|/httpd\.conf$|;
  1269.                 $entry = qq(Include "$file");
  1270.             }
  1271.             elsif ($file =~ /\.pl$/) {
  1272.                 $entry = qq(<IfModule mod_perl.c>\n    PerlRequire "$file"\n</IfModule>\n);
  1273.             }
  1274.             else {
  1275.                 next;
  1276.             }
  1277.  
  1278.             # put the .last includes very last
  1279.             if ($file =~ /\.last\.(conf|pl)$/) {
  1280.                  push @very_last_postamble, $entry;
  1281.             }
  1282.             else {
  1283.                 $self->postamble($entry);
  1284.             }
  1285.  
  1286.         }
  1287.     }
  1288.  
  1289.     $self->configure_proxy;
  1290.  
  1291.     my $conf_file = $vars->{t_conf_file};
  1292.     my $conf_file_in = join '.', $conf_file, 'in';
  1293.  
  1294.     my $in = $self->httpd_conf_template($conf_file_in);
  1295.  
  1296.     my $out = $self->genfile($conf_file);
  1297.  
  1298.     $self->preamble_run($out);
  1299.  
  1300.     for my $name (qw(user group)) { #win32/cygwin do not support
  1301.         if ($vars->{$name}) {
  1302.             print $out "\u$name    $vars->{$name}\n";
  1303.         }
  1304.     }
  1305.  
  1306.     #2.0: ServerName $ServerName:$Port
  1307.     #1.3: ServerName $ServerName
  1308.     #     Port       $Port
  1309.     my @name_cfg = $self->servername_config($vars->{servername},
  1310.                                             $vars->{port});
  1311.     for my $pair (@name_cfg) {
  1312.         print $out "@$pair\n";
  1313.     }
  1314.  
  1315.     $self->replace_vars($in, $out);
  1316.  
  1317.     # handle the case when mod_alias is built as a shared object
  1318.     # but wasn't included in the system-wide httpd.conf
  1319.     my $mod_alias = $self->find_apache_module('mod_alias.so');
  1320.     if ($mod_alias && -e $mod_alias) {
  1321.         print $out <<EOF;
  1322. <IfModule !mod_alias.c>
  1323.     LoadModule alias_module "$mod_alias"
  1324. </IfModule>
  1325. EOF
  1326.     }
  1327.  
  1328.     print $out "<IfModule mod_alias.c>\n";
  1329.     for (keys %aliases) {
  1330.         next unless $vars->{$aliases{$_}};
  1331.         print $out "    Alias /getfiles-$_ $vars->{$aliases{$_}}\n";
  1332.     }
  1333.     print $out "</IfModule>\n";
  1334.  
  1335.     print $out "\n";
  1336.  
  1337.     $self->postamble_run($out);
  1338.  
  1339.     print $out join "\n", @very_last_postamble;
  1340.  
  1341.     close $in;
  1342.     close $out or die "close $conf_file: $!";
  1343. }
  1344.  
  1345. sub need_reconfiguration {
  1346.     my $self = shift;
  1347.     my @reasons = ();
  1348.     my $vars = $self->{vars};
  1349.  
  1350.     my $exe = $vars->{apxs} || $vars->{httpd};
  1351.     # if httpd.conf is older than executable
  1352.     push @reasons, 
  1353.         "$exe is newer than $vars->{t_conf_file}"
  1354.             if -e $exe && 
  1355.                -e $vars->{t_conf_file} &&
  1356.                -M $exe < -M $vars->{t_conf_file};
  1357.  
  1358.     # if .in files are newer than their derived versions
  1359.     if (my $extra_conf = $self->generate_extra_conf) {
  1360.         for my $file (@$extra_conf) {
  1361.             push @reasons, "$file.in is newer than $file"
  1362.                 if -e $file && -M "$file.in" < -M $file;
  1363.         }
  1364.     }
  1365.  
  1366.     return @reasons;
  1367. }
  1368.  
  1369. sub error_log {
  1370.     my($self, $rel) = @_;
  1371.     my $file = catfile $self->{vars}->{t_logs}, 'error_log';
  1372.     my $rfile = abs2rel $file, $self->{vars}->{top_dir};
  1373.     return wantarray ? ($file, $rfile) :
  1374.       $rel ? $rfile : $file;
  1375. }
  1376.  
  1377. #utils
  1378.  
  1379. #For Win32 systems, stores the extensions used for executable files
  1380. #They may be . prefixed, so we will strip the leading periods.
  1381.  
  1382. my @path_ext = ();
  1383.  
  1384. if (WIN32) {
  1385.     if ($ENV{PATHEXT}) {
  1386.         push @path_ext, split ';', $ENV{PATHEXT};
  1387.         for my $ext (@path_ext) {
  1388.             $ext =~ s/^\.*(.+)$/$1/;
  1389.         }
  1390.     }
  1391.     else {
  1392.         #Win9X: doesn't have PATHEXT
  1393.         push @path_ext, qw(com exe bat);
  1394.     }
  1395. }
  1396.  
  1397. sub which {
  1398.     my $program = shift;
  1399.  
  1400.     return undef unless $program;
  1401.  
  1402.     my @results = ();
  1403.  
  1404.     for my $base (map { catfile($_, $program) } File::Spec->path()) {
  1405.         if ($ENV{HOME} and not WIN32) {
  1406.             # only works on Unix, but that's normal:
  1407.             # on Win32 the shell doesn't have special treatment of '~'
  1408.             $base =~ s/~/$ENV{HOME}/o;
  1409.         }
  1410.  
  1411.         return $base if -x $base;
  1412.  
  1413.         if (WIN32) {
  1414.             for my $ext (@path_ext) {
  1415.                 return "$base.$ext" if -x "$base.$ext";
  1416.             }
  1417.         }
  1418.     }
  1419. }
  1420.  
  1421. sub apxs {
  1422.     my($self, $q, $ok_fail) = @_;
  1423.     return unless $self->{APXS};
  1424.     my $devnull = devnull();
  1425.     my $val = qx($self->{APXS} -q $q 2>$devnull);
  1426.     chomp $val if defined $val; # apxs post-2.0.40 adds a new line
  1427.     unless ($val) {
  1428.         if ($ok_fail) {
  1429.             return "";
  1430.         }
  1431.         else {
  1432.             warn "APXS ($self->{APXS}) query for $q failed\n";
  1433.         }
  1434.     }
  1435.     $val;
  1436. }
  1437.  
  1438. sub pop_dir {
  1439.     my $dir = shift;
  1440.  
  1441.     my @chunks = splitdir $dir;
  1442.     while (my $remove = shift) {
  1443.         pop @chunks if $chunks[-1] eq $remove;
  1444.     }
  1445.  
  1446.     catfile @chunks;
  1447. }
  1448.  
  1449. sub add_inc {
  1450.     my $self = shift;
  1451.     return if $ENV{MOD_PERL}; #already setup by mod_perl
  1452.     require lib;
  1453.     # make sure that Apache-Test/lib will be first in @INC,
  1454.     # followed by modperl-2.0/lib (or some other project's lib/),
  1455.     # followed by blib/ and finally system-wide libs.
  1456.     my $top_dir = $self->{vars}->{top_dir};
  1457.     my @dirs = map { catdir $top_dir, "blib", $_ } qw(lib arch);
  1458.  
  1459.     my $apache_test_dir = catdir $top_dir, "Apache-Test";
  1460.     unshift @dirs, $apache_test_dir if -d $apache_test_dir;
  1461.  
  1462.     if ($ENV{APACHE_TEST_LIVE_DEV}) {
  1463.         my $lib_dir = catdir $top_dir, "lib";
  1464.         push @dirs, $lib_dir if -d $lib_dir;
  1465.     }
  1466.  
  1467.     lib::->import(@dirs);
  1468.     #print join "\n", "add_inc", @INC, "";
  1469. }
  1470.  
  1471. #freeze/thaw so other processes can access config
  1472.  
  1473. sub thaw {
  1474.     my $class = shift;
  1475.     $class->new({thaw => 1, @_});
  1476. }
  1477.  
  1478. sub freeze {
  1479.     require Data::Dumper;
  1480.     local $Data::Dumper::Terse = 1;
  1481.     my $data = Data::Dumper::Dumper(shift);
  1482.     chomp $data;
  1483.     $data;
  1484. }
  1485.  
  1486. sub sync_vars {
  1487.     my $self = shift;
  1488.  
  1489.     return if $self->{save}; #this is not a cached config
  1490.  
  1491.     my $changed = 0;
  1492.     my $thaw = $self->thaw;
  1493.     my $tvars = $thaw->{vars};
  1494.     my $svars = $self->{vars};
  1495.  
  1496.     for my $key (@_) {
  1497.         for my $v ($tvars, $svars) {
  1498.             if (exists $v->{$key} and not defined $v->{$key}) {
  1499.                 $v->{$key} = ''; #rid undef
  1500.             }
  1501.         }
  1502.         next if exists $tvars->{$key} and exists $svars->{$key} and
  1503.                        $tvars->{$key} eq $svars->{$key};
  1504.         $tvars->{$key} = $svars->{$key};
  1505.         $changed = 1;
  1506.     }
  1507.  
  1508.     return unless $changed;
  1509.  
  1510.     $thaw->{save} = 1;
  1511.     $thaw->save;
  1512. }
  1513.  
  1514. sub save {
  1515.     my($self) = @_;
  1516.  
  1517.     return unless $self->{save};
  1518.  
  1519.     my $name = 'apache_test_config';
  1520.     my $file = catfile $self->{vars}->{t_conf}, "$name.pm";
  1521.     my $fh = $self->genfile($file);
  1522.  
  1523.     debug "saving config data to $name.pm";
  1524.  
  1525.     (my $obj = $self->freeze) =~ s/^/    /;
  1526.  
  1527.     print $fh <<EOF;
  1528. package $name;
  1529.  
  1530. sub new {
  1531. $obj;
  1532. }
  1533.  
  1534. 1;
  1535. EOF
  1536.  
  1537.     close $fh or die "failed to write $file: $!";
  1538. }
  1539.  
  1540. sub as_string {
  1541.     my $cfg = '';
  1542.     my $command = '';
  1543.  
  1544.     # httpd opts
  1545.     my $test_config = Apache::TestConfig->new({thaw=>1});
  1546.     if (my $httpd = $test_config->{vars}->{httpd}) {
  1547.         $command = "$httpd -V";
  1548.         $cfg .= "\n*** $command\n";
  1549.         $cfg .= qx{$command};
  1550.     } else {
  1551.         $cfg .= "\n\n*** The httpd binary was not found\n";
  1552.     }
  1553.  
  1554.     # perl opts
  1555.     my $perl = $^X;
  1556.     $command = "$perl -V";
  1557.     $cfg .= "\n\n*** $command\n";
  1558.     $cfg .= qx{$command};
  1559.  
  1560.     return $cfg;
  1561. }
  1562.  
  1563. 1;
  1564.  
  1565. =head1 NAME
  1566.  
  1567. Apache::TestConfig -- Test Configuration setup module
  1568.  
  1569. =head1 SYNOPSIS
  1570.  
  1571.   use Apache::TestConfig;
  1572.  
  1573.   my $cfg = Apache::TestConfig->new(%args)
  1574.   my $fh = $cfg->genfile($file);
  1575.   $cfg->writefile($file, $content);
  1576.   $cfg->gendir($dir);
  1577.   ...
  1578.  
  1579. =head1 DESCRIPTION
  1580.  
  1581. C<Apache::TestConfig> is used in creating the C<Apache::Test>
  1582. configuration files.
  1583.  
  1584. =head1 FUNCTIONS
  1585.  
  1586. =over
  1587.  
  1588. =item genwarning()
  1589.  
  1590.   my $warn = $cfg->genwarning($filename)
  1591.  
  1592. genwarning() returns a warning string as a comment, saying that the
  1593. file was autogenerated and that it's not a good idea to modify this
  1594. file. After the warning a perl trace of calls to this this function is
  1595. appended. This trace is useful for finding what code has created the
  1596. file.
  1597.  
  1598.   my $warn = $cfg->genwarning($filename, $from_filename)
  1599.  
  1600. If C<$from_filename> is specified it'll be used in the warning to tell
  1601. which file it was generated from.
  1602.  
  1603. genwarning() automatically recognizes the comment type based on the
  1604. file extension. If the extension is not recognized, the default C<#>
  1605. style is used.
  1606.  
  1607. Currently it support C<E<lt>!-- --E<gt>>, C</* ... */> and C<#>
  1608. styles.
  1609.  
  1610. =item genfile()
  1611.  
  1612.   my $fh = $cfg->genfile($file);
  1613.  
  1614. genfile() creates a new file C<$file> for writing and returns a file
  1615. handle.
  1616.  
  1617. If parent directories of C<$file> don't exist they will be
  1618. automagically created.
  1619.  
  1620. The file C<$file> and any created parent directories (if found empty)
  1621. will be automatically removed on cleanup.
  1622.  
  1623. A comment with a warning and calls trace is added to the top of this
  1624. file. See genwarning() for more info about this comment.
  1625.  
  1626.   my $fh = $cfg->genfile($file, $from_file);
  1627.  
  1628. If C<$from_filename> is specified it'll be used in the warning to tell
  1629. which file it was generated from.
  1630.  
  1631.   my $fh = $cfg->genfile($file, $from_file, $nowarning);
  1632.  
  1633. If C<$nowarning> is true, the warning won't be added. If using this
  1634. optional argument and there is no C<$from_file> you must pass undef as
  1635. in:
  1636.  
  1637.   my $fh = $cfg->genfile($file, undef, $nowarning);
  1638.  
  1639.  
  1640. =item writefile()
  1641.  
  1642.   $cfg->writefile($file, $content, [$nowarning]);
  1643.  
  1644. writefile() creates a new file C<$file> with the content of
  1645. C<$content>.
  1646.  
  1647. A comment with a warning and calls trace is added to the top of this
  1648. file unless C<$nowarnings> is passed and set to a true value. See
  1649. genwarning() for more info about this comment.
  1650.  
  1651. If parent directories of C<$file> don't exist they will be
  1652. automagically created.
  1653.  
  1654. The file C<$file> and any created parent directories (if found empty)
  1655. will be automatically removed on cleanup.
  1656.  
  1657. =item write_perlscript()
  1658.  
  1659.   $cfg->write_perlscript($filename, @lines);
  1660.  
  1661. Similar to writefile() but creates an executable Perl script with
  1662. correctly set shebang line.
  1663.  
  1664. =item gendir()
  1665.  
  1666.   $cfg->gendir($dir);
  1667.  
  1668. gendir() creates a new directory C<$dir>.
  1669.  
  1670. If parent directories of C<$dir> don't exist they will be
  1671. automagically created.
  1672.  
  1673. The directory C<$dir> and any created parent directories will be
  1674. automatically removed on cleanup if found empty.
  1675.  
  1676. =back
  1677.  
  1678. =head1 Environment Variables
  1679.  
  1680. The following environment variables affect the configuration and the
  1681. run-time of the C<Apache::Test> framework:
  1682.  
  1683. =head2 APACHE_TEST_COLOR
  1684.  
  1685. To aid visual control over the configuration process and the run-time
  1686. phase, C<Apache::Test> uses coloured fonts when the environment
  1687. variable C<APACHE_TEST_COLOR> is set to a true value.
  1688.  
  1689. =head2 APACHE_TEST_LIVE_DEV
  1690.  
  1691. When using C<Apache::Test> during the project development phase, it's
  1692. often convenient to have the I<project/lib> (live) directory appearing
  1693. first in C<@INC> so any changes to the Perl modules, residing in it,
  1694. immediately affect the server, without a need to rerun C<make> to
  1695. update I<blib/lib>. When the environment variable
  1696. C<APACHE_TEST_LIVE_DEV> is set to a true value during the
  1697. configuration phase (C<t/TEST -config>, C<Apache::Test> will
  1698. automatically unshift the I<project/lib> directory into C<@INC>, via
  1699. the autogenerated I<t/conf/modperl_inc.pl> file.
  1700.  
  1701. =head1 AUTHOR
  1702.  
  1703. =head1 SEE ALSO
  1704.  
  1705. perl(1), Apache::Test(3)
  1706.  
  1707. =cut
  1708.  
  1709.  
  1710. __DATA__
  1711. Listen     @Port@
  1712.  
  1713. ServerRoot   "@ServerRoot@"
  1714. DocumentRoot "@DocumentRoot@"
  1715.  
  1716. PidFile     @t_logs@/httpd.pid
  1717. ErrorLog    @t_logs@/error_log
  1718. LogLevel    debug
  1719.  
  1720. <IfModule mod_log_config.c>
  1721.     TransferLog @t_logs@/access_log
  1722. </IfModule>
  1723.  
  1724. ServerAdmin @ServerAdmin@
  1725.  
  1726. #needed for http/1.1 testing
  1727. KeepAlive       On
  1728.  
  1729. HostnameLookups Off
  1730.  
  1731. <Directory />
  1732.     Options FollowSymLinks
  1733.     AllowOverride None
  1734. </Directory>
  1735.  
  1736. <IfModule @THREAD_MODULE@>
  1737.     StartServers         1
  1738.     MaxClients           @MaxClients@
  1739.     MinSpareThreads      @MaxClients@
  1740.     MaxSpareThreads      @MaxClients@
  1741.     ThreadsPerChild      @MaxClients@
  1742.     MaxRequestsPerChild  0
  1743. </IfModule>
  1744.  
  1745. <IfModule perchild.c>
  1746.     NumServers           1
  1747.     StartThreads         @MaxClients@
  1748.     MinSpareThreads      @MaxClients@
  1749.     MaxSpareThreads      @MaxClients@
  1750.     MaxThreadsPerChild   @MaxClients@
  1751.     MaxRequestsPerChild  0
  1752. </IfModule>
  1753.  
  1754. <IfModule prefork.c>
  1755.     StartServers         1
  1756.     MaxClients           @MaxClients@
  1757.     MaxRequestsPerChild  0
  1758. </IfModule>
  1759.  
  1760. <IfDefine APACHE1>
  1761.     StartServers         1
  1762.     MaxClients           @MaxClients@
  1763.     MaxRequestsPerChild  0
  1764. </IfDefine>
  1765.  
  1766. <IfModule mpm_winnt.c>
  1767.     ThreadsPerChild      20
  1768.     MaxRequestsPerChild  0
  1769. </IfModule>
  1770.  
  1771. <Location /server-info>
  1772.     SetHandler server-info
  1773. </Location>
  1774.  
  1775. <Location /server-status>
  1776.     SetHandler server-status
  1777. </Location>
  1778.  
  1779.