Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Sharing STDIN after fork

by Bod (Parson)
on Feb 13, 2022 at 02:09 UTC ( [id://11141358]=perlquestion: print w/replies, xml ) Need Help??

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

I am trying to create an email sending system that can be driven directly from our CRM. The current system which this will replace works fine but involves copying and pasting blocks of text with addresses and HTML code for emails. So I want something easier to use and less error-prone.

The script we currently use calls itself with a query string parameter set so it tracks the sending of the emails. The problem is that it can behave strangely if refreshed or called again whilst an email is still sending. So, for the replacement, I am trying to fork a process. One process will send the emails and the other will load a webpage that will periodically check the progress through AJAX calls. The progress will be stored in a DB table.

I am getting strange behaviour which I think is because sometimes one process grabs STDIN and other times the other process gets it.

#!/usr/bin/perl use CGI::Carp qw(fatalsToBrowser); use strict; use warnings; my ($etc, $pid); if ($ENV{'QUERY_STRING'} =~ /etc=(\d{6})/ ) { $etc = $1; if ( !defined($pid = fork()) ) { die "Unable to fork!"; } } else { die "Missing Email Tracking Code"; } our (%data, %file); our $dbh; our $user_number; use Template; use MIME::Lite; require "incl/common.pl"; require "incl/html.pl"; if ($pid != 0) { my $template = Template->new({INCLUDE_PATH => "$ENV{'DOCUMENT_ROOT +'}/template"}); &html_head; my $vars = { 'command' => $data{'command'}, 'mail' => $data{'mail'}, }; $template->process('sendmail.tt', $vars); &html_foot; exit; } open my $fh, '>', "testfile.txt"; print $fh "MAIL - $data{'mail'}\n\n"; #.... # spend a long time sending email #....

Within the require "incl/common.pl"; file, the %data hash is populated from the $ENV{'QUERY_STRING'} variable and STDIN. This file also connects the database. Sometimes $data{'mail'} is passed to the template and sometimes it is written to the test file.

I have had problems with fork before where I have made the database connection after the fork. Then one process has ended and closed the database connection on the other process. For that reason, here I am connecting after the fork. I don't really want to play with the require (it is nasty but works) as a lot of other code relies on it.

I have rarely used fork so...am I along the right lines in how I am doing this with fork or is there a better way?
Would the solution to the problem be to do all the hash populating and database connection stuff before the fork then make a copy of the hash and database handle in each process?

Replies are listed 'Best First'.
Re: Sharing STDIN after fork
by Corion (Patriarch) on Feb 13, 2022 at 07:48 UTC

    The STDIN filehandle is shared between your two processes, and as you already noticed, whatever process comes first reads it all.

    You could set a flag to specify, which process should read STDIN, or you could read all of STDIN and then call fork().

      Thanks Corion

      Does it make any difference which process is used for which operation or are they interchangeable?

        No, both processes are almost identical - the only difference when they start out is the result of the call to [func://fork].

        Usually, the web server will wait for the original program, not the forked copy, to finish before considering the HTTP call complete, so you might want to put long-running stuff into the child. The child process is the one with a zero from fork().

Re: Sharing STDIN after fork
by talexb (Chancellor) on Feb 13, 2022 at 17:46 UTC

    It seems like you are trying to cram a great deal of functionality into a single piece of code, and I'm not sure this is the easiest approach.

    You could diversify by having one piece of code receive E-Mail requests from the CRM (or poll the CRM for outstanding requests), and add those to a database table. Another piece of code could look for unsent table entries, build the content (so that's where the template would get used), store the result back in the database table, send the E-Mail, and update the table with the result. Finally, a simple CGI could show the most recent table entries and their status (Not sent, Processing, Processed -- with link to text that was sent).

    Alex / talexb / Toronto

    Thanks PJ. We owe you so much. Groklaw -- RIP -- 2003 to 2013.

      It seems like you are trying to cram a great deal of functionality into a single piece of code

      Yes...having slept on it, I have sort of come to the same conclusion!

      I am definitely thinking that feeding the people that need an email into a database table is a better approach. That seems to give several advantages:

      • The list of emails to send doesn't get lost if there is some inturuption
      • People who are sent two different emails can have them automagically separated by a few hours
      • There's less for the sending script to contend with - it just picks the next address from the database and sends

        Great! This also follows the Unix philosophy, which is to have a variety of tools, each of which is excellent at doing one specific thing (with some obvious exceptions). Those tools can then be combined into a larger machine to complete a much more complicated task.

        Alex / talexb / Toronto

        Thanks PJ. We owe you so much. Groklaw -- RIP -- 2003 to 2013.

        Edit: Add the missing word 'complete' to the last sentence. Ugh.

Re: Sharing STDIN after fork
by etj (Deacon) on Feb 14, 2022 at 02:26 UTC
    Have you looked at similar things that already exist, like Minion? It seems you're reinventing work queues.
      Have you looked at similar things that already exist...

      No I hadn't but I'm now looking at Minion thanks

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11141358]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others exploiting the Monastery: (5)
As of 2024-03-28 20:21 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found