diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2018-02-22 12:54:30 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2018-02-22 12:54:30 +0200 |
commit | c16f2363476167d59a2eaad38ef9e98f032ebdd1 (patch) | |
tree | 9cac41ac5cd55e76d872c9a977187484a76092f0 | |
parent | 3c71a4b3c78a8b25258e858135cd239d22bd83c9 (diff) | |
download | apache-defaults-c16f2363476167d59a2eaad38ef9e98f032ebdd1.tar.gz apache-defaults-c16f2363476167d59a2eaad38ef9e98f032ebdd1.tar.bz2 |
Improve error reporting; Add documentation and test suite.
-rw-r--r-- | lib/Apache/Defaults.pm | 226 | ||||
-rw-r--r-- | t/01version.t | 40 | ||||
-rw-r--r-- | t/02modules.t | 15 | ||||
-rw-r--r-- | t/03env.t | 14 | ||||
-rw-r--r-- | t/MockHttpd.pm | 67 | ||||
-rw-r--r-- | t/TestHttpd.pm | 25 |
6 files changed, 373 insertions, 14 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,7 +159,10 @@ sub server_root { shift->defines('HTTPD_ROOT') } sub defines { my $self = shift; $self->_get_version_info; - return @{$self->{defines}}{@_}; + if (@_) { + return @{$self->{defines}}{@_}; + } + return sort keys %{$self->{defines}}; } # List of module sources and corresponding identifiers, obtained from the @@ -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; |