home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / xampp / xampp-perl-addon-1.4.9-installer.exe / TestConfig.pm < prev    next >
Encoding:
Perl POD Document  |  2004-08-20  |  65.9 KB  |  2,432 lines

  1. # Copyright 2001-2004 The Apache Software Foundation
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. #     http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. #
  15. package Apache::TestConfig;
  16.  
  17. use strict;
  18. use warnings FATAL => 'all';
  19.  
  20. use constant WIN32   => $^O eq 'MSWin32';
  21. use constant OSX     => $^O eq 'darwin';
  22. use constant CYGWIN  => $^O eq 'cygwin';
  23. use constant NETWARE => $^O eq 'NetWare';
  24. use constant SOLARIS => $^O eq 'solaris';
  25. use constant WINFU   => WIN32 || CYGWIN || NETWARE;
  26. use constant COLOR   => ($ENV{APACHE_TEST_COLOR} && -t STDOUT) ? 1 : 0;
  27.  
  28. use constant DEFAULT_PORT => 8529;
  29.  
  30. use constant IS_MOD_PERL_2       =>
  31.     eval { require mod_perl && $mod_perl::VERSION >= 1.99 } || 0;
  32.  
  33. use constant IS_MOD_PERL_2_BUILD => IS_MOD_PERL_2 &&
  34.     require Apache::Build && Apache::Build::IS_MOD_PERL_BUILD();
  35.  
  36. use constant IS_APACHE_TEST_BUILD =>
  37.     grep { -e "$_/lib/Apache/TestConfig.pm" } qw(Apache-Test . ..);
  38.  
  39. use constant CUSTOM_CONFIG_FILE => 'Apache/TestConfigData.pm';
  40.  
  41. use File::Copy ();
  42. use File::Find qw(finddepth);
  43. use File::Basename qw(dirname);
  44. use File::Path ();
  45. use File::Spec::Functions qw(catfile abs2rel splitdir canonpath
  46.                              catdir file_name_is_absolute devnull);
  47. use Cwd qw(fastcwd);
  48. use Socket ();
  49. use Symbol ();
  50.  
  51. use Apache::TestConfigPerl ();
  52. use Apache::TestConfigParse ();
  53. use Apache::TestTrace;
  54. use Apache::TestServer ();
  55. use Apache::TestRun ();
  56.  
  57. use vars qw(%Usage);
  58.  
  59. # variables stored in $Apache::TestConfigData::vars
  60. my @data_vars_must = qw(httpd apxs);
  61. my @data_vars_opt  = qw(user group port);
  62. # mapping from $Apache::TestConfigData::vars to $ENV settings
  63. my %vars_to_env = (
  64.     httpd => 'APACHE_TEST_HTTPD',
  65.     apxs  => 'APACHE_TEST_APXS',
  66.     user  => 'APACHE_TEST_USER',
  67.     group => 'APACHE_TEST_GROUP',
  68.     port  => 'APACHE_TEST_PORT',
  69. );
  70.  
  71. %Usage = (
  72.    top_dir         => 'top-level directory (default is $PWD)',
  73.    t_dir           => 'the t/ test directory (default is $top_dir/t)',
  74.    t_conf          => 'the conf/ test directory (default is $t_dir/conf)',
  75.    t_logs          => 'the logs/ test directory (default is $t_dir/logs)',
  76.    t_conf_file     => 'test httpd.conf file (default is $t_conf/httpd.conf)',
  77.    src_dir         => 'source directory to look for mod_foos.so',
  78.    serverroot      => 'ServerRoot (default is $t_dir)',
  79.    documentroot    => 'DocumentRoot (default is $ServerRoot/htdocs',
  80.    port            => 'Port [port_number|select] (default ' . DEFAULT_PORT . ')',
  81.    servername      => 'ServerName (default is localhost)',
  82.    user            => 'User to run test server as (default is $USER)',
  83.    group           => 'Group to run test server as (default is $GROUP)',
  84.    bindir          => 'Apache bin/ dir (default is apxs -q BINDIR)',
  85.    sbindir         => 'Apache sbin/ dir (default is apxs -q SBINDIR)',
  86.    httpd           => 'server to use for testing (default is $bindir/httpd)',
  87.    target          => 'name of server binary (default is apxs -q TARGET)',
  88.    apxs            => 'location of apxs (default is from Apache::BuildConfig)',
  89.    startup_timeout => 'seconds to wait for the server to start (default is 60)',
  90.    httpd_conf      => 'inherit config from this file (default is apxs derived)',
  91.    httpd_conf_extra=> 'inherit additional config from this file',
  92.    minclients      => 'minimum number of concurrent clients (default is 1)',
  93.    maxclients      => 'maximum number of concurrent clients (default is minclients+1)',
  94.    perlpod         => 'location of perl pod documents (for testing downloads)',
  95.    proxyssl_url    => 'url for testing ProxyPass / https (default is localhost)',
  96.    sslca           => 'location of SSL CA (default is $t_conf/ssl/ca)',
  97.    sslcaorg        => 'SSL CA organization to use for tests (default is asf)',
  98.    libmodperl      => 'path to mod_perl\'s .so (full or relative to LIBEXECDIR)',
  99.    defines         => 'values to add as -D defines (for example, "VAR1 VAR2")',
  100.    (map { $_ . '_module_name', "$_ module name"} qw(cgi ssl thread access auth php)),
  101. );
  102.  
  103. my %filepath_conf_opts = map { $_ => 1 }
  104.     qw(top_dir t_dir t_conf t_logs t_conf_file src_dir serverroot
  105.        documentroot bindir sbindir httpd apxs httpd_conf httpd_conf_extra
  106.        perlpod sslca libmodperl);
  107.  
  108. sub conf_opt_is_a_filepath {
  109.     my $opt = shift;
  110.     $opt && exists $filepath_conf_opts{$opt};
  111. }
  112.  
  113. sub usage {
  114.     for my $hash (\%Usage) {
  115.         for (sort keys %$hash){
  116.             printf "  -%-18s %s\n", $_, $hash->{$_};
  117.         }
  118.     }
  119. }
  120.  
  121. sub filter_args {
  122.     my($args, $wanted_args) = @_;
  123.     my(@pass, %keep);
  124.  
  125.     my @filter = @$args;
  126.  
  127.     if (ref($filter[0])) {
  128.         push @pass, shift @filter;
  129.     }
  130.  
  131.     while (@filter) {
  132.         my $key = shift @filter;
  133.         # optinal - or -- prefix
  134.         if (defined $key && $key =~ /^-?-?(.+)/ && exists $wanted_args->{$1}) {
  135.             if (@filter) {
  136.                 $keep{$1} = shift @filter;
  137.             }
  138.             else {
  139.                 die "key $1 requires a matching value";
  140.             }
  141.         }
  142.         else {
  143.             push @pass, $key;
  144.         }
  145.     }
  146.  
  147.     return (\@pass, \%keep);
  148. }
  149.  
  150. my %passenv = map { $_,1 } qw{
  151.     APACHE_TEST_APXS
  152.     APACHE_TEST_HTTPD
  153.     APACHE_TEST_GROUP
  154.     APACHE_TEST_USER
  155.     APACHE_TEST_PORT
  156. };
  157.  
  158. sub passenv {
  159.     \%passenv;
  160. }
  161.  
  162. sub passenv_makestr {
  163.     my @vars;
  164.  
  165.     for (keys %passenv) {
  166.         push @vars, "$_=\$($_)";
  167.     }
  168.  
  169.     "@vars";
  170. }
  171.  
  172. sub server { shift->{server} }
  173.  
  174. sub modperl_2_inc_fixup {
  175.     (IS_MOD_PERL_2 && !IS_MOD_PERL_2_BUILD) ? "use Apache2;\n" : '';
  176. }
  177.  
  178. sub modperl_build_config {
  179.     eval {
  180.         require Apache::Build;
  181.     } or return undef;
  182.     return Apache::Build->build_config;
  183. }
  184.  
  185. sub new_test_server {
  186.     my($self, $args) = @_;
  187.     Apache::TestServer->new($args || $self)
  188. }
  189.  
  190. # setup httpd-independent components
  191. # for httpd-specific call $self->httpd_config()
  192. sub new {
  193.     my $class = shift;
  194.  
  195.     my $args;
  196.  
  197.     $args = shift if $_[0] and ref $_[0];
  198.  
  199.     $args = $args ? {%$args} : {@_}; #copy
  200.  
  201.     #see Apache::TestMM::{filter_args,generate_script}
  202.     #we do this so 'perl Makefile.PL' can be passed options such as apxs
  203.     #without forcing regeneration of configuration and recompilation of c-modules
  204.     #as 't/TEST apxs /path/to/apache/bin/apxs' would do
  205.     while (my($key, $val) = each %Apache::TestConfig::Argv) {
  206.         $args->{$key} = $val;
  207.     }
  208.  
  209.     my $thaw = {};
  210.  
  211.     #thaw current config
  212.     for (qw(conf t/conf)) {
  213.         last if eval {
  214.             require "$_/apache_test_config.pm";
  215.             $thaw = 'apache_test_config'->new;
  216.             delete $thaw->{save};
  217.             #incase class that generated the config was
  218.             #something else, which we can't be sure how to load
  219.             bless $thaw, 'Apache::TestConfig';
  220.         };
  221.     };
  222.  
  223.     if ($args->{thaw} and ref($thaw) ne 'HASH') {
  224.         #dont generate any new config
  225.         $thaw->{vars}->{$_} = $args->{$_} for keys %$args;
  226.         $thaw->{server} = $thaw->new_test_server;
  227.         $thaw->add_inc;
  228.         return $thaw;
  229.     }
  230.  
  231.     #regenerating config, so forget old
  232.     if ($args->{save}) {
  233.         for (qw(vhosts inherit_config modules inc cmodules)) {
  234.             delete $thaw->{$_} if exists $thaw->{$_};
  235.         }
  236.     }
  237.  
  238.     # custom config options from Apache::TestConfigData
  239.     # again, this should force reconfiguration
  240.     custom_config_add_conf_opts($args);
  241.  
  242.     my $self = bless {
  243.         clean => {},
  244.         vhosts => {},
  245.         inherit_config => {},
  246.         modules => {},
  247.         inc => [],
  248.         %$thaw,
  249.         mpm => "",
  250.         httpd_defines => {},
  251.         vars => $args,
  252.         postamble => [],
  253.         preamble => [],
  254.         postamble_hooks => [],
  255.         preamble_hooks => [],
  256.     }, ref($class) || $class;
  257.  
  258.     my $vars = $self->{vars}; #things that can be overridden
  259.  
  260.     for (qw(save verbose)) {
  261.         next unless exists $args->{$_};
  262.         $self->{$_} = delete $args->{$_};
  263.     }
  264.  
  265.     $vars->{top_dir} ||= fastcwd;
  266.     $vars->{top_dir} = pop_dir($vars->{top_dir}, 't');
  267.  
  268.     $self->add_inc;
  269.  
  270.     #help to find libmodperl.so
  271.     my $src_dir = catfile $vars->{top_dir}, qw(src modules perl);
  272.     $vars->{src_dir}      ||= $src_dir if -d $src_dir;
  273.  
  274.     $vars->{t_dir}        ||= catfile $vars->{top_dir}, 't';
  275.     $vars->{serverroot}   ||= $vars->{t_dir};
  276.     $vars->{documentroot} ||= catfile $vars->{serverroot}, 'htdocs';
  277.     $vars->{perlpod}      ||= $self->find_in_inc('pods') ||
  278.                               $self->find_in_inc('pod');
  279.     $vars->{perl}         ||= $^X;
  280.     $vars->{t_conf}       ||= catfile $vars->{serverroot}, 'conf';
  281.     $vars->{sslca}        ||= catfile $vars->{t_conf}, 'ssl', 'ca';
  282.     $vars->{sslcaorg}     ||= 'asf';
  283.     $vars->{t_logs}       ||= catfile $vars->{serverroot}, 'logs';
  284.     $vars->{t_conf_file}  ||= catfile $vars->{t_conf},   'httpd.conf';
  285.  
  286.     if (WINFU) {
  287.         for (keys %$vars) {
  288.             $vars->{$_} =~ s|\\|\/|g if defined $vars->{$_};
  289.         }
  290.     }
  291.  
  292.     $vars->{scheme}       ||= 'http';
  293.     $vars->{servername}   ||= $self->default_servername;
  294.     $vars->{port}           = $self->select_first_port;
  295.     $vars->{remote_addr}  ||= $self->our_remote_addr;
  296.  
  297.     $vars->{user}         ||= $self->default_user;
  298.     $vars->{group}        ||= $self->default_group;
  299.     $vars->{serveradmin}  ||= $self->default_serveradmin;
  300.  
  301.     $vars->{minclients}   ||= 1;
  302.     my $maxclientspreset = $vars->{maxclients} || 0;
  303.     # prevent 'server reached MaxClients setting' errors
  304.     $vars->{maxclients}   ||= $vars->{minclients} + 1;
  305.     # for threaded mpms MaxClients must be a multiple of
  306.     # ThreadsPerChild (i.e. maxclients % minclients == 0)
  307.     # so unless -maxclients was explicitly specified use a double of
  308.     # minclients
  309.     $vars->{maxclientsthreadedmpm} = 
  310.         $maxclientspreset || $vars->{minclients} * 2;
  311.  
  312.     $vars->{proxy}        ||= 'off';
  313.     $vars->{proxyssl_url} ||= '';
  314.     $vars->{defines}      ||= '';
  315.  
  316.     $self->{hostport} = $self->hostport;
  317.     $self->{server} = $self->new_test_server;
  318.  
  319.     return $self;
  320.  
  321. }
  322.  
  323. # figure out where httpd is and run extra config hooks which require
  324. # knowledge of where httpd is
  325. sub httpd_config {
  326.     my $self = shift;
  327.  
  328.     my $vars = $self->{vars};
  329.  
  330.     $self->configure_apxs;
  331.     $self->configure_httpd;
  332.  
  333.     unless ($vars->{httpd} or $vars->{apxs}) {
  334.         if ($ENV{APACHE_TEST_NO_STICKY_PREFERENCES}) {
  335.             error "You specified APACHE_TEST_NO_STICKY_PREFERENCES=1 " .
  336.                 "in which case you must explicitly specify -httpd " .
  337.                 "and/or -apxs options";
  338.             Apache::TestRun::exit_perl(0);
  339.         }
  340.  
  341.         $self->clean(1);
  342.         # this method restarts the whole program via exec
  343.         # so it never returns
  344.         $self->custom_config_first_time($self->{vars});
  345.     }
  346.  
  347.     # if we have gotten that far we know at least about the location
  348.     # of httpd and or apxs, so let's save it if we haven't saved any
  349.     # custom configs yet
  350.     unless (custom_config_exists()) {
  351.         $self->custom_config_save($self->{vars});
  352.     }
  353.  
  354.     $self->inherit_config; #see TestConfigParse.pm
  355.     $self->configure_httpd_eapi; #must come after inherit_config
  356.  
  357.     $self->default_module(cgi    => [qw(mod_cgi mod_cgid)]);
  358.     $self->default_module(thread => [qw(worker threaded)]);
  359.     $self->default_module(ssl    => [qw(mod_ssl)]);
  360.     $self->default_module(access => [qw(mod_access mod_authz_host)]);
  361.     $self->default_module(auth   => [qw(mod_auth mod_auth_basic)]);
  362.     $self->default_module(php    => [qw(mod_php4 mod_php5)]);
  363.  
  364.     $self->{server}->post_config;
  365.  
  366.     $self;
  367. }
  368.  
  369. sub default_module {
  370.     my($self, $name, $choices) = @_;
  371.  
  372.     my $mname = $name . '_module_name';
  373.  
  374.     unless ($self->{vars}->{$mname}) {
  375.         ($self->{vars}->{$mname}) = grep {
  376.             $self->{modules}->{"$_.c"};
  377.         } @$choices;
  378.  
  379.         $self->{vars}->{$mname} ||= $choices->[0];
  380.     }
  381.  
  382.     $self->{vars}->{$name . '_module'} =
  383.       $self->{vars}->{$mname} . '.c'
  384. }
  385.  
  386. sub configure_apxs {
  387.     my $self = shift;
  388.  
  389.     $self->{APXS} = $self->default_apxs;
  390.  
  391.     return unless $self->{APXS};
  392.  
  393.     $self->{APXS} =~ s{/}{\\}g if WIN32;
  394.  
  395.     my $vars = $self->{vars};
  396.  
  397.     $vars->{bindir}   ||= $self->apxs('BINDIR', 1);
  398.     $vars->{sbindir}  ||= $self->apxs('SBINDIR');
  399.     $vars->{target}   ||= $self->apxs('TARGET');
  400.     $vars->{conf_dir} ||= $self->apxs('SYSCONFDIR');
  401.  
  402.     if ($vars->{conf_dir}) {
  403.         $vars->{httpd_conf} ||= catfile $vars->{conf_dir}, 'httpd.conf';
  404.     }
  405. }
  406.  
  407. sub configure_httpd {
  408.     my $self = shift;
  409.     my $vars = $self->{vars};
  410.  
  411.     debug "configuring httpd";
  412.  
  413.     $vars->{target} ||= (WIN32 ? 'Apache.EXE' : 'httpd');
  414.  
  415.     unless ($vars->{httpd}) {
  416.         #sbindir should be bin/ with the default layout
  417.         #but its eaiser to workaround apxs than fix apxs
  418.         for my $dir (map { $vars->{$_} } qw(sbindir bindir)) {
  419.             next unless defined $dir;
  420.             my $httpd = catfile $dir, $vars->{target};
  421.             next unless -x $httpd;
  422.             $vars->{httpd} = $httpd;
  423.             last;
  424.         }
  425.  
  426.         $vars->{httpd} ||= $self->default_httpd;
  427.     }
  428.  
  429.     if ($vars->{httpd}) {
  430.         my @chunks = splitdir $vars->{httpd};
  431.         #handle both $prefix/bin/httpd and $prefix/Apache.exe
  432.         for (1,2) {
  433.             pop @chunks;
  434.             last unless @chunks;
  435.             $self->{httpd_basedir} = catfile @chunks;
  436.             last if -d "$self->{httpd_basedir}/bin";
  437.         }
  438.     }
  439.  
  440.     #cleanup httpd droppings
  441.     my $sem = catfile $vars->{t_logs}, 'apache_runtime_status.sem';
  442.     unless (-e $sem) {
  443.         $self->clean_add_file($sem);
  444.     }
  445. }
  446.  
  447. sub configure_httpd_eapi {
  448.     my $self = shift;
  449.     my $vars = $self->{vars};
  450.  
  451.     #deal with EAPI_MM_CORE_PATH if defined.
  452.     if (defined($self->{httpd_defines}->{EAPI_MM_CORE_PATH})) {
  453.         my $path = $self->{httpd_defines}->{EAPI_MM_CORE_PATH};
  454.  
  455.         #ensure the directory exists
  456.         my @chunks = splitdir $path;
  457.         pop @chunks; #the file component of the path
  458.         $path = catdir @chunks;
  459.         unless (file_name_is_absolute $path) {
  460.             $path = catdir $vars->{serverroot}, $path;
  461.         }
  462.         $self->gendir($path);
  463.     }
  464. }
  465.  
  466. sub configure_proxy {
  467.     my $self = shift;
  468.     my $vars = $self->{vars};
  469.  
  470.     #if we proxy to ourselves, must bump the maxclients
  471.     if ($vars->{proxy} =~ /^on$/i) {
  472.         $vars->{minclients}++;
  473.         $vars->{maxclients}++;
  474.         $vars->{proxy} = $self->{vhosts}->{'mod_proxy'}->{hostport};
  475.         return $vars->{proxy};
  476.     }
  477.  
  478.     return undef;
  479. }
  480.  
  481. # adds the config to the head of the group instead of the tail
  482. # XXX: would be even better to add to a different sub-group
  483. # (e.g. preamble_first) of only those that want to be first and then,
  484. # make sure that they are dumped to the config file first in the same
  485. # group (e.g. preamble)
  486. sub add_config_first {
  487.     my $self = shift;
  488.     my $where = shift;
  489.     unshift @{ $self->{$where} }, $self->massage_config_args(@_);
  490. }
  491.  
  492. sub add_config_last {
  493.     my $self = shift;
  494.     my $where = shift;
  495.     push @{ $self->{$where} }, $self->massage_config_args(@_);
  496. }
  497.  
  498. sub massage_config_args {
  499.     my $self = shift;
  500.     my($directive, $arg, $data) = @_;
  501.     my $args = "";
  502.  
  503.     if ($data) {
  504.         $args = "<$directive $arg>\n";
  505.         if (ref($data) eq 'HASH') {
  506.             while (my($k,$v) = each %$data) {
  507.                 $args .= "    $k $v\n";
  508.             }
  509.         }
  510.         elsif (ref($data) eq 'ARRAY') {
  511.             # balanced (key=>val) list
  512.             my $pairs = @$data / 2;
  513.             for my $i (0..($pairs-1)) {
  514.                 $args .= sprintf "    %s %s\n", $data->[$i*2], $data->[$i*2+1];
  515.             }
  516.         }
  517.         else {
  518.             $args .= "    $data";
  519.         }
  520.         $args .= "</$directive>\n";
  521.     }
  522.     elsif (ref($directive) eq 'ARRAY') {
  523.         $args = join "\n", @$directive;
  524.     }
  525.     else {
  526.         $args = join " ", grep length($_), $directive,
  527.           (ref($arg) && (ref($arg) eq 'ARRAY') ? "@$arg" : $arg || "");
  528.     }
  529.  
  530.     return $args;
  531. }
  532.  
  533. sub postamble_first {
  534.     shift->add_config_first(postamble => @_);
  535. }
  536.  
  537. sub postamble {
  538.     shift->add_config_last(postamble => @_);
  539. }
  540.  
  541. sub preamble_first {
  542.     shift->add_config_first(preamble => @_);
  543. }
  544.  
  545. sub preamble {
  546.     shift->add_config_last(preamble => @_);
  547. }
  548.  
  549. sub postamble_register {
  550.     push @{ shift->{postamble_hooks} }, @_;
  551. }
  552.  
  553. sub preamble_register {
  554.     push @{ shift->{preamble_hooks} }, @_;
  555. }
  556.  
  557. sub add_config_hooks_run {
  558.     my($self, $where, $out) = @_;
  559.  
  560.     for (@{ $self->{"${where}_hooks"} }) {
  561.         if ((ref($_) and ref($_) eq 'CODE') or $self->can($_)) {
  562.             $self->$_();
  563.         }
  564.         else {
  565.             error "cannot run configure hook: `$_'";
  566.         }
  567.     }
  568.  
  569.     for (@{ $self->{$where} }) {
  570.         $self->replace;
  571.         print $out "$_\n";
  572.     }
  573. }
  574.  
  575. sub postamble_run {
  576.     shift->add_config_hooks_run(postamble => @_);
  577. }
  578.  
  579. sub preamble_run {
  580.     shift->add_config_hooks_run(preamble => @_);
  581. }
  582.  
  583. sub default_group {
  584.     return if WINFU;
  585.  
  586.     my $gid = $);
  587.  
  588.     #use only first value if $) contains more than one
  589.     $gid =~ s/^(\d+).*$/$1/;
  590.  
  591.     my $group = $ENV{APACHE_TEST_GROUP} || (getgrgid($gid) || "#$gid");
  592.  
  593.     if ($group eq 'root') {
  594.         # similar to default_user, we want to avoid perms problems,
  595.         # when the server is started with group 'root'. When running
  596.         # under group root it may fail to create dirs and files,
  597.         # writable only by user
  598.         my $user = default_user();
  599.         my $gid = $user ? (getpwnam($user))[3] : '';
  600.         $group = (getgrgid($gid) || "#$gid") if $gid;
  601.     }
  602.  
  603.     $group;
  604. }
  605.  
  606. sub default_user {
  607.     return if WINFU;
  608.  
  609.     my $uid = $>;
  610.  
  611.     my $user = $ENV{APACHE_TEST_USER} || (getpwuid($uid) || "#$uid");
  612.  
  613.     if ($user eq 'root') {
  614.         my $other = (getpwnam('nobody'))[0];
  615.         if ($other) {
  616.             $user = $other;
  617.         }
  618.         else {
  619.             die "cannot run tests as User root";
  620.             #XXX: prompt for another username
  621.         }
  622.     }
  623.  
  624.     $user;
  625. }
  626.  
  627. sub default_serveradmin {
  628.     my $vars = shift->{vars};
  629.     join '@', ($vars->{user} || 'unknown'), $vars->{servername};
  630. }
  631.  
  632. sub default_apxs {
  633.     my $self = shift;
  634.  
  635.     return $self->{vars}->{apxs} if $self->{vars}->{apxs};
  636.  
  637.     if (my $build_config = modperl_build_config()) {
  638.         return $build_config->{MP_APXS};
  639.     }
  640.  
  641.     $ENV{APACHE_TEST_APXS};
  642. }
  643.  
  644. sub default_httpd {
  645.     my $vars = shift->{vars};
  646.  
  647.     if (my $build_config = modperl_build_config()) {
  648.         if (my $p = $build_config->{MP_AP_PREFIX}) {
  649.             for my $bindir (qw(bin sbin)) {
  650.                 my $httpd = catfile $p, $bindir, $vars->{target};
  651.                 return $httpd if -e $httpd;
  652.             }
  653.         }
  654.     }
  655.  
  656.     $ENV{APACHE_TEST_HTTPD};
  657. }
  658.  
  659. my $localhost;
  660.  
  661. sub default_localhost {
  662.     my $localhost_addr = pack('C4', 127, 0, 0, 1);
  663.     gethostbyaddr($localhost_addr, Socket::AF_INET()) || 'localhost';
  664. }
  665.  
  666. sub default_servername {
  667.     my $self = shift;
  668.     $localhost ||= $self->default_localhost;
  669.     die "Can't figure out the default localhost's server name"
  670.         unless $localhost;
  671. }
  672.  
  673. # memoize the selected value (so we make sure that the same port is used
  674. # via select). The problem is that select_first_port() is called 3 times after
  675. # -clean, and it's possible that a lower port will get released
  676. # between calls, leading to various places in the test suite getting a
  677. # different base port selection.
  678. #
  679. # XXX: There is still a problem if two t/TEST's configure at the same
  680. # time, so they both see the same port free, but only the first one to
  681. # bind() will actually get the port. So there is a need in another
  682. # check and reconfiguration just before the server starts.
  683. #
  684. my $port_memoized;
  685. sub select_first_port {
  686.     my $self = shift;
  687.  
  688.     my $port ||= $port_memoized || $ENV{APACHE_TEST_PORT} 
  689.         || $self->{vars}{port} || DEFAULT_PORT;
  690.  
  691.     # memoize
  692.     $port_memoized = $port;
  693.  
  694.     return $port unless $port eq 'select';
  695.  
  696.     # port select mode: try to find another available port, take into
  697.     # account that each instance of the test suite may use more than
  698.     # one port for virtual hosts, therefore try to check ports in big
  699.     # steps (20?).
  700.     my $step  = 20;
  701.     my $tries = 20;
  702.     $port = DEFAULT_PORT;
  703.     until (Apache::TestServer->port_available($port)) {
  704.         unless (--$tries) {
  705.             error "no ports available";
  706.             error "tried ports @{[DEFAULT_PORT]} - $port in $step increments";
  707.             return 0;
  708.         }
  709.         $port += $step;
  710.     }
  711.  
  712.     info "the default base port is used, using base port $port instead"
  713.         unless $port == DEFAULT_PORT;
  714.  
  715.     # memoize
  716.     $port_memoized = $port;
  717.  
  718.     return $port;
  719. }
  720.  
  721. my $remote_addr;
  722.  
  723. sub our_remote_addr {
  724.     my $self = shift;
  725.     my $name = $self->default_servername;
  726.     my $iaddr = (gethostbyname($name))[-1];
  727.     unless (defined $iaddr) {
  728.         error "Can't resolve host: '$name' (check /etc/hosts)";
  729.         exit 1;
  730.     }
  731.     $remote_addr ||= Socket::inet_ntoa($iaddr);
  732. }
  733.  
  734. sub default_loopback {
  735.     '127.0.0.1';
  736. }
  737.  
  738. sub port {
  739.     my($self, $module) = @_;
  740.  
  741.     unless ($module) {
  742.         my $vars = $self->{vars};
  743.         return $self->select_first_port() unless $vars->{scheme} eq 'https';
  744.         $module = $vars->{ssl_module_name};
  745.     }
  746.     return $self->{vhosts}->{$module}->{port};
  747. }
  748.  
  749. sub hostport {
  750.     my $self = shift;
  751.     my $vars = shift || $self->{vars};
  752.     my $module = shift || '';
  753.  
  754.     my $name = $vars->{servername};
  755.  
  756.     join ':', $name , $self->port($module || '');
  757. }
  758.  
  759. #look for mod_foo.so
  760. sub find_apache_module {
  761.     my($self, $module) = @_;
  762.  
  763.     die "find_apache_module: module name argument is required"
  764.         unless $module;
  765.  
  766.     my $vars = $self->{vars};
  767.     my $sroot = $vars->{serverroot};
  768.  
  769.     my @trys = grep { $_ }
  770.       ($vars->{src_dir},
  771.        $self->apxs('LIBEXECDIR'),
  772.        catfile($sroot, 'modules'),
  773.        catfile($sroot, 'libexec'));
  774.  
  775.     for (@trys) {
  776.         my $file = catfile $_, $module;
  777.         if (-e $file) {
  778.             debug "found $module => $file";
  779.             return $file;
  780.         }
  781.     }
  782.  
  783.     # if the module wasn't found try to lookup in the list of modules
  784.     # inherited from the system-wide httpd.conf
  785.     my $name = $module;
  786.     $name =~ s/\.s[ol]$/.c/;  #mod_info.so => mod_info.c
  787.     $name =~ s/^lib/mod_/; #libphp4.so => mod_php4.c
  788.     return $self->{modules}->{$name} if $self->{modules}->{$name};
  789.  
  790. }
  791.  
  792. #generate files and directories
  793.  
  794. my %warn_style = (
  795.     html    => sub { "<!-- @_ -->" },
  796.     c       => sub { "/* @_ */" },
  797.     default => sub { join '', grep {s/^/\# /gm} @_ },
  798. );
  799.  
  800. my %file_ext = (
  801.     map({$_ => 'html'} qw(htm html)),
  802.     map({$_ => 'c'   } qw(c h)),
  803. );
  804.  
  805. # return the passed file's extension or '' if there is no one
  806. # note: that '/foo/bar.conf.in' returns an extension: 'conf.in';
  807. # note: a hidden file .foo will be recognized as an extension 'foo'
  808. sub filename_ext {
  809.     my ($self, $filename) = @_;
  810.     my $ext = (File::Basename::fileparse($filename, '\..*'))[2] || '';
  811.     $ext =~ s/^\.(.*)/lc $1/e;
  812.     $ext;
  813. }
  814.  
  815. sub warn_style_sub_ref {
  816.     my ($self, $filename) = @_;
  817.     my $ext = $self->filename_ext($filename);
  818.     return $warn_style{ $file_ext{$ext} || 'default' };
  819. }
  820.  
  821. sub genwarning {
  822.     my($self, $filename, $from_filename) = @_;
  823.     return unless $filename;
  824.     my $warning = "WARNING: this file is generated";
  825.     $warning .= " (from $from_filename)" if defined $from_filename;
  826.     $warning .= ", do not edit\n";
  827.     $warning .= calls_trace();
  828.     return $self->warn_style_sub_ref($filename)->($warning);
  829. }
  830.  
  831. sub calls_trace {
  832.     my $frame = 1;
  833.     my $trace = '';
  834.  
  835.     while (1) {
  836.         my($package, $filename, $line) = caller($frame);
  837.         last unless $filename;
  838.         $trace .= sprintf "%02d: %s:%d\n", $frame, $filename, $line;
  839.         $frame++;
  840.     }
  841.  
  842.     return $trace;
  843. }
  844.  
  845. sub clean_add_file {
  846.     my($self, $file) = @_;
  847.  
  848.     $self->{clean}->{files}->{ rel2abs($file) } = 1;
  849. }
  850.  
  851. sub clean_add_path {
  852.     my($self, $path) = @_;
  853.  
  854.     $path = rel2abs($path);
  855.  
  856.     # remember which dirs were created and should be cleaned up
  857.     while (1) {
  858.         $self->{clean}->{dirs}->{$path} = 1;
  859.         $path = dirname $path;
  860.         last if -e $path;
  861.     }
  862. }
  863.  
  864. sub genfile_trace {
  865.     my($self, $file, $from_file) = @_;
  866.     my $name = abs2rel $file, $self->{vars}->{t_dir};
  867.     my $msg = "generating $name";
  868.     $msg .= " from $from_file" if defined $from_file;
  869.     debug $msg;
  870. }
  871.  
  872. sub genfile_warning {
  873.     my($self, $file, $from_file, $fh) = @_;
  874.  
  875.     if (my $msg = $self->genwarning($file, $from_file)) {
  876.         print $fh $msg, "\n";
  877.     }
  878. }
  879.  
  880. # $from_file == undef if there was no templates used
  881. sub genfile {
  882.     my($self, $file, $from_file, $nowarning) = @_;
  883.  
  884.     # create the parent dir if it doesn't exist yet
  885.     my $dir = dirname $file;
  886.     $self->makepath($dir);
  887.  
  888.     $self->genfile_trace($file, $from_file);
  889.  
  890.     my $fh = Symbol::gensym();
  891.     open $fh, ">$file" or die "open $file: $!";
  892.  
  893.     $self->genfile_warning($file, $from_file, $fh) unless $nowarning;
  894.  
  895.     $self->clean_add_file($file);
  896.  
  897.     return $fh;
  898. }
  899.  
  900. # gen + write file
  901. sub writefile {
  902.     my($self, $file, $content, $nowarning) = @_;
  903.  
  904.     my $fh = $self->genfile($file, undef, $nowarning);
  905.  
  906.     print $fh $content if $content;
  907.  
  908.     close $fh;
  909. }
  910.  
  911. sub perlscript_header {
  912.  
  913.     require FindBin;
  914.  
  915.     my @dirs = ();
  916.  
  917.     # mp2 needs its modper-2.0/lib before blib was created
  918.     if (IS_MOD_PERL_2_BUILD || $ENV{APACHE_TEST_LIVE_DEV}) {
  919.         # the live 'lib/' dir of the distro
  920.         # (e.g. modperl-2.0/ModPerl-Registry/lib)
  921.         my $dir = canonpath catdir $FindBin::Bin, "lib";
  922.         push @dirs, $dir if -d $dir;
  923.  
  924.         # the live dir of the top dir if any  (e.g. modperl-2.0/lib)
  925.         if (-e catfile($FindBin::Bin, "..", "Makefile.PL")) {
  926.             my $dir = canonpath catdir $FindBin::Bin, "..", "lib";
  927.             push @dirs, $dir if -d $dir;
  928.         }
  929.     }
  930.  
  931.     for (qw(. ..)) {
  932.         my $dir = canonpath catdir $FindBin::Bin, $_ , "Apache-Test", "lib";
  933.         if (-d $dir) {
  934.             push @dirs, $dir;
  935.             last;
  936.         }
  937.     }
  938.  
  939.     {
  940.         my $dir = canonpath catdir $FindBin::Bin, "t", "lib";
  941.         push @dirs, $dir if -d $dir;
  942.     }
  943.  
  944.     my $dirs = join("\n    ", '', @dirs) . "\n";;
  945.  
  946.     return <<"EOF";
  947.  
  948. use strict;
  949. use warnings FATAL => 'all';
  950.  
  951. use lib qw($dirs);
  952.  
  953. EOF
  954. }
  955.  
  956. # gen + write executable perl script file
  957. sub write_perlscript {
  958.     my($self, $file, $content) = @_;
  959.  
  960.     my $fh = $self->genfile($file, undef, 1);
  961.  
  962.     # shebang
  963.     print $fh "#!$Config{perlpath}\n";
  964.  
  965.     $self->genfile_warning($file, undef, $fh);
  966.  
  967.     print $fh $content if $content;
  968.  
  969.     close $fh;
  970.     chmod 0755, $file;
  971. }
  972.  
  973. sub cpfile {
  974.     my($self, $from, $to) = @_;
  975.     File::Copy::copy($from, $to);
  976.     $self->clean_add_file($to);
  977. }
  978.  
  979. sub symlink {
  980.     my($self, $from, $to) = @_;
  981.     CORE::symlink($from, $to);
  982.     $self->clean_add_file($to);
  983. }
  984.  
  985. sub gendir {
  986.     my($self, $dir) = @_;
  987.     $self->makepath($dir);
  988. }
  989.  
  990. # returns a list of dirs successfully created
  991. sub makepath {
  992.     my($self, $path) = @_;
  993.  
  994.     return if !defined($path) || -e $path;
  995.  
  996.     $self->clean_add_path($path);
  997.  
  998.     return File::Path::mkpath($path, 0, 0755);
  999. }
  1000.  
  1001. sub open_cmd {
  1002.     my($self, $cmd) = @_;
  1003.     # untaint some %ENV fields
  1004.     local @ENV{ qw(PATH IFS CDPATH ENV BASH_ENV) };
  1005.  
  1006.     my $handle = Symbol::gensym();
  1007.     open $handle, "$cmd|" or die "$cmd failed: $!";
  1008.  
  1009.     return $handle;
  1010. }
  1011.  
  1012. sub clean {
  1013.     my $self = shift;
  1014.     $self->{clean_level} = shift || 2; #2 == really clean, 1 == reconfigure
  1015.  
  1016.     $self->new_test_server->clean;
  1017.     $self->cmodules_clean;
  1018.     $self->sslca_clean;
  1019.  
  1020.     for (keys %{ $self->{clean}->{files} }) {
  1021.         if (-e $_) {
  1022.             debug "unlink $_";
  1023.             unlink $_;
  1024.         }
  1025.         else {
  1026.             debug "unlink $_: $!";
  1027.         }
  1028.     }
  1029.  
  1030.     # if /foo comes before /foo/bar, /foo will never be removed
  1031.     # hence ensure that sub-dirs are always treated before a parent dir
  1032.     for (reverse sort keys %{ $self->{clean}->{dirs} }) {
  1033.         if (-d $_) {
  1034.             my $dh = Symbol::gensym();
  1035.             opendir($dh, $_);
  1036.             my $notempty = grep { ! /^\.{1,2}$/ } readdir $dh;
  1037.             closedir $dh;
  1038.             next if $notempty;
  1039.             debug "rmdir $_";
  1040.             rmdir $_;
  1041.         }
  1042.     }
  1043. }
  1044.  
  1045. sub replace {
  1046.     my $self = shift;
  1047.     my $file = $Apache::TestConfig::File
  1048.         ? "in file $Apache::TestConfig::File" : '';
  1049.  
  1050.     s[@(\w+)@]
  1051.      [ my $key = lc $1;
  1052.       exists $self->{vars}->{$key}
  1053.       ? $self->{vars}->{$key}
  1054.       : die "invalid token: \@$1\@ $file\n";
  1055.      ]ge;
  1056. }
  1057.  
  1058. #need to configure the vhost port for redirects and $ENV{SERVER_PORT}
  1059. #to have the correct values
  1060. my %servername_config = (
  1061.     0 => sub {
  1062.         my($name, $port) = @_;
  1063.         [ServerName => ''], [Port => 0];
  1064.     },
  1065.     1 => sub {
  1066.         my($name, $port) = @_;
  1067.         [ServerName => $name], [Port => $port];
  1068.     },
  1069.     2 => sub {
  1070.         my($name, $port) = @_;
  1071.         [ServerName => "$name:$port"];
  1072.     },
  1073. );
  1074.  
  1075. sub servername_config {
  1076.     my $self = shift;
  1077.     $self->server->version_of(\%servername_config)->(@_);
  1078. }
  1079.  
  1080. sub parse_vhost {
  1081.     my($self, $line) = @_;
  1082.  
  1083.     my($indent, $module, $namebased);
  1084.     if ($line =~ /^(\s*)<VirtualHost\s+(?:_default_:|([^:]+):(?!:))?(.*?)\s*>\s*$/) {
  1085.         $indent    = $1 || "";
  1086.         $namebased = $2 || "";
  1087.         $module    = $3;
  1088.     }
  1089.     else {
  1090.         return undef;
  1091.     }
  1092.  
  1093.     my $vars = $self->{vars};
  1094.     my $mods = $self->{modules};
  1095.     my $have_module = "$module.c";
  1096.     my $ssl_module = $vars->{ssl_module};
  1097.  
  1098.     #if module ends with _ssl and it is not the module that implements ssl,
  1099.     #then assume this module is a vhost with SSLEngine On (or similar)
  1100.     #see mod_echo in extra.conf.in for example
  1101.     if ($module =~ /^(mod_\w+)_ssl$/ and $have_module ne $ssl_module) {
  1102.         $have_module = "$1.c"; #e.g. s/mod_echo_ssl.c/mod_echo.c/
  1103.         return undef unless $mods->{$ssl_module};
  1104.     }
  1105.  
  1106.     #don't allocate a port if this module is not configured
  1107.     #assumes the configuration is inside an <IfModule $have_module>
  1108.     if ($module =~ /^mod_/ and not $mods->{$have_module}) {
  1109.         return undef;
  1110.     }
  1111.  
  1112.     #allocate a port and configure this module into $self->{vhosts}
  1113.     my $port = $self->new_vhost($module, $namebased);
  1114.  
  1115.     #extra config that should go *inside* the <VirtualHost ...>
  1116.     my @in_config = $self->servername_config($namebased
  1117.                                                  ? $namebased
  1118.                                                  : $vars->{servername},
  1119.                                              $port);
  1120.  
  1121.     my @out_config = ();
  1122.     if ($self->{vhosts}->{$module}->{namebased} < 2) {
  1123.         #extra config that should go *outside* the <VirtualHost ...>
  1124.         @out_config = ([Listen => '0.0.0.0:' . $port]);
  1125.  
  1126.         if ($self->{vhosts}->{$module}->{namebased}) {
  1127.             push @out_config => [NameVirtualHost => "*:$port"];
  1128.         }
  1129.     }
  1130.  
  1131.     #there are two ways of building a vhost
  1132.     #first is when we parse test .pm and .c files
  1133.     #second is when we scan *.conf.in
  1134.     my $form_postamble = sub {
  1135.         my $indent = shift;
  1136.         for my $pair (@_) {
  1137.             $self->postamble("$indent@$pair");
  1138.         }
  1139.     };
  1140.  
  1141.     my $form_string = sub {
  1142.         my $indent = shift;
  1143.         join "\n", map { "$indent@$_\n" } @_;
  1144.     };
  1145.  
  1146.     my $double_indent = $indent ? $indent x 2 : ' ' x 4;
  1147.     return {
  1148.         port          => $port,
  1149.         #used when parsing .pm and .c test modules
  1150.         in_postamble  => sub { $form_postamble->($double_indent, @in_config) },
  1151.         out_postamble => sub { $form_postamble->($indent, @out_config) },
  1152.         #used when parsing *.conf.in files
  1153.         in_string     => $form_string->($double_indent, @in_config),
  1154.         out_string    => $form_string->($indent, @out_config),
  1155.         line          => "$indent<VirtualHost " . ($namebased ? '*' : '_default_') .
  1156.                          ":$port>",
  1157.     };
  1158. }
  1159.  
  1160. sub replace_vhost_modules {
  1161.     my $self = shift;
  1162.  
  1163.     if (my $cfg = $self->parse_vhost($_)) {
  1164.         $_ = '';
  1165.         for my $key (qw(out_string line in_string)) {
  1166.             next unless $cfg->{$key};
  1167.             $_ .= "$cfg->{$key}\n";
  1168.         }
  1169.     }
  1170. }
  1171.  
  1172. sub replace_vars {
  1173.     my($self, $in, $out) = @_;
  1174.  
  1175.     local $_;
  1176.     while (<$in>) {
  1177.         $self->replace;
  1178.         $self->replace_vhost_modules;
  1179.         print $out $_;
  1180.     }
  1181. }
  1182.  
  1183. sub index_html_template {
  1184.     my $self = shift;
  1185.     return "welcome to $self->{server}->{name}\n";
  1186. }
  1187.  
  1188. sub generate_index_html {
  1189.     my $self = shift;
  1190.     my $dir = $self->{vars}->{documentroot};
  1191.     $self->gendir($dir);
  1192.     my $file = catfile $dir, 'index.html';
  1193.     return if -e $file;
  1194.     my $fh = $self->genfile($file);
  1195.     print $fh $self->index_html_template;
  1196. }
  1197.  
  1198. sub types_config_template {
  1199.     return <<EOF;
  1200. text/html  html htm
  1201. image/gif  gif
  1202. image/jpeg jpeg jpg jpe
  1203. image/png  png
  1204. text/plain asc txt
  1205. EOF
  1206. }
  1207.  
  1208. sub generate_types_config {
  1209.     my $self = shift;
  1210.  
  1211.     # handle the case when mod_mime is built as a shared object
  1212.     # but wasn't included in the system-wide httpd.conf
  1213.     my $mod_mime = $self->find_apache_module('mod_mime.so');
  1214.     if ($mod_mime && -e $mod_mime) {
  1215.         $self->preamble(IfModule => '!mod_mime.c',
  1216.                         qq{LoadModule mime_module "$mod_mime"\n});
  1217.     }
  1218.  
  1219.     unless ($self->{inherit_config}->{TypesConfig}) {
  1220.         my $types = catfile $self->{vars}->{t_conf}, 'mime.types';
  1221.         unless (-e $types) {
  1222.             my $fh = $self->genfile($types);
  1223.             print $fh $self->types_config_template;
  1224.             close $fh;
  1225.         }
  1226.         $self->postamble(TypesConfig => qq("$types"));
  1227.     }
  1228. }
  1229.  
  1230. # various dup bugs in older perl and perlio in perl < 5.8.4 need a
  1231. # workaround to explicitly rewind the dupped DATA fh before using it
  1232. my $DATA_pos = tell DATA;
  1233. sub httpd_conf_template {
  1234.     my($self, $try) = @_;
  1235.  
  1236.     my $in = Symbol::gensym();
  1237.     if (open $in, $try) {
  1238.         return $in;
  1239.     }
  1240.     else {
  1241.         my $dup = Symbol::gensym();
  1242.         open $dup, "<&DATA" or die "Can't dup DATA: $!";
  1243.         seek $dup, $DATA_pos, 0; # rewind to the beginning
  1244.         return $dup; # so we don't close DATA
  1245.     }
  1246. }
  1247.  
  1248. #certain variables may not be available until certain config files
  1249. #are generated.  for example, we don't know the ssl port until ssl.conf.in
  1250. #is parsed.  ssl port is needed for proxyssl testing
  1251.  
  1252. sub check_vars {
  1253.     my $self = shift;
  1254.     my $vars = $self->{vars};
  1255.  
  1256.     unless ($vars->{proxyssl_url}) {
  1257.         my $ssl = $self->{vhosts}->{ $vars->{ssl_module_name} };
  1258.         if ($ssl) {
  1259.             $vars->{proxyssl_url} ||= $ssl->{hostport};
  1260.         }
  1261.  
  1262.         if ($vars->{proxyssl_url}) {
  1263.             $vars->{minclients}++;
  1264.             $vars->{maxclients}++;
  1265.         }
  1266.     }
  1267. }
  1268.  
  1269. sub extra_conf_files_needing_update {
  1270.     my $self = shift;
  1271.  
  1272.     my @need_update = ();
  1273.     finddepth(sub {
  1274.         return unless /\.in$/;
  1275.         (my $generated = $File::Find::name) =~ s/\.in$//;
  1276.         push @need_update, $generated 
  1277.             unless -e $generated && -M $generated < -M $File::Find::name;
  1278.     }, $self->{vars}->{t_conf});
  1279.  
  1280.     return @need_update;
  1281. }
  1282.  
  1283. sub generate_extra_conf {
  1284.     my $self = shift;
  1285.  
  1286.     my(@extra_conf, @conf_in, @conf_files);
  1287.  
  1288.     finddepth(sub {
  1289.         return unless /\.in$/;
  1290.         push @conf_in, catdir $File::Find::dir, $_;
  1291.     }, $self->{vars}->{t_conf});
  1292.  
  1293.     #make ssl port always be 8530 when available
  1294.     for my $file (@conf_in) {
  1295.         if (basename($file) =~ /^ssl/) {
  1296.             unshift @conf_files, $file;
  1297.         }
  1298.         else {
  1299.             push @conf_files, $file;
  1300.         }
  1301.     }
  1302.  
  1303.     for my $file (@conf_files) {
  1304.         (my $generated = $file) =~ s/\.in$//;
  1305.         debug "Will 'Include' $generated config file";
  1306.         push @extra_conf, $generated;
  1307.     }
  1308.  
  1309.     # if at least one .in file was modified or the derivative is
  1310.     # missing, regenerate them all (so information like assigned port
  1311.     # numbers will be correct)
  1312.     if ($self->extra_conf_files_needing_update) {
  1313.         for my $file (@conf_files) {
  1314.             local $Apache::TestConfig::File = $file;
  1315.  
  1316.             my $in = Symbol::gensym();
  1317.             open($in, $file) or next;
  1318.  
  1319.             (my $generated = $file) =~ s/\.in$//;
  1320.             my $out = $self->genfile($generated, $file);
  1321.             $self->replace_vars($in, $out);
  1322.  
  1323.             close $in;
  1324.             close $out;
  1325.  
  1326.             $self->check_vars;
  1327.         }
  1328.     }
  1329.  
  1330.     #we changed order to give ssl the first port after DEFAULT_PORT
  1331.     #but we want extra.conf Included first so vhosts inherit base config
  1332.     #such as LimitRequest*
  1333.     return [ sort @extra_conf ];
  1334. }
  1335.  
  1336. sub sslca_can {
  1337.     my($self, $check) = @_;
  1338.  
  1339.     my $vars = $self->{vars};
  1340.     return 0 unless $self->{modules}->{ $vars->{ssl_module} };
  1341.     return 0 unless -d "$vars->{t_conf}/ssl";
  1342.  
  1343.     require Apache::TestSSLCA;
  1344.  
  1345.     if ($check) {
  1346.         my $openssl = Apache::TestSSLCA::openssl();
  1347.         if (which($openssl)) {
  1348.             return 1;
  1349.         }
  1350.  
  1351.         error "cannot locate '$openssl' program required to generate SSL CA";
  1352.         exit(1);
  1353.     }
  1354.  
  1355.     return 1;
  1356. }
  1357.  
  1358. sub sslca_generate {
  1359.     my $self = shift;
  1360.  
  1361.     my $ca = $self->{vars}->{sslca};
  1362.     return if $ca and -d $ca; #t/conf/ssl/ca
  1363.  
  1364.     return unless $self->sslca_can(1);
  1365.  
  1366.     Apache::TestSSLCA::generate($self);
  1367. }
  1368.  
  1369. sub sslca_clean {
  1370.     my $self = shift;
  1371.  
  1372.     # XXX: httpd config is required, for now just skip ssl clean if
  1373.     # there is none. should probably add some flag which will tell us
  1374.     # when httpd_config was already run
  1375.     return unless $self->{vars}->{httpd} && $self->{vars}->{ssl_module};
  1376.  
  1377.     return unless $self->sslca_can;
  1378.  
  1379.     Apache::TestSSLCA::clean($self);
  1380. }
  1381.  
  1382. #XXX: just a quick hack to support t/TEST -ssl
  1383. #outside of httpd-test/perl-framework
  1384. sub generate_ssl_conf {
  1385.     my $self = shift;
  1386.     my $vars = $self->{vars};
  1387.     my $conf = "$vars->{t_conf}/ssl";
  1388.     my $httpd_test_ssl = "../httpd-test/perl-framework/t/conf/ssl";
  1389.     my $ssl_conf = "$vars->{top_dir}/$httpd_test_ssl";
  1390.  
  1391.     if (-d $ssl_conf and not -d $conf) {
  1392.         $self->gendir($conf);
  1393.         for (qw(ssl.conf.in)) {
  1394.             $self->cpfile("$ssl_conf/$_", "$conf/$_");
  1395.         }
  1396.         for (qw(certs keys crl)) {
  1397.             $self->symlink("$ssl_conf/$_", "$conf/$_");
  1398.         }
  1399.     }
  1400. }
  1401.  
  1402. sub find_in_inc {
  1403.     my($self, $dir) = @_;
  1404.     for my $path (@INC) {
  1405.         my $location = "$path/$dir";
  1406.         return $location if -d $location;
  1407.     }
  1408.     return "";
  1409. }
  1410.  
  1411. sub prepare_t_conf {
  1412.     my $self = shift;
  1413.     $self->gendir($self->{vars}->{t_conf});
  1414. }
  1415.  
  1416. my %aliases = (
  1417.     "perl-pod"     => "perlpod",
  1418.     "binary-httpd" => "httpd",
  1419.     "binary-perl"  => "perl",
  1420. );
  1421. sub generate_httpd_conf {
  1422.     my $self = shift;
  1423.     my $vars = $self->{vars};
  1424.  
  1425.     #generated httpd.conf depends on these things to exist
  1426.     $self->generate_types_config;
  1427.     $self->generate_index_html;
  1428.  
  1429.     $self->gendir($vars->{t_logs});
  1430.     $self->gendir($vars->{t_conf});
  1431.  
  1432.     my @very_last_postamble = ();
  1433.     if (my $extra_conf = $self->generate_extra_conf) {
  1434.         for my $file (@$extra_conf) {
  1435.             my $entry;
  1436.             if ($file =~ /\.conf$/) {
  1437.                 next if $file =~ m|/httpd\.conf$|;
  1438.                 $entry = qq(Include "$file");
  1439.             }
  1440.             elsif ($file =~ /\.pl$/) {
  1441.                 $entry = qq(<IfModule mod_perl.c>\n    PerlRequire "$file"\n</IfModule>\n);
  1442.             }
  1443.             else {
  1444.                 next;
  1445.             }
  1446.  
  1447.             # put the .last includes very last
  1448.             if ($file =~ /\.last\.(conf|pl)$/) {
  1449.                  push @very_last_postamble, $entry;
  1450.             }
  1451.             else {
  1452.                 $self->postamble($entry);
  1453.             }
  1454.  
  1455.         }
  1456.     }
  1457.  
  1458.     $self->configure_proxy;
  1459.  
  1460.     my $conf_file = $vars->{t_conf_file};
  1461.     my $conf_file_in = join '.', $conf_file, 'in';
  1462.  
  1463.     my $in = $self->httpd_conf_template($conf_file_in);
  1464.  
  1465.     my $out = $self->genfile($conf_file);
  1466.  
  1467.     $self->preamble_run($out);
  1468.  
  1469.     for my $name (qw(user group)) { #win32/cygwin do not support
  1470.         if ($vars->{$name}) {
  1471.             print $out "\u$name    $vars->{$name}\n";
  1472.         }
  1473.     }
  1474.  
  1475.     #2.0: ServerName $ServerName:$Port
  1476.     #1.3: ServerName $ServerName
  1477.     #     Port       $Port
  1478.     my @name_cfg = $self->servername_config($vars->{servername},
  1479.                                             $vars->{port});
  1480.     for my $pair (@name_cfg) {
  1481.         print $out "@$pair\n";
  1482.     }
  1483.  
  1484.     $self->replace_vars($in, $out);
  1485.  
  1486.     # handle the case when mod_alias is built as a shared object
  1487.     # but wasn't included in the system-wide httpd.conf
  1488.     my $mod_alias = $self->find_apache_module('mod_alias.so');
  1489.     if ($mod_alias && -e $mod_alias) {
  1490.         print $out <<EOF;
  1491. <IfModule !mod_alias.c>
  1492.     LoadModule alias_module "$mod_alias"
  1493. </IfModule>
  1494. EOF
  1495.     }
  1496.  
  1497.     print $out "<IfModule mod_alias.c>\n";
  1498.     for (keys %aliases) {
  1499.         next unless $vars->{$aliases{$_}};
  1500.         print $out "    Alias /getfiles-$_ $vars->{$aliases{$_}}\n";
  1501.     }
  1502.     print $out "</IfModule>\n";
  1503.  
  1504.     print $out "\n";
  1505.  
  1506.     $self->postamble_run($out);
  1507.  
  1508.     print $out join "\n", @very_last_postamble;
  1509.  
  1510.     close $in;
  1511.     close $out or die "close $conf_file: $!";
  1512. }
  1513.  
  1514. sub need_reconfiguration {
  1515.     my($self, $conf_opts) = @_;
  1516.     my @reasons = ();
  1517.     my $vars = $self->{vars};
  1518.  
  1519.     # if '-port select' we need to check from scratch which ports are
  1520.     # available
  1521.     if (my $port = $conf_opts->{port} || $Apache::TestConfig::Argv{port}) {
  1522.         if ($port eq 'select') {
  1523.             push @reasons, "'-port $port' requires reconfiguration";
  1524.         }
  1525.     }
  1526.  
  1527.     my $exe = $vars->{apxs} || $vars->{httpd} || '';
  1528.     # if httpd.conf is older than executable
  1529.     push @reasons,
  1530.         "$exe is newer than $vars->{t_conf_file}"
  1531.             if -e $exe && 
  1532.                -e $vars->{t_conf_file} &&
  1533.                -M $exe < -M $vars->{t_conf_file};
  1534.  
  1535.     # any .in files are newer than their derived versions?
  1536.     if (my @files = $self->extra_conf_files_needing_update) {
  1537.         # invalidate the vhosts cache, since a different port could be
  1538.         # assigned on reparse
  1539.         $self->{vhosts} = {};
  1540.         for my $file (@files) {
  1541.             push @reasons, "$file.in is newer than $file";
  1542.         }
  1543.     }
  1544.  
  1545.     # if special env variables are used (since they can change any time)
  1546.     # XXX: may be we could check whether they have changed since the
  1547.     # last run and thus avoid the reconfiguration?
  1548.     {
  1549.         my $passenv = passenv();
  1550.         if (my @env_vars = grep { $ENV{$_} } keys %$passenv) {
  1551.             push @reasons, "environment variables (@env_vars) are set";
  1552.         }
  1553.     }
  1554.  
  1555.     return @reasons;
  1556. }
  1557.  
  1558. sub error_log {
  1559.     my($self, $rel) = @_;
  1560.     my $file = catfile $self->{vars}->{t_logs}, 'error_log';
  1561.     my $rfile = abs2rel $file, $self->{vars}->{top_dir};
  1562.     return wantarray ? ($file, $rfile) :
  1563.       $rel ? $rfile : $file;
  1564. }
  1565.  
  1566. #utils
  1567.  
  1568. #For Win32 systems, stores the extensions used for executable files
  1569. #They may be . prefixed, so we will strip the leading periods.
  1570.  
  1571. my @path_ext = ();
  1572.  
  1573. if (WIN32) {
  1574.     if ($ENV{PATHEXT}) {
  1575.         push @path_ext, split ';', $ENV{PATHEXT};
  1576.         for my $ext (@path_ext) {
  1577.             $ext =~ s/^\.*(.+)$/$1/;
  1578.         }
  1579.     }
  1580.     else {
  1581.         #Win9X: doesn't have PATHEXT
  1582.         push @path_ext, qw(com exe bat);
  1583.     }
  1584. }
  1585.  
  1586. sub which {
  1587.     my $program = shift;
  1588.  
  1589.     return undef unless $program;
  1590.  
  1591.     for my $base (map { catfile($_, $program) } File::Spec->path()) {
  1592.         if ($ENV{HOME} and not WIN32) {
  1593.             # only works on Unix, but that's normal:
  1594.             # on Win32 the shell doesn't have special treatment of '~'
  1595.             $base =~ s/~/$ENV{HOME}/o;
  1596.         }
  1597.  
  1598.         return $base if -x $base && -f _;
  1599.  
  1600.         if (WIN32) {
  1601.             for my $ext (@path_ext) {
  1602.                 return "$base.$ext" if -x "$base.$ext" && -f _;
  1603.             }
  1604.         }
  1605.     }
  1606. }
  1607.  
  1608. sub apxs {
  1609.     my($self, $q, $ok_fail) = @_;
  1610.     return unless $self->{APXS};
  1611.     local @ENV{ qw(PATH IFS CDPATH ENV BASH_ENV) };
  1612.     my $devnull = devnull();
  1613.     my $apxs = shell_ready($self->{APXS});
  1614.     my $val = qx($apxs -q $q 2>$devnull);
  1615.     chomp $val if defined $val; # apxs post-2.0.40 adds a new line
  1616.     unless ($val) {
  1617.         if ($ok_fail) {
  1618.             return "";
  1619.         }
  1620.         else {
  1621.             warn "APXS ($self->{APXS}) query for $q failed\n";
  1622.         }
  1623.     }
  1624.     $val;
  1625. }
  1626.  
  1627. sub pop_dir {
  1628.     my $dir = shift;
  1629.  
  1630.     my @chunks = splitdir $dir;
  1631.     while (my $remove = shift) {
  1632.         pop @chunks if $chunks[-1] eq $remove;
  1633.     }
  1634.  
  1635.     catfile @chunks;
  1636. }
  1637.  
  1638. sub add_inc {
  1639.     my $self = shift;
  1640.     return if $ENV{MOD_PERL}; #already setup by mod_perl
  1641.     require lib;
  1642.     # make sure that Apache-Test/lib will be first in @INC,
  1643.     # followed by modperl-2.0/lib (or some other project's lib/),
  1644.     # followed by blib/ and finally system-wide libs.
  1645.     my $top_dir = $self->{vars}->{top_dir};
  1646.     my @dirs = map { catdir $top_dir, "blib", $_ } qw(lib arch);
  1647.  
  1648.     my $apache_test_dir = catdir $top_dir, "Apache-Test";
  1649.     unshift @dirs, $apache_test_dir if -d $apache_test_dir;
  1650.  
  1651.     if ($ENV{APACHE_TEST_LIVE_DEV}) {
  1652.         my $lib_dir = catdir $top_dir, "lib";
  1653.         push @dirs, $lib_dir if -d $lib_dir;
  1654.     }
  1655.  
  1656.     lib::->import(@dirs);
  1657.     #print join "\n", "add_inc", @INC, "";
  1658. }
  1659.  
  1660. #freeze/thaw so other processes can access config
  1661.  
  1662. sub thaw {
  1663.     my $class = shift;
  1664.     $class->new({thaw => 1, @_});
  1665. }
  1666.  
  1667. sub freeze {
  1668.     require Data::Dumper;
  1669.     local $Data::Dumper::Terse = 1;
  1670.     my $data = Data::Dumper::Dumper(shift);
  1671.     chomp $data;
  1672.     $data;
  1673. }
  1674.  
  1675. sub sync_vars {
  1676.     my $self = shift;
  1677.  
  1678.     return if $self->{save}; #this is not a cached config
  1679.  
  1680.     my $changed = 0;
  1681.     my $thaw = $self->thaw;
  1682.     my $tvars = $thaw->{vars};
  1683.     my $svars = $self->{vars};
  1684.  
  1685.     for my $key (@_) {
  1686.         for my $v ($tvars, $svars) {
  1687.             if (exists $v->{$key} and not defined $v->{$key}) {
  1688.                 $v->{$key} = ''; #rid undef
  1689.             }
  1690.         }
  1691.         next if exists $tvars->{$key} and exists $svars->{$key} and
  1692.                        $tvars->{$key} eq $svars->{$key};
  1693.         $tvars->{$key} = $svars->{$key};
  1694.         $changed = 1;
  1695.     }
  1696.  
  1697.     return unless $changed;
  1698.  
  1699.     $thaw->{save} = 1;
  1700.     $thaw->save;
  1701. }
  1702.  
  1703. sub save {
  1704.     my($self) = @_;
  1705.  
  1706.     return unless $self->{save};
  1707.  
  1708.     my $name = 'apache_test_config';
  1709.     my $file = catfile $self->{vars}->{t_conf}, "$name.pm";
  1710.     my $fh = $self->genfile($file);
  1711.  
  1712.     debug "saving config data to $name.pm";
  1713.  
  1714.     (my $obj = $self->freeze) =~ s/^/    /;
  1715.  
  1716.     print $fh <<EOF;
  1717. package $name;
  1718.  
  1719. sub new {
  1720. $obj;
  1721. }
  1722.  
  1723. 1;
  1724. EOF
  1725.  
  1726.     close $fh or die "failed to write $file: $!";
  1727. }
  1728.  
  1729. sub as_string {
  1730.     my $cfg = '';
  1731.     my $command = '';
  1732.  
  1733.     # httpd opts
  1734.     my $test_config = Apache::TestConfig->new({thaw=>1});
  1735.     if (my $httpd = $test_config->{vars}->{httpd}) {
  1736.         $httpd = shell_ready($httpd);
  1737.         $command = "$httpd -V";
  1738.         $cfg .= "\n*** $command\n";
  1739.         $cfg .= qx{$command};
  1740.     } else {
  1741.         $cfg .= "\n\n*** The httpd binary was not found\n";
  1742.     }
  1743.  
  1744.     # perl opts
  1745.     my $perl = shell_ready($^X);
  1746.     $command = "$perl -V";
  1747.     $cfg .= "\n\n*** $command\n";
  1748.     $cfg .= qx{$command};
  1749.  
  1750.     return $cfg;
  1751. }
  1752.  
  1753. # make a string suitable for feed to shell calls (wrap in quotes and
  1754. # escape quotes)
  1755. sub shell_ready {
  1756.     my $arg = shift;
  1757.     $arg =~ s!\\?"!\\"!g;
  1758.     return qq["$arg"];
  1759. }
  1760.  
  1761. ### Permanent custom configuration functions ###
  1762.  
  1763. # determine which configuration file Apache/TestConfigData.pm to use
  1764. # (as there could be several). The order searched is:
  1765. # 1) $ENV{HOME}/.apache-test/
  1766. # 2) in @INC
  1767. my $custom_config_path;
  1768. sub custom_config_path {
  1769.  
  1770.     return $custom_config_path if $custom_config_path;
  1771.  
  1772.     my @inc  = ();
  1773.  
  1774.     # XXX $ENV{HOME} isn't propagated in mod_perl
  1775.     push @inc, catdir $ENV{HOME}, '.apache-test' if $ENV{HOME};
  1776.  
  1777.     push @inc, @INC;
  1778.  
  1779.     for (@inc) {
  1780.         my $candidate = File::Spec->rel2abs(catfile $_, CUSTOM_CONFIG_FILE);
  1781.         next unless -e $candidate;
  1782.         return $custom_config_path = $candidate;
  1783.     }
  1784.  
  1785.     return '';
  1786. }
  1787.  
  1788. sub custom_config_exists {
  1789.     # try to load custom config if it wasn't loaded yet (there are
  1790.     # many entry points to this API)
  1791.     custom_config_load();
  1792.  
  1793.     # it's enough to check whether we have a custom
  1794.     # config for 'httpd' or 'apxs'.
  1795.     my $httpd = $Apache::TestConfigData::vars->{httpd} || '';
  1796.     return 1 if $httpd && -e $httpd && -x _;
  1797.  
  1798.     my $apxs = $Apache::TestConfigData::vars->{apxs} || '';
  1799.     return 1 if $apxs && -e $apxs && -x _;
  1800.  
  1801.     return 0;
  1802. }
  1803.  
  1804. # to be used only from Apache-Test/Makefile.PL to write the custom
  1805. # configuration module so it'll be copied to blib during 'make' and
  1806. # updated to use custom config data during 'make test' and then
  1807. # installed system-wide via 'make install'
  1808. #
  1809. # it gets written only if the custom configuration didn't exist
  1810. # already
  1811. sub custom_config_file_stub_write {
  1812.  
  1813.     return if custom_config_exists();
  1814.  
  1815.     # It doesn't matter whether it gets written under modperl-2.0/lib
  1816.     # or Apache-Test/lib root, since Apache::TestRun uses the same
  1817.     # logic and will update that file with real config data, which
  1818.     # 'make install' will then pick and install system-wide. but
  1819.     # remember that $FindBin::Bin is the location of top-level
  1820.     # 'Makefile.PL'
  1821.     require FindBin; # load it only for this particular use
  1822.     my $path = catfile $FindBin::Bin, "lib",
  1823.         Apache::TestConfig::CUSTOM_CONFIG_FILE;
  1824.  
  1825.     # write an empty stub
  1826.     Apache::TestConfig::custom_config_write($path, '');
  1827. }
  1828.  
  1829. sub custom_config_save {
  1830.     my $self = shift;
  1831.     my $conf_opts = shift;
  1832.  
  1833.     if ($ENV{APACHE_TEST_NO_STICKY_PREFERENCES}) {
  1834.         debug "APACHE_TEST_NO_STICKY_PREFERENCES=1 => " .
  1835.             "skipping save of custom config data";
  1836.         return;
  1837.     }
  1838.  
  1839.     my $vars = $self->{vars};
  1840.     my $config_dump = '';
  1841.  
  1842.     # minimum httpd and/or apxs needs to be set
  1843.     return 0 unless $vars->{httpd} or $Apache::TestConfigData::vars->{httpd}
  1844.         or          $vars->{apxs}  or $Apache::TestConfigData::vars->{apxs};
  1845.  
  1846.     # it doesn't matter how these vars were set (httpd may or may not
  1847.     # get set using the path to apxs, w/o an explicit -httpd value)
  1848.     for (@data_vars_must) {
  1849.         next unless my $var = $vars->{$_} || $conf_opts->{$_};
  1850.         $config_dump .= qq{    '$_' => '$var',\n};
  1851.     }
  1852.  
  1853.     # save these vars only if they were explicitly set via command line
  1854.     # options. For example if someone builds A-T as user 'foo', then
  1855.     # installs it as root and we save it, all users will now try to
  1856.     # configure under that user 'foo' which won't quite work.
  1857.     for (@data_vars_opt) {
  1858.         next unless my $var = $conf_opts->{$_};
  1859.         $config_dump .= qq{    '$_' => '$var',\n};
  1860.     }
  1861.  
  1862.     if (IS_APACHE_TEST_BUILD) {
  1863.         my $path = catfile $vars->{top_dir}, 'lib', CUSTOM_CONFIG_FILE;
  1864.         # if it doesn't exist, then we already have a global config file
  1865.         # if it does, then we have need to update it and its blib/ copy
  1866.         if (-e $path and custom_config_path_is_writable($path)) {
  1867.             custom_config_write($path, $config_dump);
  1868.             # also update blib/lib, since usually that's the one that
  1869.             # appears in @INC when t/TEST is run. and it won't be
  1870.             # synced with blib/ unless 'make' was run
  1871.             my $blib_path = catfile $vars->{top_dir},
  1872.                 'blib', 'lib', CUSTOM_CONFIG_FILE;
  1873.             if (-e $blib_path and custom_config_path_is_writable($blib_path)) {
  1874.                 custom_config_write($blib_path, $config_dump);
  1875.             }
  1876.             return 1;
  1877.         }
  1878.     }
  1879.  
  1880.     my $path;
  1881.     if ($path = custom_config_path() ) {
  1882.         # do nothing, the config file already exists (global)
  1883.         debug "Found custom config '$path'";
  1884.     }
  1885.     elsif (File::Spec->file_name_is_absolute(__FILE__)) {
  1886.         # next try a global location, as if it was configured before
  1887.         # Apache::Test's 'make install' (install in the same dir as
  1888.         # Apache/TestRun.pm)
  1889.         # if the filename is not absolute that means that we are still
  1890.         # in Apache-Test build (could just test for IS_APACHE_TEST_BUILD)
  1891.         my $base = dirname dirname __FILE__;
  1892.         $path = catdir $base, CUSTOM_CONFIG_FILE;
  1893.     }
  1894.  
  1895.     # check whether we can write to the directory of the chosen path
  1896.     # (e.g. root-owned directory)
  1897.     if ($path and custom_config_path_is_writable($path)) {
  1898.         custom_config_write($path, $config_dump);
  1899.         return 1;
  1900.     }
  1901.     # if we have no writable path yet, try to use ~
  1902.     elsif ($ENV{HOME}) {
  1903.         $path = catfile $ENV{HOME}, '.apache-test', CUSTOM_CONFIG_FILE;
  1904.         if ($path and custom_config_path_is_writable($path)) {
  1905.             custom_config_write($path, $config_dump);
  1906.             return 1;
  1907.         }
  1908.     }
  1909.  
  1910.     # XXX: should we croak since we failed to write config
  1911.     error "Failed to find a config file to save the custom " .
  1912.         "configuration in";
  1913.     return 0;
  1914. }
  1915.  
  1916. sub custom_config_path_is_writable {
  1917.     my $path = shift;
  1918.  
  1919.     return 0 unless $path;
  1920.  
  1921.     my $file_created    = '';
  1922.     my $top_dir_created = '';
  1923.     # first make sure that the file is writable if it exists
  1924.     # already (it might be non-writable if installed via EU::MM or in
  1925.     # blib/)
  1926.     if (-e $path) {
  1927.         my $mode = (stat _)[2];
  1928.         $mode |= 0200;
  1929.         chmod $mode, $path; # it's ok if we fail
  1930.         # keep it writable if we have changed it from not being one
  1931.         # so that custom_config_save will be able to just overwrite it
  1932.     }
  1933.     else {
  1934.         my $dir = dirname $path;
  1935.         if ($dir and !-e $dir) {
  1936.             my @dirs = File::Path::mkpath($dir, 0, 0755);
  1937.             # the top level dir to nuke on cleanup if it was created
  1938.             $top_dir_created = shift @dirs if @dirs;
  1939.         }
  1940.         # not really create yet, but will be in the moment
  1941.         $file_created = 1;
  1942.     }
  1943.  
  1944.     # try to open for append (even though it may not exist
  1945.     my $fh = Symbol::gensym;
  1946.     if (open $fh, ">>$path") {
  1947.         close $fh;
  1948.         # cleanup if we just created the file
  1949.         unlink $path if $file_created;
  1950.         File::Path::rmtree([$top_dir_created], 0, 0) if $top_dir_created;
  1951.         return 1;
  1952.     }
  1953.  
  1954.     return 0;
  1955. }
  1956.  
  1957. sub custom_config_write {
  1958.     my($path, $config_dump) = @_;
  1959.  
  1960.     my $pkg = << "EOC";
  1961. package Apache::TestConfigData;
  1962.  
  1963. use strict;
  1964. use warnings;
  1965.  
  1966. \$Apache::TestConfigData::vars = {
  1967. $config_dump
  1968. };
  1969.  
  1970. 1;
  1971.  
  1972. =head1 NAME
  1973.  
  1974. Apache::TestConfigData - Configuration file for Apache::Test
  1975.  
  1976. =cut
  1977. EOC
  1978.  
  1979.     debug "Writing custom config $path";
  1980.     my $dir = dirname $path;
  1981.     File::Path::mkpath($dir, 0, 0755) unless -e $dir;
  1982.     my $fh = Symbol::gensym;
  1983.     open $fh, ">$path" or die "Cannot open $path: $!";
  1984.     print $fh $pkg;
  1985.     close $fh;
  1986. }
  1987.  
  1988. sub custom_config_add_conf_opts {
  1989.     my $args = shift;
  1990.  
  1991.     return unless $Apache::TestConfigData::vars and 
  1992.         keys %$Apache::TestConfigData::vars;
  1993.  
  1994.     debug "overlaying custom config data";
  1995.  
  1996.     # the logic is quite complicated with 'httpd' and 'apxs', since
  1997.     # one is enough to run the test suite, and we need to avoid the
  1998.     # situation where both are saved in custom config but only one
  1999.     # (let's say httpd) is overriden by the command line /env var and
  2000.     # a hell may break loose if we take that overriden httpd value and
  2001.     # also use apxs from custom config which could point to a different
  2002.     # server. So if there is an override of apxs or httpd, do not use
  2003.     # the custom config for apxs or httpd.
  2004.     my $vars_must_overriden = grep {
  2005.         $ENV{ $vars_to_env{$_} } || $args->{$_}
  2006.     } @data_vars_must;
  2007.  
  2008.     # mod_perl 2.0 build always knows the right httpd location (and
  2009.     # optionally apxs)
  2010.     $vars_must_overriden++ if IS_MOD_PERL_2_BUILD();
  2011.  
  2012.     unless ($vars_must_overriden) {
  2013.         for (@data_vars_must) {
  2014.             next unless $Apache::TestConfigData::vars->{$_};
  2015.             $args->{$_} = $Apache::TestConfigData::vars->{$_};
  2016.         }
  2017.     }
  2018.  
  2019.     for (@data_vars_opt) {
  2020.         next unless $Apache::TestConfigData::vars->{$_};
  2021.         # env vars override custom config
  2022.         my $env_value = $ENV{ $vars_to_env{$_} };
  2023.         next unless defined $env_value and length $env_value;
  2024.         $args->{$_} ||= $Apache::TestConfigData::vars->{$_};
  2025.     }
  2026. }
  2027.  
  2028. my $custom_config_loaded = 0;
  2029. sub custom_config_load {
  2030.  
  2031.     if ($ENV{APACHE_TEST_NO_STICKY_PREFERENCES}) {
  2032.         debug "APACHE_TEST_NO_STICKY_PREFERENCES=1 => " .
  2033.             "skipping load of custom config data";
  2034.         return;
  2035.     }
  2036.  
  2037.     return if $custom_config_loaded;
  2038.  
  2039.     if (my $custom_config_path = custom_config_path()) {
  2040.         debug "loading custom config data from: '$custom_config_path'";
  2041.         $custom_config_loaded++;
  2042.         require $custom_config_path;
  2043.     }
  2044.     else {
  2045.         debug "no custom config data was loaded";
  2046.     }
  2047. }
  2048.  
  2049. sub custom_config_first_time {
  2050.     my $self = shift;
  2051.     my $conf_opts = shift;
  2052.  
  2053.     my $vars = $self->{vars};
  2054.  
  2055.     print qq[
  2056.  
  2057. We are now going to configure the Apache-Test framework.
  2058. This configuration process needs to be done only once.
  2059.  
  2060. ];
  2061.  
  2062.     print qq[
  2063.  
  2064. First we need to know where the 'httpd' executable is located.
  2065. If you have more than one Apache server is installed, make sure
  2066. you supply the path to the one you are going to use for testing.
  2067. You can always override this setting at run time via the '-httpd'
  2068. option. For example:
  2069.  
  2070.   % t/TEST -httpd /path/to/alternative/httpd
  2071.  
  2072. or via the environment variable APACHE_TEST_HTTPD. For example:
  2073.  
  2074.   % APACHE_TEST_HTTPD=/path/to/alternative/httpd t/TEST
  2075.  
  2076. If for some reason you want to skip the test suite, type: skip
  2077. ];
  2078.  
  2079.     {
  2080.         my %choices = ();
  2081.         my @tries = qw(httpd httpd2);
  2082.         # Win32 uses Apache or perhaps Apache2, not apache/apache2
  2083.         push @tries, WIN32 ? qw(Apache Apache2) : qw(apache apache2);
  2084.         for (grep defined $_,
  2085.              map({ catfile $vars->{$_}, $vars->{target} } qw(sbindir bindir)),
  2086.              $self->default_httpd, which($vars->{target}),
  2087.              $ENV{APACHE}, $ENV{APACHE2},
  2088.              $ENV{APACHE_TEST_HTTPD}, $ENV{APACHE_TEST_HTTPD2},
  2089.              map {which($_)} @tries) {
  2090.             $choices{$_}++ if -e $_ && -x _;
  2091.         }
  2092.         my $optional = 0;
  2093.         my $wanted = 'httpd';
  2094.         $vars->{$wanted} = 
  2095.             _custom_config_prompt_path($wanted, \%choices, $optional);
  2096.     }
  2097.  
  2098.     print qq[
  2099.  
  2100. Next we need to know where the 'apxs' script is located. This script
  2101. provides a lot of information about the apache installation, and makes
  2102. it easier to find things. However it's not available on all platforms,
  2103. therefore it's optional.
  2104.  
  2105. If you don't have it installed it's not a problem. Just press Enter.
  2106.  
  2107. Notice that if you have Apache 2.x installed that script could be
  2108. called as 'apxs2'.
  2109.  
  2110. If you have more than one Apache server is installed, make sure you
  2111. supply the path to the apxs script you are going to use for testing.
  2112. You can always override this setting at run time via the '-apxs'
  2113. option. For example:
  2114.  
  2115.   % t/TEST -apxs /path/to/alternative/apxs
  2116.  
  2117. or via the environment variable APACHE_TEST_APXS. For example:
  2118.  
  2119.   % APACHE_TEST_APXS=/path/to/alternative/apxs t/TEST
  2120.  
  2121. ];
  2122.     {
  2123.         my %choices = ();
  2124.         for (grep defined $_,
  2125.              map({ catfile $vars->{$_}, 'apxs' } qw(sbindir bindir)),
  2126.              $self->default_apxs,
  2127.              $ENV{APXS},  $ENV{APACHE_TEST_APXS},  which('apxs'),
  2128.              $ENV{APXS2}, $ENV{APACHE_TEST_APXS2}, which('apxs2')) {
  2129.             $choices{$_}++ if -e $_ && -x _;
  2130.         }
  2131.         my $optional = 1;
  2132.         my $wanted = 'apxs';
  2133.         $vars->{$wanted} = 
  2134.             _custom_config_prompt_path($wanted, \%choices, $optional);
  2135.     }
  2136.  
  2137.     $self->custom_config_save($conf_opts);
  2138.  
  2139.     # we probably could reconfigure on the fly ($self->configure), but
  2140.     # the problem is various cached data which won't be refreshed. so
  2141.     # the simplest is just to restart the run from scratch
  2142.     Apache::TestRun::rerun();
  2143. }
  2144.  
  2145. sub _custom_config_prompt_path {
  2146.     my($wanted, $rh_choices, $optional) = @_;
  2147.  
  2148.     my $ans;
  2149.     my $default = '';
  2150.     my $optional_str = $optional ? " (optional)" : '';
  2151.     my $prompt =
  2152.         "\nPlease provide a full path to$optional_str '$wanted' executable";
  2153.  
  2154.     my @choices = ();
  2155.     if (%$rh_choices) {
  2156.         $prompt .= " or choose from the following options:\n\n";
  2157.         my $c = 0;
  2158.         for (sort keys %$rh_choices) {
  2159.             $c++;
  2160.             $prompt .= "    [$c] $_\n";
  2161.             push @choices, $_;
  2162.         }
  2163.         $prompt .= " \n";
  2164.         $default = 1; # a wild guess
  2165.     }
  2166.     else {
  2167.         $prompt .= ":\n\n";
  2168.     }
  2169.  
  2170.     while (1) {
  2171.         $ans = ExtUtils::MakeMaker::prompt($prompt, $default);
  2172.  
  2173.         # strip leading/closing spaces
  2174.         $ans =~ s/^\s*|\s*$//g;
  2175.  
  2176.         # convert the item number to the path
  2177.         if ($ans =~ /^(\d+)$/) {
  2178.             if ($1 > 0 and $choices[$1-1]) {
  2179.                 $ans = $choices[$1-1];
  2180.             }
  2181.             else {
  2182.                 warn "The choice '$ans' doesn't exist\n";
  2183.                 next;
  2184.             }
  2185.         }
  2186.  
  2187.         if ($optional) {
  2188.             return '' unless $ans;
  2189.         }
  2190.  
  2191.         # stop the test suite without an error (so automatic tools
  2192.         # like CPAN.pm will be able to continue)
  2193.         if (lc($ans) eq 'skip' && !$optional) {
  2194.             skip_test_suite();
  2195.             next; # in case they change their mind
  2196.         }
  2197.  
  2198.         unless (File::Spec->file_name_is_absolute($ans)) {
  2199.             my $cwd = Cwd::cwd();
  2200.             warn "The path '$ans' is not an absolute path. " .
  2201.                 "Please specify an absolute path\n";
  2202.             next;
  2203.         }
  2204.  
  2205.         warn("'$ans' doesn't exist\n"),     next unless -e $ans;
  2206.         warn("'$ans' is not executable\n"), next unless -x $ans;
  2207.  
  2208.         return $ans;
  2209.     }
  2210. }
  2211.  
  2212. 1;
  2213.  
  2214. =head1 NAME
  2215.  
  2216. Apache::TestConfig -- Test Configuration setup module
  2217.  
  2218. =head1 SYNOPSIS
  2219.  
  2220.   use Apache::TestConfig;
  2221.  
  2222.   my $cfg = Apache::TestConfig->new(%args)
  2223.   my $fh = $cfg->genfile($file);
  2224.   $cfg->writefile($file, $content);
  2225.   $cfg->gendir($dir);
  2226.   ...
  2227.  
  2228. =head1 DESCRIPTION
  2229.  
  2230. C<Apache::TestConfig> is used in creating the C<Apache::Test>
  2231. configuration files.
  2232.  
  2233. =head1 FUNCTIONS
  2234.  
  2235. =over
  2236.  
  2237. =item genwarning()
  2238.  
  2239.   my $warn = $cfg->genwarning($filename)
  2240.  
  2241. genwarning() returns a warning string as a comment, saying that the
  2242. file was autogenerated and that it's not a good idea to modify this
  2243. file. After the warning a perl trace of calls to this this function is
  2244. appended. This trace is useful for finding what code has created the
  2245. file.
  2246.  
  2247.   my $warn = $cfg->genwarning($filename, $from_filename)
  2248.  
  2249. If C<$from_filename> is specified it'll be used in the warning to tell
  2250. which file it was generated from.
  2251.  
  2252. genwarning() automatically recognizes the comment type based on the
  2253. file extension. If the extension is not recognized, the default C<#>
  2254. style is used.
  2255.  
  2256. Currently it support C<E<lt>!-- --E<gt>>, C</* ... */> and C<#>
  2257. styles.
  2258.  
  2259. =item genfile()
  2260.  
  2261.   my $fh = $cfg->genfile($file);
  2262.  
  2263. genfile() creates a new file C<$file> for writing and returns a file
  2264. handle.
  2265.  
  2266. If parent directories of C<$file> don't exist they will be
  2267. automagically created.
  2268.  
  2269. The file C<$file> and any created parent directories (if found empty)
  2270. will be automatically removed on cleanup.
  2271.  
  2272. A comment with a warning and calls trace is added to the top of this
  2273. file. See genwarning() for more info about this comment.
  2274.  
  2275.   my $fh = $cfg->genfile($file, $from_file);
  2276.  
  2277. If C<$from_filename> is specified it'll be used in the warning to tell
  2278. which file it was generated from.
  2279.  
  2280.   my $fh = $cfg->genfile($file, $from_file, $nowarning);
  2281.  
  2282. If C<$nowarning> is true, the warning won't be added. If using this
  2283. optional argument and there is no C<$from_file> you must pass undef as
  2284. in:
  2285.  
  2286.   my $fh = $cfg->genfile($file, undef, $nowarning);
  2287.  
  2288.  
  2289. =item writefile()
  2290.  
  2291.   $cfg->writefile($file, $content, [$nowarning]);
  2292.  
  2293. writefile() creates a new file C<$file> with the content of
  2294. C<$content>.
  2295.  
  2296. A comment with a warning and calls trace is added to the top of this
  2297. file unless C<$nowarnings> is passed and set to a true value. See
  2298. genwarning() for more info about this comment.
  2299.  
  2300. If parent directories of C<$file> don't exist they will be
  2301. automagically created.
  2302.  
  2303. The file C<$file> and any created parent directories (if found empty)
  2304. will be automatically removed on cleanup.
  2305.  
  2306. =item write_perlscript()
  2307.  
  2308.   $cfg->write_perlscript($filename, @lines);
  2309.  
  2310. Similar to writefile() but creates an executable Perl script with
  2311. correctly set shebang line.
  2312.  
  2313. =item gendir()
  2314.  
  2315.   $cfg->gendir($dir);
  2316.  
  2317. gendir() creates a new directory C<$dir>.
  2318.  
  2319. If parent directories of C<$dir> don't exist they will be
  2320. automagically created.
  2321.  
  2322. The directory C<$dir> and any created parent directories will be
  2323. automatically removed on cleanup if found empty.
  2324.  
  2325. =back
  2326.  
  2327. =head1 Environment Variables
  2328.  
  2329. The following environment variables affect the configuration and the
  2330. run-time of the C<Apache::Test> framework:
  2331.  
  2332. =head2 APACHE_TEST_COLOR
  2333.  
  2334. To aid visual control over the configuration process and the run-time
  2335. phase, C<Apache::Test> uses coloured fonts when the environment
  2336. variable C<APACHE_TEST_COLOR> is set to a true value.
  2337.  
  2338. =head2 APACHE_TEST_LIVE_DEV
  2339.  
  2340. When using C<Apache::Test> during the project development phase, it's
  2341. often convenient to have the I<project/lib> (live) directory appearing
  2342. first in C<@INC> so any changes to the Perl modules, residing in it,
  2343. immediately affect the server, without a need to rerun C<make> to
  2344. update I<blib/lib>. When the environment variable
  2345. C<APACHE_TEST_LIVE_DEV> is set to a true value during the
  2346. configuration phase (C<t/TEST -config>, C<Apache::Test> will
  2347. automatically unshift the I<project/lib> directory into C<@INC>, via
  2348. the autogenerated I<t/conf/modperl_inc.pl> file.
  2349.  
  2350. =head1 AUTHOR
  2351.  
  2352. =head1 SEE ALSO
  2353.  
  2354. perl(1), Apache::Test(3)
  2355.  
  2356. =cut
  2357.  
  2358.  
  2359. __DATA__
  2360. Listen     0.0.0.0:@Port@
  2361.  
  2362. ServerRoot   "@ServerRoot@"
  2363. DocumentRoot "@DocumentRoot@"
  2364.  
  2365. PidFile     @t_logs@/httpd.pid
  2366. ErrorLog    @t_logs@/error_log
  2367. LogLevel    debug
  2368.  
  2369. <IfModule mod_log_config.c>
  2370.     TransferLog @t_logs@/access_log
  2371. </IfModule>
  2372.  
  2373. ServerAdmin @ServerAdmin@
  2374.  
  2375. #needed for http/1.1 testing
  2376. KeepAlive       On
  2377.  
  2378. HostnameLookups Off
  2379.  
  2380. <Directory />
  2381.     Options FollowSymLinks
  2382.     AllowOverride None
  2383. </Directory>
  2384.  
  2385. <IfModule @THREAD_MODULE@>
  2386.     StartServers         1
  2387.     MinSpareThreads      @MinClients@
  2388.     MaxSpareThreads      @MinClients@
  2389.     ThreadsPerChild      @MinClients@
  2390.     MaxClients           @MaxClientsThreadedMPM@
  2391.     MaxRequestsPerChild  0
  2392. </IfModule>
  2393.  
  2394. <IfModule perchild.c>
  2395.     NumServers           1
  2396.     StartThreads         @MinClients@
  2397.     MinSpareThreads      @MinClients@
  2398.     MaxSpareThreads      @MinClients@
  2399.     MaxThreadsPerChild   @MaxClients@
  2400.     MaxRequestsPerChild  0
  2401. </IfModule>
  2402.  
  2403. <IfModule prefork.c>
  2404.     StartServers         @MinClients@
  2405.     MinSpareServers      @MinClients@
  2406.     MaxSpareServers      @MinClients@
  2407.     MaxClients           @MaxClients@
  2408.     MaxRequestsPerChild  0
  2409. </IfModule>
  2410.  
  2411. <IfDefine APACHE1>
  2412.     StartServers         @MinClients@
  2413.     MinSpareServers      @MinClients@
  2414.     MaxSpareServers      @MinClients@
  2415.     MaxClients           @MaxClients@
  2416.     MaxRequestsPerChild  0
  2417. </IfDefine>
  2418.  
  2419. <IfModule mpm_winnt.c>
  2420.     ThreadsPerChild      50
  2421.     MaxRequestsPerChild  0
  2422. </IfModule>
  2423.  
  2424. <Location /server-info>
  2425.     SetHandler server-info
  2426. </Location>
  2427.  
  2428. <Location /server-status>
  2429.     SetHandler server-status
  2430. </Location>
  2431.  
  2432.