(A continuation of:
http://marc.info/?l=apache-modperl&m=117507879929572&w=2
http://marc.info/?l=apache-modperl&m=119072925228529&w=2
)
I've just spent many frustrating days debugging a situation that
turned out to be caused by mod_perl's closing of file descriptor 1
(STDOUT).
Here's the reproducible case I ultimately got it down to. Using
mod_perl 2, with a dead-simple configuration and this handler:
use DBI;
sub handler {
my $dbh = DBI->connect( "DBI:mysql:$database", $user, $pass,
{ RaiseError => 1 } );
system('echo "hello"');
eval { $dbh->do("select 1") };
print "dbh - " . ( $@ ? "error: $...@\n" : "ok" ) . "\n";
return 0;
}
This outputs:
dbh - error: DBD::mysql::db do failed: Lost connection to MySQL
server during query at...
The DBI connection dies because mod_perl closes fd 1 (STDOUT). So the
next open - in this case the remote mysql connection created by DBI -
gets fd 1. The child process created by system() writes innocently to
STDOUT, which goes to our mysql socket, causing havoc.
We can confirm this by inserting this at the beginning of the handler:
sub handler {
open(my $fh, ">/dev/null");
print "fh - " . fileno($fh) . "\n";
...
Now this outputs:
fh - 1
dbh - ok
The initial open grabs fd 1, which means that DBI gets a different fd,
and the connection doesn't die.
Now this example is contrived, but replace 'echo "hello"' with any
innocuous system() or backtick call that accidentally sends a bit of
output to STDOUT, and you can see how this would cause all kinds of
baffling bugs. In my case, it was originally a call to "sendmail" that
intermittently sent a warning to STDOUT, and thus destroyed our first
database connection. It worked fine in mod_perl 1, but started
breaking when we upgraded to mod_perl 2.
Is there really no way to fix this in mod_perl, perhaps by
automatically opening a /dev/null handle as I did above? Other future
developers will surely endure the same hours of frustration I did if
it is left alone.
Thanks
Jon