summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Apache/Defaults.pm222
-rw-r--r--t/01version.t40
-rw-r--r--t/02modules.t15
-rw-r--r--t/03env.t14
-rw-r--r--t/MockHttpd.pm67
-rw-r--r--t/TestHttpd.pm25
6 files changed, 371 insertions, 12 deletions
diff --git a/lib/Apache/Defaults.pm b/lib/Apache/Defaults.pm
index 13c729e..08c4865 100644
--- a/lib/Apache/Defaults.pm
+++ b/lib/Apache/Defaults.pm
@@ -5,6 +5,7 @@ use File::Spec;
use IPC::Open3;
use Shell::GetEnv;
use DateTime::Format::Strptime;
+use Text::ParseWords;
use Symbol 'gensym';
use Carp;
@@ -26,22 +27,28 @@ sub new {
@servlist = qw(/usr/sbin/httpd /usr/sbin/apache2);
}
- if (my @select = grep { -x $_ } @servlist) {
+ if (my @select = grep { -x $_->[0] }
+ map { [ shellwords($_) ] } @servlist) {
$self->{server} = shift @select;
} else {
croak "No suitable httpd binary found";
}
if ($v = delete $_{environ}) {
- $self->{environ} = Shell::GetEnv->new('sh', ". $v", startup => 0)
- ->envs;
+ my $env = Shell::GetEnv->new('sh', ". $v",
+ { startup => 0 });
+ if ($env->status) {
+ croak "Got status ".$env->status." trying to inherit environment";
+ }
+ $self->{environ} = $env->envs;
}
croak "unrecognized arguments" if keys(%_);
return $self;
}
-sub server { shift->{server} }
+sub server { shift->{server}[0] }
+sub server_command { @{shift->{server}} }
sub environ { shift->{environ} }
sub probe {
@@ -50,17 +57,31 @@ sub probe {
open(my $nullout, '>', File::Spec->devnull);
open(my $nullin, '<', File::Spec->devnull);
- my $fd = gensym;
+ my $out = gensym;
+ my $err = gensym;
local %ENV = %{$self->{environ}} if $self->{environ};
- if (my $pid = open3($nullin, $fd, $nullout, $self->server, @opt)) {
- while (<$fd>) {
+ if (my $pid = open3($nullin, $out, $err,
+ $self->server_command, @opt)) {
+ while (<$out>) {
chomp;
last unless &{$cb}($_);
}
+ waitpid($pid, 0);
+ if ($? == -1) {
+ croak "failed to execute " .$self->server . ": $!";
+ } elsif ($? & 127) {
+ croak sprintf("%s died with signal %d%s",
+ $self->server, $? & 127,
+ ($? & 128) ? ' (core dumped)' : '');
+ } elsif (my $code = $? >> 8) {
+ local $/ = undef;
+ croak sprintf("%s terminated with status %d; error message: %s",
+ $self->server, $code, <$err>);
+ }
}
- close $fd;
close $nullin;
- close $nullout;
+ close $out;
+ close $err;
}
sub dequote {
@@ -84,7 +105,6 @@ sub _get_version_info {
$self->{built} =
DateTime::Format::Strptime->new(
pattern => '%b %d %Y %H:%M%S',
- strict => 1,
locale => 'en_US',
time_zone => 'UTC',
on_error => 'undef'
@@ -104,7 +124,7 @@ sub _get_version_info {
$self->{MPM_forked} = $+{b} eq 'yes';
} elsif (/^\s+-D\s+(?<name>.+?)=(?<val>.+)$/) {
$self->{defines}{$+{name}} = $self->dequote($+{val});
- } elsif (/^\s+-D\s+(?<name>.+?)(?:\s*(?<com>.+))?$/) {
+ } elsif (/^\s+-D\s+(?<name>\S+)(?:\s*(?<com>.+))?$/) {
$self->{defines}{$+{name}} = 1;
}
return 1;
@@ -139,8 +159,11 @@ sub server_root { shift->defines('HTTPD_ROOT') }
sub defines {
my $self = shift;
$self->_get_version_info;
+ if (@_) {
return @{$self->{defines}}{@_};
}
+ return sort keys %{$self->{defines}};
+}
# List of module sources and corresponding identifiers, obtained from the
# httpd-2.4.6 source.
@@ -271,7 +294,7 @@ sub preloaded {
if (@_) {
return @{$self->{preloaded}}{@_};
}
- return keys %{$self->{preloaded}};
+ return sort keys %{$self->{preloaded}};
}
sub _get_module_info {
@@ -290,3 +313,178 @@ sub _get_module_info {
}
1;
+__END__
+=head1 NAME
+
+Apache::Defaults - Get default settings for Apache httpd daemon
+
+=head1 SYNOPSIS
+
+ $x = new Apache::Defaults;
+ print $x->name;
+ print $x->version;
+ print $x->server_root;
+ print $x->built;
+ print $x->architecture;
+ print $x->MPM;
+ print $x->defines('DYNAMIC_MODULE_LIMIT');
+ print $x->preloaded('cgi_module');
+
+=head1 DESCRIPTION
+
+Detects the default settings of the Apache httpd daemon by invoking
+it with appropriate options and analyzing its output.
+
+=head1 METHODS
+
+=head2 new
+
+ $x = new Apache::Defaults(%attrs);
+
+Detects the settings of the apache server and returns the object representing
+them. Attributes (I<%attrs>) are:
+
+=over 4
+
+=item C<server>
+
+Full pathname of the B<httpd> binary to inspect. The argument can also be
+a reference to the list of possible pathnames. In this case, the first of
+them that exists on disk and has executable privileges will be used. Full
+command line can also be used, e.g.:
+
+ server => '/usr/sbin/httpd -d /etc/httpd'
+
+The default used in the absense of this attribute is:
+
+ [ '/usr/sbin/httpd', '/usr/sbin/apache2' ]
+
+=item C<environ>
+
+Name of the shell script that sets the environment for B<httpd> invocation.
+For some obscure reason, B<httpd> attempts to read its configuration file
+even if invoked with the B<-V> option (which is intended to print the version
+and build parameters of the daemon). It will fail if its configuration refers
+to the environment variables, defined elsewhere. This situation is quite
+common in Debian-based distributions, which define the environment variables
+in file F</etc/apache2/envvars>. This attribute is intended to cope with
+such problems, e.g.:
+
+ $x = new Apache::Defaults(environ => /etc/apache2/envvars)
+
+=back
+
+The method will I<croak> if an error occurs (e.g. the server binary
+is not found or exits with failure).
+
+=head2 server
+
+ $s = $x->server;
+
+Returns the pathname of the B<httpd> binary.
+
+=head2 server_command
+
+ @cmd = $x->server_command;
+
+Returns full command line of the B<httpd> binary.
+
+=head2 environ
+
+ $hashref = $x->environ
+
+Returns a reference to the environment used when invoking the server.
+
+=head2 name
+
+ $s = $x->name;
+
+Returns server implementation name (normally C<Apache>).
+
+=head2 version
+
+ $v = $x->version;
+
+Returns server version (as string).
+
+=head2 platform
+
+ $s = $x->platform;
+
+Platform (distribution) on which the binary is compiled.
+
+=head2 architecture
+
+Architecture for which the server is built.
+
+=head2 built
+
+ $d = $x->built;
+
+Returns a B<DateTime> object, representing the time when the server
+was built.
+
+=head2 loaded_with
+
+APR tools with which the server is loaded.
+
+=head2 compiled_with
+
+APR tools with which the server is compiled.
+
+=head2 MPM
+
+MPM module loaded in the configuration.
+
+=head2 MPM_threaded
+
+True if the MPM is threaded.
+
+=head2 MPM_forked
+
+True if the MPM is forked.
+
+=head2 defines
+
+ @names = $x->defines;
+
+Returns the list of symbolic names defined during the compilation. The
+names are in lexical order.
+
+ @values = $x->defines(@names);
+
+Returns values of the named defines.
+
+=head2 server_root
+
+ $s = $x->server_root;
+
+Returns default server root directory. This is equivalent to
+
+ $x->defines('HTTPD_ROOT');
+
+=head2 preloaded
+
+ @ids = $x->preloaded;
+
+Returns the list of the preloaded module identifiers, in lexical order.
+
+ @sources = $x->preloaded(@ids);
+
+Returns the list of module source names for the given source identifiers.
+For non-existing identifiers, B<undef> is returned.
+
+=head1 LICENSE
+
+GPLv3+: GNU GPL version 3 or later, see
+L<http://gnu.org/licenses/gpl.html>.
+
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+
+=head1 AUTHORS
+
+Sergey Poznyakoff <gray@gnu.org>
+
+=cut
+
diff --git a/t/01version.t b/t/01version.t
new file mode 100644
index 0000000..f99504a
--- /dev/null
+++ b/t/01version.t
@@ -0,0 +1,40 @@
+# -*- perl -*-
+use lib qw(t lib);
+use strict;
+use warnings;
+use Test;
+use TestHttpd;
+
+plan test => 10;
+
+my $x = new TestHttpd;
+
+ok($x->name, 'Apache');
+ok($x->version, '2.4.6');
+ok($x->built, '2013-08-05T16:03:02');
+ok($x->architecture, '64-bit');
+ok($x->MPM, 'event');
+ok(join("\n", $x->defines)."\n",<<EOT);
+APR_HAS_MMAP
+APR_HAS_OTHER_CHILD
+APR_HAS_SENDFILE
+APR_HAVE_IPV6
+APR_USE_PTHREAD_SERIALIZE
+APR_USE_SYSVSEM_SERIALIZE
+AP_HAVE_RELIABLE_PIPED_LOGS
+AP_TYPES_CONFIG_FILE
+DEFAULT_ERRORLOG
+DEFAULT_PIDLOG
+DEFAULT_SCOREBOARD
+DYNAMIC_MODULE_LIMIT
+HTTPD_ROOT
+SERVER_CONFIG_FILE
+SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+SUEXEC_BIN
+EOT
+;
+ok($x->defines('APR_HAS_SENDFILE'), 1);
+ok($x->defines('APR_HAVE_IPV6'), 1);
+ok($x->defines('DYNAMIC_MODULE_LIMIT'), 256);
+ok($x->server_root, "/usr");
+
diff --git a/t/02modules.t b/t/02modules.t
new file mode 100644
index 0000000..af85a63
--- /dev/null
+++ b/t/02modules.t
@@ -0,0 +1,15 @@
+# -*- perl -*-
+use lib qw(t lib);
+use strict;
+use warnings;
+use Test;
+use TestHttpd;
+
+plan test => 3;
+
+my $x = new TestHttpd;
+
+ok(join(',', $x->preloaded), 'cgi_module,so_module');
+ok($x->preloaded('cgi_module'), 'mod_cgi.c');
+ok(join(',', $x->preloaded('so_module','cgi_module')), 'mod_so.c,mod_cgi.c');
+
diff --git a/t/03env.t b/t/03env.t
new file mode 100644
index 0000000..9644840
--- /dev/null
+++ b/t/03env.t
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use lib qw(t lib);
+use strict;
+use warnings;
+use Test;
+use TestHttpd;
+
+plan test => 3;
+
+my $x = new TestHttpd(environ => { MOCK_HTTPD_CATCH => 22 });
+
+ok($x->name, 'Apache');
+ok($x->version, '2.4.6');
+ok($x->defines('MOCK_HTTPD_CATCH'), 22);
diff --git a/t/MockHttpd.pm b/t/MockHttpd.pm
new file mode 100644
index 0000000..1d75926
--- /dev/null
+++ b/t/MockHttpd.pm
@@ -0,0 +1,67 @@
+package MockHttpd;
+use strict;
+use warnings;
+use Data::Dumper;
+
+unless (caller) {
+ my $arg = shift @ARGV;
+ if ($arg && !@ARGV) {
+ if ($arg eq '-V') {
+ version();
+ exit(0);
+ } elsif ($arg eq '-l') {
+ modules();
+ exit(0);
+ }
+ }
+ print STDERR $0 . ' ' . __PACKAGE__ . " -l | -V\n";
+ exit(1);
+}
+
+sub version {
+ print <<'EOT';
+Server version: Apache/2.4.6 (Unix)
+Server built: Aug 5 2013 16:32:54
+Server's Module Magic Number: 20120211:23
+Server loaded: APR 1.4.6, APR-UTIL 1.5.1
+Compiled using: APR 1.4.6, APR-UTIL 1.5.1
+Architecture: 64-bit
+Server MPM: event
+ threaded: yes (fixed thread count)
+ forked: yes (variable process count)
+Server compiled with....
+ -D APR_HAS_SENDFILE
+ -D APR_HAS_MMAP
+ -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
+ -D APR_USE_SYSVSEM_SERIALIZE
+ -D APR_USE_PTHREAD_SERIALIZE
+ -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+ -D APR_HAS_OTHER_CHILD
+ -D AP_HAVE_RELIABLE_PIPED_LOGS
+ -D DYNAMIC_MODULE_LIMIT=256
+ -D HTTPD_ROOT="/usr"
+ -D SUEXEC_BIN="/usr/bin/suexec"
+ -D DEFAULT_PIDLOG="/var/run/httpd.pid"
+ -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
+ -D DEFAULT_ERRORLOG="logs/error_log"
+ -D AP_TYPES_CONFIG_FILE="/etc/httpd/mime.types"
+ -D SERVER_CONFIG_FILE="/etc/httpd/httpd.conf"
+EOT
+;
+ if ($ENV{MOCK_HTTPD_CATCH}) {
+ print " -D MOCK_HTTPD_CATCH=\"$ENV{MOCK_HTTPD_CATCH}\"\n";
+ }
+}
+
+sub modules {
+ print <<'EOT';
+Compiled in modules:
+ core.c
+ mod_so.c
+ http_core.c
+ mod_cgi.c
+EOT
+;
+}
+
+1;
diff --git a/t/TestHttpd.pm b/t/TestHttpd.pm
new file mode 100644
index 0000000..44f6b87
--- /dev/null
+++ b/t/TestHttpd.pm
@@ -0,0 +1,25 @@
+package TestHttpd;
+use strict;
+use warnings;
+use lib qw(t lib);
+use parent 'Apache::Defaults';
+use MockHttpd;
+use File::Temp qw(tempfile);
+
+sub new {
+ my $class = shift;
+ local %_ = @_;
+ if (my $env = $_{environ}) {
+ my ($fh, $name) = tempfile();
+ while (my ($k,$v) = each %$env) {
+ $v =~ s/(["\\])/\\$1/g;
+ print $fh "$k=\"$v\"\n";
+ print $fh "export $k\n";
+ }
+ close $fh;
+ $_{environ} = $name;
+ }
+ $class->SUPER::new(server => "$^X ".$INC{'MockHttpd.pm'}, %_);
+}
+
+1;

Return to:

Send suggestions and report system problems to the System administrator.