How does this Pipe work?

I have been reviewing another person's code (a more experienced developer than I am), and am somewhat baffled by the subroutine he is using to send email. Here is the subroutine:

########################

sub mail {
    my ($to, $from, $subject, $body, $extra_headers_href) = @_;

    $SIG{CHLD} = "IGNORE";

    my $pid = open(MAIL, "|-");

    if (!defined($pid)) {
        die("Couldn't fork");
    }

    if ($pid == 0) {
        # We're the child
        exec("/usr/sbin/sendmail", "-t", "-f$from");
    } else {
        # We're the parent
        print MAIL "From: $from\nTo: $to\nSubject: $subject\n\n";
        print MAIL $body;
        close MAIL;
        return 1;
    }
}

##################

What I really don't understand is how the pipe works, specifically, how the parent knows to write the mail information to MAIL before the child executes sendmail.

Will the child always execute after the parent has finished? In my testing, I am unable to tell.

This is just kind of confusing me, so if anyone could lend some advice as to how the child knows to wait for the parent to finish writing to MAIL before sending the email, I would be happily enlightended.

Thanks,

-Dan
dgoyetteAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

perldorkCommented:
Actually,

The parent doesn't know that :) .. sendmail, when used in this manner, will read from STDIN until it receives an EOF character .. then it will process the mail it has received.

When using fork, it is not guaranteed that the child will be created before the code the parent executes is run .. so it would be possible for the parent to start writing before the child is ready to accept the input ..

However, unless IO is set to be unbuffered, this should not be an issue, as the OS/perl will buffer some amount of text internally before sending it to the pipe.

Still, that code snippet doesn't make a lot of sense ... if the goal is to have the mail process not keep the main program from executing the pipe and mail send should be all done in the child .. the way it is done above the main program will still have to wait for the mail command to complete before continuing ...

Doing it in the style below would let the mail command run "asynchronously" ...

my $pid = fork();

if ($pid == 0) { # Child
    open(MAIL,"blah blah") || die "open failed: $!\n";
    print MAIL $stuff;
    print MAIL $more_stuff;
    close(MAIL);
    exit(0);
}

#  This part of the program will continue while sendmail does its' work in the child.




0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
PC_User321Commented:
Two processes are needed - one to run sendmail and the other to pump information into it.
The fork gives you the two processes, and the required data path between them.
0
dgoyetteAuthor Commented:
Excellent. That does answer my question. Thank you.
0
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

perldorkCommented:
Two processes are needed, but the explicit fork() is not
needed.

doing an open(SYMBOL,"cmd |")

causes perl to create the needed pipes and do the fork
exec pair for the child process, so the only reason to
then do an explicit fork() as well is to allow the mail to be sent and not have the main program have to wait for the sendmail command to complete.

0
PC_User321Commented:
>> ... to then do an explicit fork() as well ...
There is no explicit fork.
0
perldorkCommented:
You are right, there isn't .. my bad.

That still seems like the hard way to do things .. as opposed to doing what I thought i saw ;) ..

open(MAIL,"cmd |");

which does the fork/exec for the user.

I sure did see just what I wanted to while reading that ... man, time to get the eyes checked!
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Perl

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.