In the documentation of fork it says "Note that if your forked child inherits system file descriptors like STDIN and STDOUT...". Any elaboration on "like" would be appreciated - what specifically is inherited when you fork() a CGI script?

I'm trying to implement a non-blocking fork of a CGI script using this as a reference. However, I want to maintain/reopen the STDERR location in the child process but everything that I am trying also maintains the association with the parent process causing the parent to wait for the child to finish (e.g. the browser "loading" animation never stops). If I close STDERR or point it to /dev/null after the fork() it works fine but I need the child process to print STDERR messages to the same file that the parent process is using but still sever all associations with the parent process. So the way I'm implementing it is basically that upon submission the parent process will display a friendly message on the web page after the fork() and the child process will run for however long it needs to and kick off an email when it's done.

Script Workflow(-ish):
  page.cgi displays a form
  user submits the form
  page.cgi handles form submission
  print STDERR "forking...\n";
  fork()
  parent:
    page.cgi display "thanks, we'll email you when your request has been completed"
    end of execution and user goes off to different parts of the site.
  child:
    sleep 20; #page.cgi does a bunch of stuff based on the user's criteria
    print STDERR "sending email...\n"; #email the user with a summary of what actions were carried out by the script
    end of execution

So what's happening is although I'm receiving an email the "sending email..." isn't being logged which would mean that if something goes wrong in the child process our logs wouldn't reflect it. Turns out I was receiving a "Permission denied" error when trying to reopen STDERR for the child process but didn't know it until I changed the open(STDERR,...); to email me if it failed open(STDERR,...) || do {...$!...$mail->send()};
I don't know why I would be getting a "Permission denied" error if the script is being executed by www-data which opens STDERR in the first place (before the fork). It may have something to do with the way something is configured but I was curious if a monk had a more elegant solution.

Here's a bit of the code that implements the workflow that I described.
In the code you see below, the fork() is wrapped in a condition and only executes if the processing of the user's request is going to take a while to finish based on a threshold defined at the top of the script.

use POSIX qw(setsid);
use strict;

my $iteration_limit = 1; #15;

display_form();
process_form() if $cgi->param('action');

sub display_form {
    ...
}

sub process_form {
    my ($action) = @_;
    ...
    my $iterations = 0;
    map {map {$iterations++} ... #calculate iterations
    my $did_fork = 0;
    if ($iterations > $iteration_limit) {
        print STDERR "forking...\n";

        #non-blocking fork
        #$SIG{CHLD} = "IGNORE";
        #open(STDERR2, ">&STDERR")  || die "Cannot duplicate stderr: $!\n";
        defined(my $pid = fork())  || die "Cannot fork: $!\n";
        return if $pid;            #return if we are the parent process
        (setsid() != -1)           || die "Cannot start a new session: $!\n";
        chdir("/")                 || die "Cannot chdir to /: $!\n";
        open(STDIN,  "</dev/null") || die "Cannot read /dev/null: $!\n";
        open(STDOUT, ">/dev/null") || die "Cannot write to /dev/null: $!\n";
        close(STDERR);
        open(STDERR, ">>/var/log/apache/sitename-err.log") || do {
            my $err = $!;
            my $mail = ...::Email->new(
                'Type'    => 'Text/html',
                'To'      => 'my email address',#$email,
                'From'    => "site's email address",
                'Subject' => 'EMAIL SUBJECT (CHANGE THIS LATER) err',
                'Data'    => $err
            );
            $mail->send();
        };
        #close(STDERR);
        #open(STDERR, ">>&STDERR2") || die "Cannot restore stderr: $!\n";
        #close(STDERR2);
        #open(STDERR, ">&STDOUT")   || die "Cannot duplicate stdout: $!\n";
        #open(STDERR, ">&STDERR")   || die "Cannot duplicate stderr: $!\n";
        $did_fork++;
    }
    sleep 30; #simulate form processing

    #actual form processing commented out
    #...

    return unless $did_fork;
    print STDERR "sending email...\n";
    $renderer->{'template'} =~ s/\.html$/_email.html/;

    my $mail = ...::Email->new(
        'Type'    => 'Text/html',
        'To'      => 'my email address',#$email,
        'From'    => "site's email address",
        'Subject' => 'EMAIL SUBJECT (CHANGE THIS LATER)',
        'Data'    => ...
    );
    $mail->send();
    exit(0);
}

In reply to CGI fork() inherits by asohn

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.