dbooth has asked for the wisdom of the Perl Monks concerning the following question:

Update 3-Jan-2012: SOLVED! I discovered that this problem was being caused by the mere inclusion of the following module:

use Test::MockObject;
even though no functions from that module were being called. Since this appears to be a bug in Test::MockObject, I reported the bug here: https://rt.cpan.org/Public/Bug/Display.html?id=73723 That bug report shows the exact minimal handler code that I used to reproduce the bug.

Original post:
What is the right way to run a shell command from mod_perl2? In a non-mod_perl2 script I would normally use backticks `...` or system(...) but they are causing a child process segmentation fault after several successful HTTP requests, so I am guessing there is some kind of bad interaction with Apache2 threads going on, or some kind of race condition with the child process.

To be clear, from a mod_perl2 response handler I simply wish to run a short shell command and capture its stdout and stderr to files or strings for further processing before sending back the HTTP response. I.e., my response handler should block until the shell command completes. As a simple example I tried both $foo=`date` and system("date > /tmp/date") and they both work correctly for a few HTTP requests and then cause an Apache2 child segmentation fault: "[Mon Jan 02 11:16:46 2012] [notice] child pid 6833 exit signal Segmentation fault (11)". I have verified that it is the Apache2 child process that is seg faulting -- not the "date" process -- by using "pgrep apache2" to list the Apache2 process IDs before running the test, and it is indeed one of those Apache2 PIDs that I see in the Apache2 error log message when this happens.

Update 3-Jan-2012: My current test script successfully makes four HTTP GET requests to my mod_perl2 handler without incident, during which my subprocess command runs properly. On the fifth request, the Apache2 child process tries unsuccessfully to run my subprocess command, and that Apache2 child dies with a segmentation fault, causing the client to receive no data. The failure may not be noticed if you are not carefully watching the Apache2 error log, because Firefox and wget -- though not curl -- seem to automatically re-try the request, and Apache2 automatically spawns new children as needed if its children die.

I have searched the web and found several approaches that sound like they may be of interest, but I do not know which (if any) is the right approach to take:

  1. The "Practical mod_perl" book by Stas Bekman and Eric Cholet: http://modperlbook.org/html/10-2-5-A-Complete-Fork-Example.html They show an example of doing a fork from mod_perl, but their example was written for mod_perl version 1, and I do not know if the technique they describe is still applicable in mod_perl2.
    Update: I tried something along the lines of this and #2 (below) and I still get Apache2 child segmentation faults after a few successful requests. See the exact code that I used in my reply at http://www.perlmonks.org/?node_id=945998
  2. Advice on doing a fork from mod_perl2: http://stackoverflow.com/questions/471681/how-do-i-fork-properly-with-mod-perl2 Is this what I need to do? And if so, why hasn't this pattern been made into a module? Or has it? And if so, what module?
  3. Module Apache::forks : http://search.cpan.org/~rybskej/Apache-forks-0.03/lib/Apache/forks.pm This module seems to be very immature (version 0.03), so I don't know if I should try it. Plus the documentation warns that it must be the very first module loaded, which makes me worry that it is doing deep perl sorcery that had better be well vetted before one should rely on it.
  4. Module Apache::fork : http://search.cpan.org/~gozer/mod_perl-1.30/lib/Apache/fork.pm This module seems more mature, but the documentation is poorly written and very unclear, so I don't know if it is appropriate for my needs.
    Update: This seems to be for mod_perl version 1, so I cannot use it.
  5. Module Proc::SafeExec : <a href="http://search.cpan.org/~bilbo/Proc-SafeExec-1.4/lib/Proc/SafeExec.pm I don't know whether this module would provide any benefit, nor whether it is "safe" to use with mod_perl2.
  6. Some advice on spawning long-running process from mod_perl2: http://www.gossamer-threads.com/lists/modperl/modperl/101145?do=post_view_threaded#101145 However, my need is not for a long-running process. Plus I agree with the original poster's comment that "it really shouldn't be so complex": http://www.gossamer-threads.com/lists/modperl/modperl/101146?do=post_view_threaded#101146
  7. Module Apache2::SubProcess : http://perl.apache.org/docs/2.0/api/Apache2/SubProcess.html But this module seems to be for spawning a background process. I don't see any way in its API to block and wait for the child to finish. The API does not even seem to return the child's PID.

I've been struggling with this for many hours, and want to be sure that I end up with the right solution, so that my handler won't fail randomly at some future time when I least expect it. I am using Apache 2.2.14-5ubuntu8.7 with the worker MPM, perl 5.10.1 and libapache2-mod-perl2 2.0.4-6ubuntu1 under Ubuntu 10.04.

Suggestions from the masters, please? How should I approach this seemingly simple task?

  • Comment on Correct way to run a shell command from mod_perl2? (Child process segmentation fault) -- SOLVED!
  • Select or Download Code

Replies are listed 'Best First'.
Re: Correct way to run a shell command from mod_perl2? (Child process segmentation fault)
by repellent (Priest) on Jan 03, 2012 at 00:49 UTC
    Looking at the "Complete Fork Example" (OP bullet 1) outlined in Practical mod_perl, it reminds me of code to daemonize a forked process. You know, set a new session, be a process group leader, close file descriptors, chdir /, yada yada Richard Stevens yada, etc. Perhaps that will cut the umbilical cord of the child from the main Apache process.

    Try this example:
    my $pid = open(my $FROM_KID, "-|"); die("Failed fork: $!") unless defined($pid); if ($pid) # parent { chomp(my $output = do { local $/; <$FROM_KID> }); waitpid($pid, 0); printf <<'RESULT', $output, $? >> 8; Child output: %s Exit: %d RESULT } else { daemonize_self("", undef); # leave stdout alone exec "date" or die("Failed exec: $!"); }

    Now for the code of daemonize_self():

      I tried something along the lines of #1 and #2 and it still causes an Apache2 child segmentation fault after a few successful requests. Here is the code I used:

      sub RunCommand { my $pgm = shift; my @execargs = @_; $SIG{CHLD} = 'IGNORE'; # This should flush stdout. my $ofh = select(STDOUT);$| = 1;select $ofh; warn "Before first fork $$"; my $kpid = fork; defined($kpid) || die; if ($kpid) { # Parent process my $w = waitpid($kpid, 0); # warn "waitpid returned $w\n"; } else { close STDIN; close STDOUT; close STDERR; setsid() >= 0 or die; my $gpid = fork; defined($gpid) or die; if ($gpid) { my $w = waitpid($gpid, 0); # warn "waitpid returned $w\n"; } else { open(STDIN, "</dev/null") or die; open(STDOUT, ">/dev/null") or die; open(STDERR, ">/dev/null") or die; # Child process exec($pgm, @execargs) or die; } CORE::exit(0); } }

      I invoked that function from within my mod_perl2 response handler as:

      &RunCommand("/home/dbooth/rdf-pipeline/trunk/pid.perl");

      The pid.perl command I ran uses no stdin, stdout or stderr, but merely logs its PID and date to /tmp/pid.txt:

      #! /usr/bin/perl -w # Append the PID and date/time to /tmp/pid.txt # so that we can verify that this ran. use HTTP::Date; my $tmp = "/tmp/pid.txt"; open(my $fh, ">>$tmp") or die; my $t = time2str(time); print $fh "pid: $$ $t\n"; close $fh or die; exit 0;

      P.S. See also the update to my original post, in which I note that this command still runs successfully during the HTTP request in which the Apache2 child process dies from a segmentation fault.

Re: Correct way to run a shell command from mod_perl2? (Child process segmentation fault)
by Anonymous Monk on Jan 02, 2012 at 19:11 UTC

      I tried IPC::System::Simple and it did not help. I get the same segmentation fault as before.

      What are you suggesting that I upgrade? perl? mod_perl2?