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

I am working a problem I'm having with the qmail-scanner scripts. (not my code, but my users are losing email as a result).

I'm having a specific problem where a function within an eval is forking and attemping to communicate via a pipe between the child and parent. (this works).

There is an intermitent error where the child fails to open stdin (using the pipe input fd) and the author of the code decided that the child should exit.

I understand that a eval can not trap the exit and I would expect the child to exit.

But it appears that the parent is also exiting based on the lack of additional log messages from the parent.

I've been using eval and fork for years (just never together) and I'm seeking validation that the child exit within an eval can/should cause a parent to also exit.

Thanks,
Mark.

PS. I have searched the site and read the following node.
http://www.perlmonks.com/index.pl?node_id=166538

Replies are listed 'Best First'.
Re: eval and fork: [bad?] magic
by Abigail-II (Bishop) on Jul 16, 2002 at 17:40 UTC
    You fork and open a pipe between the processes. Then the child dies, leaving the pipe without readers. If the parents attempts to write to the pipe, it will get a SIGPIPE signal - for which the default action is to abort the process.

    Abigail

Re: eval and fork: [bad?] magic
by vladb (Vicar) on Jul 16, 2002 at 16:42 UTC
    I'm seeking validation that the child exit within an eval can/should cause a parent to also exit.

    Regardless of whatever my reply to this might be it will be a rater loose assertion as this is largely dependant on particular system you are talking about. The original author of the script might have gone through a certain design process and concluded that to do things certain way is most appropriate.

    However, from my personal experience, I'd rarely if ever follow the suit. I'd generally try to keep the parent alive nontheless and simply log an error message to a file where it could be picked up by say a monitoring agent (such as Big Sister, for instance). Unless the exit of the child process is not due to a crytical malfunction, there's hardly any need to cause the whole process to collapse.

    _____________________
    # Under Construction

      The system is linux. (i sometimes forget there are "other" systems out there....)

      It is my intention to keep the parent alive.
      I also believe that the author of this code wanted to keep the parent alive.
      The parent is coded to log an error message, but never does.

Re: eval and fork: [bad?] magic
by graff (Chancellor) on Jul 17, 2002 at 05:25 UTC
    I don't know if you tried putting "eval fork" in the search box at the top of a PM page to see what nodes would come up, but I recall a recent node where someone else wound up in a situation where fork was being called from within an eval block. It's too late at night here for me to figure out whether that case is relevant to yours, but it may be worth a look...

      A review of the logs show a consistent/coinicident error message from the qmail-scanner-queue.pl script.
      "Unable to reopen fd 0. (#4.3.0) - ..." from the qmail_requeue function. (see below)

      The qmail_requeue function is called from within the eval block (intention of the eval is to catch time-outs)

      The qmail_requeue function then forks and the parent/child clean up their respective file handles for the Pipe that exists between them.

      The child fails the open, and calls the tempfail which eventually exits with an error code 111.

      The parent should catch this error code and also call tempfail with a different error message (which is never observed).

      We never get the "Unable to queue message ($status). (#4.3.0) - ..." error message which means that the parent exited OR the child process did not return a non-zero error code OR waitpid did not return the proper child status.

      I have listed parts of the code that are important to my question. The authors information and our site specific info have been removed.

      I have submitted this information to the list server for this program on sourceforge. In the meantime, I need to work on a fix for our site. I personally not comfortable with the fork within the eval and I am asking if any body has done such a thing?

      I also think that the waitpid code need to be rewritten to deal with the signals portion of the child processes.

      comments.

      Thanks
      Mark.

      Un-modified code snippets below....

      #!/usr/bin/perl # # File: qmail-scanner-queue.pl # Version: 1.12 # # # This file was auto-generated by: # # ./configure --spooldir /var/spool/qmailscan --qmaildir /ISP/qmail -- +bindir /ISP/qmail/bin --qmail-queue-binary / var/qmail/bin/qmail-queue --admin ispadmin --domain ***.net --notify +sender,admin,recips --local-domains ***.net,***.com,***.com,***.com - +-lang en_GB --debug 1 --unzip 1 --add-dscr-hdrs 0 --archive 0 --re dundant 0 --log-details 0 --fix-mime 1 --scanners "auto" ... eval { $SIG{ALRM} = sub { die "Maximum time exceeded. Something cannot hand +le this message." }; alarm $MAXTIME; ... if ($quarantine_event) { &debug("unsetting TCPREMOTEIP env var"); delete $ENV{'TCPREMOTEIP'}; &email_quarantine_report; } else { &qmail_parent_check; &qmail_requeue($env_returnpath,$env_recips,"$scandir/$wmaildir/new +/$file_id"); } alarm 0; }; $alarm_status=$@; if ($alarm_status and $alarm_status ne "" ) { if ($alarm_status eq "Maximum time exceeded. Something cannot handle + this message.") { &tempfail("ALARM: taking longer than $MAXTIME secs. Requeuing...") +; } else { &tempfail("Requeuing: $alarm_status"); } } ... sub qmail_requeue { my($sender,$env_recips,$msg)=@_; my ($temp,$findate); ... local $SIG{PIPE} = 'IGNORE'; my $pid = fork; if (not defined $pid) { &tempfail ("Unable to fork. (#4.3.0) - $!"); } elsif ($pid == 0) { # In child. Mutilate our file handles. close EIN; open(STDIN,"<$msg")|| &tempfail ("Unable to reopen fd 0. (#4.3.0) +- $!"); ... } else { # In parent. close EOUT; # Feed the envelope addresses to qmail-queue. print EIN "$sender\0$env_recips"; close EIN || &tempfail ("Write error to envelope pipe. (#4.3.0) - + $!"); } # We should now have queued the message. Let's find out the exit st +atus # of qmail-queue. waitpid ($pid, 0); my $status =($? >> 8); if ($status != 0) { &tempfail ("Unable to queue message ($status). ( +#4.3.0) - $!" )} } sub tempfail { syslog('mail|info',"$V_HEADER-$VERSION:[$file_id] @_"); if ($log_details ne "syslog") { warn "$V_HEADER-$VERSION:[$file_id] ",@_, "\n"; } $nowtime = sprintf "%02d/%02d/%02d %02d:%02d:%02d", $mday, $mon+1, $ +year+1900, $hour, $min, $sec; &debug("tempfail: $V_HEADER-$VERSION: ",@_); close(LOG); &cleanup; exit 111; }
        Okay. So, did you take a look at that recent node that I mentioned earlier? If I read it right, I think it says that if you put the "child" portion of your code into an eval block, it will behave better:
        sub qmail_requeue { ... my $pid = fork; ... if (not defined $pid) { &tempfail ("Unable to fork. (#4.3.0) - $!"); } elsif ($pid == 0) { # In child. PUT THIS PART IN AN EVAL BLOCK eval { close EIN; ... } } else { # in parent ...
        But as I mentioned in one of my replies on that other thread, I am (like you) not conversant with using fork from within an eval block -- check that thread for yourself, and good luck!