Getting a non-zero error code from httpd(1) if the applicationdoesn"t compile

Getting a non-zero error code from httpd(1) if the applicationdoesn"t compile

am 15.10.2011 18:00:26 von avarab

Maybe this is something elementary that I've just missed, but it seems
to me that when transitioning from mod_perl 1 to 2 there's no way to
get a non-zero exit code from Apache if your application has
compilation errors.

I have an application that I'm loading like this:

PerlPostConfigRequire /usr/local/myapp/startup.pm

And in that file I do:

use strict;
use warnings;

use Apache2::ServerUtil ();

BEGIN {
die "Only makes sense when running under mod_perl\n" if !$ENV{MOD_PERL};

# We don't want to run this in the first Apache startup phase or
# we'll start up a useless instance of our application that'll
# consume memory
unless (Apache2::ServerUtil::restart_count() > 1) {
print STDERR "Running in Apache initialization phase #1\n";
return;
}

require lib;
lib->import('/usr/local/myapp/lib');

# Preload everything
require MyApp::Preload;
require MyApp::Apache::Handler;
}

1;

And even though I've inserted a "die" at line two of Handler.pm when
starting I get:

$ date; time sudo /usr/sbin/httpd; echo $?
Sat Oct 15 17:50:24 CEST 2011

real 0m1.113s
user 0m0.045s
sys 0m0.035s
0

At which point httpd returns zero exit code, but then proceeds to
start up my application, only later will I get this in the error log:

[Sat Oct 15 17:50:31 2011] [error] Died at
/usr/local/myapp/lib/MyApp/Apache/Handler.pm line 2

Of course if I load the application both when restart_count() == 1 and
>1 I'll get an error, but I'll also waste a lot of memory since I'll
be keeping around a copy of the application.

The reason I want to have a non-zero exit code if the application has
an error when initializing is that I have some production systems that
indicate problems if the startup fails, and while we could start up,
wait a bit, and then try if a http requests succeeds I'd rather change
how Apache operates than changing those systems if possible.

As far as I can tell my only options are:

* To find some Apache config option to make the server wait until
it's successfully loaded mod_perl in restart_count() > 1. This
option has thus far evaded be and it might not exist at all.

* Wrap Apache in an init script that forks, and then either polls
http:// or does an strace() and waits for sign of successfully
forked children.

* Change the startup.pm to have something like:

unless (Apache2::ServerUtil::restart_count() > 1) {
print STDERR "Running in Apache initialization phase #1\n";
system $^X => qw(MyApp::Apache::Handler -e1) and die $!;
return;
}

Which would slow down our startup, but wouldn't take up a
unnecessary amount of memory by keeping a the restart_count() ==
1's processes require()'d modules around.

Re: Getting a non-zero error code from httpd(1) if the application doesn"t compile

am 15.10.2011 19:35:11 von torsten.foertsch

On Saturday, 15 October 2011 18:00:26 =C6var Arnfjör=F0 Bjarmason wrote:
> Maybe this is something elementary that I've just missed, but it seems
> to me that when transitioning from mod_perl 1 to 2 there's no way to
> get a non-zero exit code from Apache if your application has
> compilation errors.
>=20
> I have an application that I'm loading like this:
>=20
> PerlPostConfigRequire /usr/local/myapp/startup.pm

How about loading it earlier?

$ cat xx

use POSIX();
POSIX::exit(15);

$ httpd -c 'Include "'$PWD/xx'"'; echo $?
15
$ cat xx

die

$ httpd -c 'Include "'$PWD/xx'"'; echo $?
Syntax error on line 2 of /.../xx:
\t(in cleanup) Died at /.../xx line 2.\n
1
$=20

Torsten Förtsch

=2D-=20
Need professional modperl support? Hire me! (http://foertsch.name)

Like fantasy? http://kabatinte.net

Re: Getting a non-zero error code from httpd(1) if the applicationdoesn"t compile

am 16.10.2011 02:26:53 von avarab

On Sat, Oct 15, 2011 at 19:35, Torsten Förtsch et> wrote:
> On Saturday, 15 October 2011 18:00:26 Ævar Arnfjörð Bjarma=
son wrote:
>> Maybe this is something elementary that I've just missed, but it seems
>> to me that when transitioning from mod_perl 1 to 2 there's no way to
>> get a non-zero exit code from Apache if your application has
>> compilation errors.
>>
>> I have an application that I'm loading like this:
>>
>>     PerlPostConfigRequire     /usr/local/myapp/start=
up.pm
>
> How about loading it earlier?
>
> $ cat xx
>
> use POSIX();
> POSIX::exit(15);
>

> $ httpd -c 'Include "'$PWD/xx'"'; echo $?
> 15
> $ cat xx
>
> die
>

> $ httpd -c 'Include "'$PWD/xx'"'; echo $?
> Syntax error on line 2 of /.../xx:
> \t(in cleanup) Died at /.../xx line 2.\n
> 1
> $

I can load code earlier, but unless I'm misunderstanding something the
PerlInterpreter running that code *won't* be the one that'll be forked
and serving requests.

Re: Getting a non-zero error code from httpd(1) if the application doesn"t compile

am 16.10.2011 16:05:12 von torsten.foertsch

On Sunday, 16 October 2011 02:26:53 Ævar Arnfjörð Bjarmason =
wrote:
> > $ cat xx
> >
> > use POSIX();
> > POSIX::exit(15);
> >

> > $ httpd -c 'Include "'$PWD/xx'"'; echo $?
> > 15
> > $ cat xx
> >
> > die
> >

> > $ httpd -c 'Include "'$PWD/xx'"'; echo $?
> > Syntax error on line 2 of /.../xx:
> > \t(in cleanup) Died at /.../xx line 2.\n
> > 1
> > $
>=20
> I can load code earlier, but unless I'm misunderstanding something the
> PerlInterpreter running that code won't be the one that'll be forked
> and serving requests.

If you are running prefork with a non-threaded perl (-Uuseithreads and
=2DUusemultiplicity) it's always the same interpreter.

The only thing is the code in a block is interpreted twice at
startup.

Between the both runs the interpreter shuts down. If modperl is compiled
as shared library it is even unloaded from memory. The same happens by
the way when Apache receives a SIGHUP (restart) or SIGUSR1 (graceful
restart).

Now let's see what happens with a threaded perl and worker MPM.

Here is again my xx file that is included after the main httpd.conf has
been read.

0 r2@opi ~/Download$ cat xx

package XX;
use ModPerl::Util ();
use Apache2::RequestRec ();
use Apache2::RequestIO ();
use Apache2::ServerUtil ();

sub stderr {readlink "/proc/$$/fd/2"}
sub l {
open my $f, '>>', '/tmp/xx';
print $f "$$: ".ModPerl::Util::current_callback.
": perl=3D".ModPerl::Util::current_perl_id.
" restart_count=3D".Apache2::ServerUtil::restart_count.
" stderr=3D".stderr."\n";
0;
}

l;

sub handler {
my ($r)=3D@_;
$r->content_type("text/plain");
$r->print("$$: perl=3D".ModPerl::Util::current_perl_id.
" restart_count=3D".Apache2::ServerUtil::restart_count."\n");
0;
}


SetHandler modperl
PerlResponseHandler XX

PerlChildInitHandler XX::l
PerlPostConfigHandler XX::l
PerlFixupHandler XX::l

0 r2@opi ~/Download$ :>/tmp/xx &&
> chmod 666 /tmp/xx &&
> sudo /opt/apache-worker/sbin/httpd -f /etc/opt/apache-worker/original/htt=
pd-modperl.conf -c 'Include "'$PWD/xx\"

0 r2@opi ~/Download$ cat /tmp/xx
19751: ��z: perl=3D0x79f3f0 restart_count=3D1 stderr=3D/dev=
/pts/4
19751: PerlPostConfigHandler: perl=3D0x79f3f0 restart_count=3D1 stderr=3D/v=
ar/opt/apache-worker/logs/error_log

PID 19751 is the parent of the process that is normally referred to as the
parent apache. This process is still a child of the shell and hence can
report a return code. In the first line STDERR is still connected to the
terminal. PostConfig runs after OpenLogs. So, here error_log is active and
STDERR goes there.

Now apache throws away all done so far and even unloads modperl from
memory. Also STDERR is closed and the httpd disconnects from the calling
shell.

19752: ï¿=BD>l: perl=3D0x9932b0 restart_count=3D2 stderr=3D/dev/null
19752: PerlPostConfigHandler: perl=3D0x9932b0 restart_count=3D2 stderr=3D/v=
ar/opt/apache-worker/logs/error_log

PPID of process 19752 is 1. Now httpd cannot report to the shell. The perl
interpreter 0x9932b0 is called the interpreter pool parent. Later on when
requests are served this interpreter is cloned. So, the clones inherit all
the code that is compiled in this interpreter. An exception to this rule
is the PerlOption +Parent. If a VHost is configured with this option it
gets a brand new interpreter pool parent.

19754: PerlChildInitHandler: perl=3D0x9932b0 restart_count=3D2 stderr=3D/va=
r/opt/apache-worker/logs/error_log
19755: PerlChildInitHandler: perl=3D0x9932b0 restart_count=3D2 stderr=3D/va=
r/opt/apache-worker/logs/error_log
19756: PerlChildInitHandler: perl=3D0x9932b0 restart_count=3D2 stderr=3D/va=
r/opt/apache-worker/logs/error_log

The same interpreter runs the ChildInit phase for each child process.

Now let's see what happens at runtime:

0 r2@opi ~/Download$ curl http://localhost/xx
19754: perl=3D0xb7a2d0 restart_count=3D2

Perl interpreter 0xb7a2d0 is a clone of 0x9932b0. It is created on demand.
But the fact that is can execute the code from the section shows
that it inherits such things.

0 r2@opi ~/Download$ tail -2 /tmp/xx
19756: PerlChildInitHandler: perl=3D0x9932b0 restart_count=3D2 stderr=3D/va=
r/opt/apache-worker/logs/error_log
19754: PerlFixupHandler: perl=3D0xb7a2d0 restart_count=3D2 stderr=3D

The Fixup handler is run by the same interpreter. This can be controlled
by the PerlInterpScope directive which is "request" by default. Were it
"handler" we couldn't say for sure that the Response phase is run by the
same interpreter as Fixup is. But both interpreters would be clones of the
interpreter pool parent.

Conclusion:

1) As can be seen by the example above httpd can report a return code to
the calling shell only while restart_count==1. Otherwise it has already
disconnected and is a child of the init process.

2) Application code can be compiled in sections, by PerlLoadModule
or PerlConfigRequire. It is inherited by the interpreter cloning process.

The important point for you is 1). httpd will report the status code to
the calling shell also if the interpreter is started by a PerlRequire or
PerlPostConfigRequire but only if restart_count is still 1.

Torsten Förtsch

=2D-=20
Need professional modperl support? Hire me! (http://foertsch.name)

Like fantasy? http://kabatinte.net