Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Shared file-handles & fork fun

by kabeldag (Hermit)
on Nov 22, 2006 at 13:18 UTC ( [id://585507]=perlquestion: print w/replies, xml ) Need Help??

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

Hi folks,

How would I be able to write to the opened binary process in the child process from the parent
process in the else block. Should I share/reference the SREAD and SWRITE handles between both
the child and parent ? And how would I do that (without opening the handles globally) !?

I also need to be able to monitor the state of the binary process that I have spawned, that's why I have used open2(),
because it returns a PID that I can use waitpid() on and re-load the binary when it has terminated.
But at the same time, I also need to be able to write to the binary process. That's why I have separated both tasks with fork().
Maybe you think that what I am doing should/could be done differently ? Got any ideas ?

It's too damned hot. I need a cold glass of water ...

Update: PS --- All processes MUST be daemons/run in the background
use strict; use POSIX qw(setgid setuid); use IPC::Open2; my $got_sig=0; $SIG{'TERM'}=\&catch_sig; $SIG{'INT'}=\&catch_sig; $SIG{'HUP'}=\&catch_sig; $SIG{'QUIT'}=\&catch_sig; $SIG{'ABRT'}=\&catch_sig; sub catch_sig { $got_sig=1; } # Set UID & GID my $uid = xxx; my $gid = xxx; # Set GID before UID so that we can still call setuid(). setgid $uid; setuid $gid; my $program = "/path/to/some/binary"; my $actual_prog = "file_name_of_binary"; my @prog_opts = "some_sort_of_arguments"; # GMT offset needs to be changed accordingly in Daylight # Savings periods : Cause I am lazy ... my $gmt_offset = 8; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime(time); my $zone_standard_hour = ($hour + $gmt_offset); my $actual_year = ($year + 1900); my $actual_mon = ($mon+1); my $actual_standard_time = "$zone_standard_hour$min$sec-$mday-$actual_ +mon-$actual_year"; my $logfile = "/absolute/path/of/logfile/logs/$actual_standard_time-se +rver.log"; ################################################### # FORGET ABOUT ANYTHING ABOVE HERE. READ BELOW !!!# ################################################### my $spid; print "Launching \"$actual_prog\" as UID($uid)/GID($gid)\n"; unless(my $ppid=fork()) { if (!(my $pid=fork())) { # Child while(1) { open(STDERR,">> $logfile"); $spid = open2('SREAD', 'SWRITE', "$program @prog_opts"); waitpid($spid,'WNOHANG'); print STDOUT "Server \"$actual_prog\" exited. Re-load!\n"; } }else{ # Parent while(1) { if($got_sig==1) { print STDOUT "Closing up shop ...\n"; kill('KILL',$spid); exit(0); }else{ sleep 5; print SWRITE "Blah blah blah\n" or print STDOUT "No good, sunny boy !\n"; } } } }

Replies are listed 'Best First'.
Re: Shared file-handles & fork fun
by shmem (Chancellor) on Nov 22, 2006 at 14:41 UTC

    You seem to be mixing concepts.

    First, read open again. If you use a piped open, it will return the PID of the child process:

    my $pid = open my $fh, "| $some_program" or die "Can't run '$some_program': $!\n";

    Second, you will only need IPC::Open2 to write and read to/from the child process, but in your script I don't see you're attempting to read. open2 will also return a PID, and it will handle all that fork stuff and plumbing the pipes for you, so you don't need to fork.

    Third: if you open a filehandle in the child after forking, it is visible in the child only. If you open it before forking, the file will be open in both parent and child:

    #!/usr/bin/perl -w use strict; open(O,'>', "$$.tmp"); select O; $| = 1; # make output unbuffered print STDOUT "file is $$.tmp\n"; $SIG{'CHLD'} = \&reap; if ( (my $pid = fork()) == 0 ) { sleep 1; print "child ($$) here\n"; close O; exit 0; } else { print "parent ($$) here\n"; sleep 2; print "parent ($$) waking up..\n"; } sub reap { my $pid = wait; print "reaped PID $pid\n"; } __END__ file is 4782.tmp
    $ cat 4782.tmp parent (4782) here child (4783) here reaped PID 4783 parent (4782) waking up..

    Note that you must set $| = 1 for your filehandle to make output unbuffered if you want to keep the output of parent and child in order. Or better use IO::File and set autoflush FH 1.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      I don't need to read from the process, just write to it. I know open2() returns PID. I am specifically using open2() because it returns a PID and I am able to write to the process. But maybe I can do that like you said.

      I know I can open the handle globally (ie; before I fork), but I couldn't figure out how to work it into the code I wrote so that the flow works out to how it should.

      Thing is, I need to be able to write to the binary process, whilst also checking to see if it exists, if it doesn't, I need to restart it.

      If the binary process terminates for some reason, it must be restarted. But if I want to actually end the binary process, I need to kill off the parent(s). I couldn't see that this would work with what I had done unless I just send it a catchable SIGNAL. Of course if one were to send a SIGKILL signal to the parent, it wouldn't be catchable. Sure it will terminate it, but the binary process would still remain.

      I will try some stuff and re-post it.

      And here is a solution :

      ------------------------------------
      use POSIX ":sys_wait_h"; # Didn't do this before. my $spid; print "Launching \"$actual_prog\" as UID($uid)/GID($gid)\n"; unless (my $pid=fork()) { # Re-direct $program's STDERR to a file on the hard disk open(STDERR,">> $logfile"); $spid = open2(*SREAD, *SWRITE, "$program @prog_options"); while(1) { if($got_sig==1) { print STDOUT "GOT SIGNAL on $0\n"; print SWRITE "Hey there server process\n"; sleep 0.5; kill('KILL',$spid); exit(0); }else{ my $der = waitpid($spid,WNOHANG); # If return val < 0. It ended ... if($der==-1) { print STDOUT "Time to re-launch\n"; open(STDERR,">> $logfile"); $spid = open2(*SREAD, *SWRITE, "$program @prog +_options"); } } } }
      ------------------------------------

      Obviously I am only using one Perl process to manage the spawned binary process now. I also specifically included sys_wait_h : use POSIX ":sys_wait_h";, which I hadn't done before. Now the following works fine :
      my $der = waitpid($spid,WNOHANG); if($der==-1) { print STDOUT "Time to re-launch\n"; open(STDERR,">> $logfile"); $spid = open2(*SREAD, *SWRITE, "$program @prog +_options"); }
      and I can do other stuff whilst the child process state hasn't changed (ie; actually non-blocking waitpid()).

      This works fine for me. But I liked my other code. Oh well. Whatever works. I should probably re-read over waitpid() and how Perl implements it.

Re: Shared file-handles & fork fun
by zentara (Archbishop) on Nov 22, 2006 at 14:27 UTC
    I'm not entirely sure what you are trying to do with the script, but you might want to try a threaded script (instead of fork), since it makes sharing filehandles possible. See Re^3: Passing globs between threads

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (2)
As of 2024-04-25 23:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found