File Coverage

lib/Apache/Defaults.pm
Criterion Covered Total %
statement 118 160 73.7
branch 49 84 58.3
condition 3 6 50.0
subroutine 23 26 88.4
pod 8 10 80.0
total 201 286 70.2


line stmt bran cond sub pod time code
1             package Apache::Defaults;
2 3     3   25341 use strict;
  3         26  
  3         86  
3 3     3   15 use warnings;
  3         5  
  3         77  
4 3     3   14 use File::Spec;
  3         6  
  3         69  
5 3     3   1593 use IPC::Open3;
  3         13138  
  3         240  
6 3     3   2138 use Shell::GetEnv;
  3         101912  
  3         146  
7 3     3   2427 use DateTime::Format::Strptime;
  3         1807484  
  3         21  
8 3     3   2340 use Text::ParseWords;
  3         4199  
  3         218  
9 3     3   23 use Symbol 'gensym';
  3         14  
  3         153  
10 3     3   19 use Carp;
  3         5  
  3         3982  
11              
12             our $VERSION = '1.03';
13              
14             sub new {
15 3     3 1 2010 my $class = shift;
16 3         17 my $self = bless { on_error => 'croak' }, $class;
17 3         13 local %_ = @_;
18 3         6 my $v;
19              
20 3 50       16 if (my $v = delete $_{on_error}) {
21             croak "invalid on_error value"
22 0 0       0 unless grep { $_ eq $v } qw(croak return);
  0         0  
23 0         0 $self->{on_error} = $v;
24             }
25            
26 3         25 my @servlist;
27 3 50       16 if ($v = delete $_{server}) {
28 3 50       11 if (ref($v) eq 'ARRAY') {
29 0         0 @servlist = @$v;
30             } else {
31 3         9 @servlist = ( $v );
32             }
33             } else {
34 0         0 @servlist = qw(/usr/sbin/apachectl /usr/sbin/httpd /usr/sbin/apache2);
35             }
36            
37 3 50       10 if (my @select = grep { -x $_->[0] }
  3 0       691  
38 3         19 map { [ shellwords($_) ] } @servlist) {
39 3         28 $self->{server} = shift @select;
40             } elsif ($self->{on_error} eq 'return') {
41 0         0 $self->{status} = 127;
42 0         0 $self->{error} = "No suitable httpd binary found";
43             } else {
44 0         0 croak "No suitable httpd binary found";
45             }
46              
47 3         9 my $envfile = delete $_{environ};
48 3 50       16 croak "unrecognized arguments" if keys(%_);
49              
50 3 100       10 if ($envfile) {
51 1 50       14 unless (-f $envfile) {
52 0 0       0 if ($self->{on_error} eq 'return') {
53 0         0 $self->{status} = 127;
54 0         0 $self->{error} = "environment file $envfile does not exist";
55 0         0 return $self;
56             } else {
57 0         0 croak "environment file $envfile does not exist";
58             }
59             }
60 1 50       14 unless (-r $envfile) {
61 0 0       0 if ($self->{on_error} eq 'return') {
62 0         0 $self->{status} = 127;
63 0         0 $self->{error} = "environment file $envfile is not readable";
64 0         0 return $self;
65             } else {
66 0         0 croak "environment file $envfile is not readable";
67             }
68             }
69              
70 1         2 my $env = eval {
71 1         12 Shell::GetEnv->new('sh', ". $envfile", { startup => 0 });
72             };
73 1 50       39351 if ($@) {
    50          
74 0 0       0 if ($self->{on_error} eq 'return') {
75 0         0 $self->{status} = 127;
76 0         0 $self->{error} = $@;
77 0         0 return $self;
78             } else {
79 0         0 croak $@;
80             }
81             } elsif ($env->status) {
82 0 0       0 if ($self->{on_error} eq 'return') {
83 0         0 $self->{status} = $env->status;
84 0         0 $self->{error} = "Failed to inherit environment";
85 0         0 return $self;
86             } else {
87 0         0 croak sprintf("Got status %d trying to inherit environment",
88             $env->status);
89             }
90             } else {
91 1         28 $self->{environ} = $env->envs;
92             }
93             }
94              
95 3 50       261 $self->_get_version_info unless $self->status;
96 3 50       71 $self->_get_module_info unless $self->status;
97            
98 3         171 return $self;
99             }
100              
101 0     0 1 0 sub server { shift->{server}[0] }
102 6     6 1 16 sub server_command { @{shift->{server}} }
  6         71  
103 0     0 1 0 sub environ { shift->{environ} }
104              
105             sub probe {
106 6     6 0 38 my ($self, $cb, @opt) = @_;
107              
108 6         467 open(my $nullin, '<', File::Spec->devnull);
109              
110 6         69 my $out = gensym;
111 6         191 my $err = gensym;
112 6 100       101 local %ENV = %{$self->{environ}} if $self->{environ};
  2         229  
113 6 50       53 if (my $pid = open3($nullin, $out, $err,
114             $self->server_command, @opt)) {
115 6         65388 while (<$out>) {
116 94         233 chomp;
117 94 50       140 last unless &{$cb}($_);
  94         234  
118             }
119 6         250 waitpid($pid, 0);
120 6 50       38 if ($self->{on_error} eq 'croak') {
    0          
121 6 50       125 if ($? == -1) {
    50          
    50          
122 0         0 croak "failed to execute " .$self->server . ": $!";
123             } elsif ($? & 127) {
124 0 0       0 croak sprintf("%s died with signal %d%s",
125             $self->server, $? & 127,
126             ($? & 128) ? ' (core dumped)' : '');
127             } elsif (my $code = $? >> 8) {
128 0         0 local $/ = undef;
129 0         0 croak sprintf("%s terminated with status %d; error message: %s",
130             $self->server, $code, <$err>);
131             }
132             } elsif ($?) {
133 0         0 local $/ = undef;
134 0         0 $self->{status} = $?;
135 0         0 $self->{error} = <$err>;
136             }
137             }
138 6         153 close $nullin;
139 6         78 close $out;
140 6         580 close $err;
141             }
142              
143             sub dequote {
144 25     25 0 113 my ($self, $arg) = @_;
145 25 100       158 if ($arg =~ s{^"(.*?)"$}{$1}) {
146 22         39 $arg =~ s{\\([\\"])}{$1}g;
147             }
148 25         193 return $arg;
149             }
150              
151             sub _get_version_info {
152 3     3   6 my $self = shift;
153             $self->probe(sub {
154 79     79   147 local $_ = shift;
155 79 100       889 if (m{^Server version:\s+(.+?)/(\S+)\s+\((.*?)\)}) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
156 3         99 $self->{name} = $1;
157 3         68 $self->{version} = $2;
158 3         55 $self->{platform} = $3;
159             } elsif (/^Server built:\s+(.+)/) {
160             $self->{built} =
161 3         145 DateTime::Format::Strptime->new(
162             pattern => '%b %d %Y %H:%M%S',
163             locale => 'en_US',
164             time_zone => 'UTC',
165             on_error => 'undef'
166             )->parse_datetime($1);
167            
168             } elsif (/^Server loaded:\s+(.+)$/) {
169 3         25 $self->{loaded_with} = $1;
170             } elsif (/^Compiled using:\s+(.+)$/) {
171 3         42 $self->{compiled_with} = $1;
172             } elsif (/^Architecture:\s+(.+)$/) {
173 3         15 $self->{architecture} = $1;
174             } elsif (/^Server MPM:\s+(.+)$/) {
175 3         18 $self->{MPM} = $1;
176             } elsif (/^\s+threaded:\s+(?<b>yes|no)/) {
177 3     3   1923 $self->{MPM_threaded} = $+{b} eq 'yes';
  3         1395  
  3         948  
  3         84  
178             } elsif (/^\s+forked:\s+(?<b>yes|no)/) {
179 3         41 $self->{MPM_forked} = $+{b} eq 'yes';
180             } elsif (/^\s+-D\s+(?<name>.+?)=(?<val>.+)$/) {
181 25         182 $self->{defines}{$+{name}} = $self->dequote($+{val});
182             } elsif (/^\s+-D\s+(?<name>\S+)(?:\s*(?<com>.+))?$/) {
183 24         178 $self->{defines}{$+{name}} = 1;
184             }
185 79         18691 return 1;
186 3         82 }, '-V');
187             }
188              
189             my @ATTRIBUTES = qw(status error
190             name
191             version
192             platform
193             built
194             loaded_with
195             compiled_with
196             architecture
197             MPM
198             MPM_threaded
199             MPM_forked);
200             {
201 3     3   25 no strict 'refs';
  3         7  
  3         2191  
202             foreach my $attribute (@ATTRIBUTES) {
203 13     13   356 *{ __PACKAGE__ . '::' . $attribute } = sub { shift->{$attribute} }
204             }
205             }
206              
207 1     1 1 5 sub server_root { shift->defines('HTTPD_ROOT') }
208              
209             sub server_config {
210 0     0 1 0 my $self = shift;
211 0         0 my $conf = $self->defines('SERVER_CONFIG_FILE');
212 0 0 0     0 if ($conf && !File::Spec->file_name_is_absolute($conf)) {
213 0         0 $conf = File::Spec->catfile($self->server_root, $conf);
214             }
215 0         0 return $conf;
216             }
217              
218             sub defines {
219 6     6 1 17 my $self = shift;
220 6 100       34 if (@_) {
221 5         19 return @{$self->{defines}}{@_};
  5         51  
222             }
223 1         3 return sort keys %{$self->{defines}};
  1         46  
224             }
225            
226             # List of module sources and corresponding identifiers, obtained from the
227             # httpd-2.4.6 source.
228             my %modlist = (
229             'event.c' => 'mpm_event_module',
230             'prefork.c' => 'mpm_prefork_module',
231             'worker.c' => 'mpm_worker_module',
232             'mod_access_compat.c' => 'access_compat_module',
233             'mod_actions.c' => 'actions_module',
234             'mod_alias.c' => 'alias_module',
235             'mod_allowmethods.c' => 'allowmethods_module',
236             'mod_asis.c' => 'asis_module',
237             'mod_auth_basic.c' => 'auth_basic_module',
238             'mod_auth_digest.c' => 'auth_digest_module',
239             'mod_auth_form.c' => 'auth_form_module',
240             'mod_authn_anon.c' => 'authn_anon_module',
241             'mod_authn_core.c' => 'authn_core_module',
242             'mod_authn_dbd.c' => 'authn_dbd_module',
243             'mod_authn_dbm.c' => 'authn_dbm_module',
244             'mod_authn_file.c' => 'authn_file_module',
245             'mod_authn_socache.c' => 'authn_socache_module',
246             'mod_authnz_ldap.c' => 'authnz_ldap_module',
247             'mod_authz_core.c' => 'authz_core_module',
248             'mod_authz_dbd.c' => 'authz_dbd_module',
249             'mod_authz_dbm.c' => 'authz_dbm_module',
250             'mod_authz_groupfile.c' => 'authz_groupfile_module',
251             'mod_authz_host.c' => 'authz_host_module',
252             'mod_authz_owner.c' => 'authz_owner_module',
253             'mod_authz_user.c' => 'authz_user_module',
254             'mod_autoindex.c' => 'autoindex_module',
255             'mod_buffer.c' => 'buffer_module',
256             'mod_cache.c' => 'cache_module',
257             'mod_cache_disk.c' => 'cache_disk_module',
258             'mod_cache_socache.c' => 'cache_socache_module',
259             'mod_cern_meta.c' => 'cern_meta_module',
260             'mod_cgi.c' => 'cgi_module',
261             'mod_cgid.c' => 'cgid_module',
262             'mod_charset_lite.c' => 'charset_lite_module',
263             'mod_data.c' => 'data_module',
264             'mod_dav.c' => 'dav_module',
265             'mod_dav_fs.c' => 'dav_fs_module',
266             'mod_dav_lock.c' => 'dav_lock_module',
267             'mod_dbd.c' => 'dbd_module',
268             'mod_deflate.c' => 'deflate_module',
269             'mod_dialup.c' => 'dialup_module',
270             'mod_dir.c' => 'dir_module',
271             'mod_dumpio.c' => 'dumpio_module',
272             'mod_echo.c' => 'echo_module',
273             'mod_env.c' => 'env_module',
274             'mod_example.c' => 'example_module',
275             'mod_expires.c' => 'expires_module',
276             'mod_ext_filter.c' => 'ext_filter_module',
277             'mod_file_cache.c' => 'file_cache_module',
278             'mod_filter.c' => 'filter_module',
279             'mod_headers.c' => 'headers_module',
280             'mod_heartbeat' => 'heartbeat_module',
281             'mod_heartmonitor.c' => 'heartmonitor_module',
282             'mod_ident.c' => 'ident_module',
283             'mod_imagemap.c' => 'imagemap_module',
284             'mod_include.c' => 'include_module',
285             'mod_info.c' => 'info_module',
286             'mod_isapi.c' => 'isapi_module',
287             'mod_lbmethod_bybusyness.c' => 'lbmethod_bybusyness_module',
288             'mod_lbmethod_byrequests.c' => 'lbmethod_byrequests_module',
289             'mod_lbmethod_bytraffic.c' => 'lbmethod_bytraffic_module',
290             'mod_lbmethod_heartbeat.c' => 'lbmethod_heartbeat_module',
291             'util_ldap.c' => 'ldap_module',
292             'mod_log_config.c' => 'log_config_module',
293             'mod_log_debug.c' => 'log_debug_module',
294             'mod_log_forensic.c' => 'log_forensic_module',
295             'mod_logio.c' => 'logio_module',
296             'mod_lua.c' => 'lua_module',
297             'mod_macro.c' => 'macro_module',
298             'mod_mime.c' => 'mime_module',
299             'mod_mime_magic.c' => 'mime_magic_module',
300             'mod_negotiation.c' => 'negotiation_module',
301             'mod_nw_ssl.c' => 'nwssl_module',
302             'mod_privileges.c' => 'privileges_module',
303             'mod_proxy.c' => 'proxy_module',
304             'mod_proxy_ajp.c' => 'proxy_ajp_module',
305             'mod_proxy_balancer.c' => 'proxy_balancer_module',
306             'mod_proxy_connect.c' => 'proxy_connect_module',
307             'mod_proxy_express.c' => 'proxy_express_module',
308             'mod_proxy_fcgi.c' => 'proxy_fcgi_module',
309             'mod_proxy_fdpass.c' => 'proxy_fdpass_module',
310             'mod_proxy_ftp.c' => 'proxy_ftp_module',
311             'mod_proxy_html.c' => 'proxy_html_module',
312             'mod_proxy_http.c' => 'proxy_http_module',
313             'mod_proxy_scgi.c' => 'proxy_scgi_module',
314             'mod_proxy_wstunnel.c' => 'proxy_wstunnel_module',
315             'mod_ratelimit.c' => 'ratelimit_module',
316             'mod_reflector.c' => 'reflector_module',
317             'mod_remoteip.c' => 'remoteip_module',
318             'mod_reqtimeout.c' => 'reqtimeout_module',
319             'mod_request.c' => 'request_module',
320             'mod_rewrite.c' => 'rewrite_module',
321             'mod_sed.c' => 'sed_module',
322             'mod_session.c' => 'session_module',
323             'mod_session_cookie.c' => 'session_cookie_module',
324             'mod_session_crypto.c' => 'session_crypto_module',
325             'mod_session_dbd.c' => 'session_dbd_module',
326             'mod_setenvif.c' => 'setenvif_module',
327             'mod_slotmem_plain.c' => 'slotmem_plain_module',
328             'mod_slotmem_shm.c' => 'slotmem_shm_module',
329             'mod_so.c' => 'so_module',
330             'mod_socache_dbm.c' => 'socache_dbm_module',
331             'mod_socache_dc.c' => 'socache_dc_module',
332             'mod_socache_memcache.c' => 'socache_memcache_module',
333             'mod_socache_shmcb.c' => 'socache_shmcb_module',
334             'mod_speling.c' => 'speling_module',
335             'mod_ssl.c' => 'ssl_module',
336             'mod_status.c' => 'status_module',
337             'mod_substitute.c' => 'substitute_module',
338             'mod_suexec.c' => 'suexec_module',
339             'mod_unique_id.c' => 'unique_id_module',
340             'mod_unixd.c' => 'unixd_module',
341             'mod_userdir.c' => 'userdir_module',
342             'mod_usertrack.c' => 'usertrack_module',
343             'mod_version.c' => 'version_module',
344             'mod_vhost_alias.c' => 'vhost_alias_module',
345             'mod_watchdog.c' => 'watchdog_module',
346             'mod_xml2enc.c' => 'xml2enc_module'
347             );
348              
349             sub preloaded {
350 3     3 1 51 my $self = shift;
351 3 100       23 if (@_) {
352 2         5 return @{$self->{preloaded}}{@_};
  2         31  
353             }
354 1         11 return sort keys %{$self->{preloaded}};
  1         34  
355             }
356              
357             sub _get_module_info {
358 3     3   11 my $self = shift;
359             $self->probe(sub {
360 15     15   79 local $_ = shift;
361             # print "GOT $_\n";
362 15 100 100     284 if (/^\s*(\S+\.c)$/ && exists($modlist{$1})) {
363 6         102 $self->{preloaded}{$modlist{$1}} = $1;
364             }
365 15         146 return 1;
366 3         66 }, '-l');
367             }
368              
369             1;
370             __END__
371             =head1 NAME
372              
373             Apache::Defaults - Get default settings for Apache httpd daemon
374              
375             =head1 SYNOPSIS
376              
377             $x = new Apache::Defaults;
378             print $x->name;
379             print $x->version;
380             print $x->server_root;
381             print $x->server_config;
382             print $x->built;
383             print $x->architecture;
384             print $x->MPM;
385             print $x->defines('DYNAMIC_MODULE_LIMIT');
386             print $x->preloaded('cgi_module');
387              
388             =head1 DESCRIPTION
389              
390             Detects the default settings of the Apache httpd daemon by invoking
391             it with appropriate options and analyzing its output.
392              
393             =head1 METHODS
394              
395             =head2 new
396              
397             $x = new Apache::Defaults(%attrs);
398              
399             Detects the settings of the apache server and returns the object representing
400             them. Attributes (I<%attrs>) are:
401              
402             =over 4
403              
404             =item C<server>
405              
406             Full pathname of the B<httpd> binary to inspect. The argument can also be
407             a reference to the list of possible pathnames. In this case, the first of
408             them that exists on disk and has executable privileges will be used. Full
409             command line can also be used, e.g.:
410              
411             server => '/usr/sbin/httpd -d /etc/httpd'
412              
413             The default used in the absense of this attribute is:
414              
415             [ '/usr/sbin/apachectl', '/usr/sbin/httpd', '/usr/sbin/apache2' ]
416              
417             The use of B<apachectl> is preferred over directly invoking B<httpd> daemon,
418             because the apache configuration file might contain referenmces to environment
419             variables defined elsewhere, which will cause B<httpd> to fail. B<apachectl>
420             takes care of this by including the file with variable definitions prior to
421             calling B<httpd>. See also C<environ>, below.
422            
423             =item C<environ>
424              
425             Name of the shell script that sets the environment for B<httpd> invocation.
426             Usually, this is the same script that is sourced by B<apachectl> prior to
427             passing control over to B<httpd>. This option provides another solution to
428             the environment problem mentioned above. E.g.:
429              
430             $x = new Apache::Defaults(environ => /etc/apache2/envvars)
431              
432             =item C<on_error>
433              
434             Controls error handling. Allowed values are C<croak> and C<return>.
435             If the value is C<croak> (the default), the method will I<croak> if an
436             error occurs. If set to C<return>, the constructor will return a valid
437             object. The B<httpd> exit status and diagnostics emitted to the stderr
438             will be available via the B<status> and B<error> methods.
439              
440             =back
441              
442             =head2 status
443              
444             $x = new Apache::Defaults(on_error => 'return');
445             if ($x->status) {
446             die $x->error;
447             }
448              
449             Returns the status of the last B<httpd> invocation (i.e. the value of
450             the B<$?> perl variable after B<waitpid>). The caller should inspect
451             this value, after constructing an B<Apache::Defaults> object with
452             the C<on_error> attribute set to C<return>.
453              
454             =head2 error
455              
456             Returns additional diagnostics if B<$x-E<gt>status != 0>. Normally, these are
457             diagnostic messages that B<httpd> printed to standard error before
458             termination.
459            
460             =head2 server
461              
462             $s = $x->server;
463            
464             Returns the pathname of the B<httpd> binary.
465              
466             =head2 server_command
467              
468             @cmd = $x->server_command;
469              
470             Returns the full command line of the B<httpd> binary.
471              
472             =head2 server_config
473              
474             $s = $x->server_config;
475              
476             Returns the full pathname of the server configuration file.
477            
478             =head2 environ
479              
480             $hashref = $x->environ;
481              
482             Returns a reference to the environment used when invoking the server.
483              
484             =head2 name
485              
486             $s = $x->name;
487              
488             Returns server implementation name (normally C<Apache>).
489              
490             =head2 version
491              
492             $v = $x->version;
493              
494             Returns server version (as string).
495              
496             =head2 platform
497              
498             $s = $x->platform;
499              
500             Platform (distribution) on which the binary is compiled.
501              
502             =head2 architecture
503              
504             Architecture for which the server is built.
505            
506             =head2 built
507              
508             $d = $x->built;
509              
510             Returns a B<DateTime> object, representing the time when the server
511             was built.
512              
513             =head2 loaded_with
514              
515             APR tools with which the server is loaded.
516            
517             =head2 compiled_with
518              
519             APR tools with which the server is compiled.
520            
521             =head2 MPM
522              
523             MPM module loaded in the configuration.
524              
525             =head2 MPM_threaded
526              
527             True if the MPM is threaded.
528              
529             =head2 MPM_forked
530              
531             True if the MPM is forked.
532              
533             =head2 defines
534              
535             @names = $x->defines;
536              
537             Returns the list of symbolic names defined during the compilation. The
538             names are in lexical order.
539              
540             @values = $x->defines(@names);
541              
542             Returns values of the named defines.
543              
544             =head2 server_root
545              
546             $s = $x->server_root;
547            
548             Returns default server root directory. This is equivalent to
549              
550             $x->defines('HTTPD_ROOT');
551              
552             =head2 preloaded
553              
554             @ids = $x->preloaded;
555              
556             Returns the list of the preloaded module identifiers, in lexical order.
557              
558             @sources = $x->preloaded(@ids);
559              
560             Returns the list of module source names for the given source identifiers.
561             For non-existing identifiers, B<undef> is returned.
562              
563             =head1 LICENSE
564              
565             GPLv3+: GNU GPL version 3 or later, see
566             L<http://gnu.org/licenses/gpl.html>.
567            
568             This is free software: you are free to change and redistribute it.
569             There is NO WARRANTY, to the extent permitted by law.
570            
571             =head1 AUTHORS
572              
573             Sergey Poznyakoff <gray@gnu.org>
574            
575             =cut
576