Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

POE and Win32::Process control

by m-rau (Scribe)
on Feb 13, 2005 at 14:02 UTC ( #430575=perlquestion: print w/ replies, xml ) Need Help??
m-rau has asked for the wisdom of the Perl Monks concerning the following question:

I am working with POE, win32 and ActiveState. I need to implement event driven fork/ exec. POE::Wheel::Run says, that ActiveState Perl doesn't like this module one bit.. So what's next?

I created an event, which utilizes Win32::Process within the following process flow:
- PROCESS 1 (LEFT) --------- - PROCESS 2 (RIGHT) ------- ----------- | POE:event | ----------- | V ------------------------ | Win32::Process::Create | ------------------------ \ (1) ------------------------- | -----> | 1) system( target ) \\ | |(2) | 2>stdout.txt 2>&1 | V | 2) print #eof# to | ------------------------ | stdout.txt | | POE::Wheel::FollowTail | ------------------------- | of file stdout.txt | <--- ------------------------ | |(3) | V no | ------------------- | | got line #eof# in | --------- | stdout.txt | ------------------- | yes V ------- | end | -------
Remarks:
  1. Reason for this is, that I was not able to pipe stdout and stderr to a file using Win32::Process::Create. Question: Do you know how to pipe stdout/ stderr with Win32::Process?
  2. I want to follow the target process' output. It is extremely important that multiple users cann follow the output. Hence, I need to create an output stream (file stdout.txt). Now I can create a POE::Wheel::FollowTail for every user interested in the target process' stdout and stderr.
  3. The POE::Wheel::FollowTail needs to know when there is no further output. Either process 2 (right side) needs to use POE's inter-kernel-communication (POE::Component::IKC) to inform process 1 (left side) about the process termination (system call is done). I thought, adding some #eof# indicator is much simpler.

Question is:

Since process 2 (right side) is kind of a child process, the process itself must be controlled by process 1 (left side) acting as a parent process. I have the process id of process 2 (right side) available via Win32::Process -> GetProcessID() and I can perform a Win32::Process -> Kill. But this leaves the target-process triggered by the system call alive. This target-process is the critical process. All other processes are wrappers. How to kill this process on a win32? Please consider the following issues:
  • I could use Win32::Process create method at process 2 (right side). The thing is, I do not know how to redirect stdout/ stderr with Win32::Process (see above).
  • I do not want to rely on fork in process 1 on the left side, since I do not trust fork on win32. Remember, ActiveState Perl doesn't like POE::Whell::Run which also utilizes fork.
  • Using exec instead of system at process 2 (right side) makes it impossible to inform the left process about process termination (#eof#).
Help of the Perl/ POE community is very welcome. But I think, this is more a Perl question, since POE acts only as the environment.

If you really need some code fragments then
sub server_executeStart { my ($self, $kernel, $heap) = @_[ OBJECT, KERNEL, HEAP ]; # [ ... ] (snip) # # trigger win32::process my $cmd = sprintf 'perl Message/exec.pl --command="%s" --parameter +="%s" --stdout="%s"', $node -> { command }, $node -> { parameter }, $node -> { stream }; &debug( 'Spawning [%s]', $cmd ); my $proc; if (!Win32::Process::Create( $proc, $^X, $cmd, 0, CREATE_NO_WINDOW +, "." )) { &debug( 'win32 error: [%s]', Win32::FormatMessage( Win32::GetL +astError() ) ); $self -> { win32error } = 1; } # [ ... ] (snip) # # inform the client about execution start $kernel -> post( 'IKC', 'post', "poe://$clientKernel/$clientSession/executeStart", [ ] ); # next state: execution control $kernel -> yield( 'executeControl' ); } sub server_executeControl { my ($self, $kernel, $heap) = @_[ OBJECT, KERNEL, HEAP ]; # [ ... ] (snip) # # Creating follow wheel for $file (process stdout/ stderr) POE::Session -> create( inline_states => { _start => sub { my ($heap) = @_[ HEAP ]; $heap -> { follow } = POE::Wheel::FollowTail -> new( Filename => $file, InputEvent => 'gotLine', Seek => 0, PollInterval => '#eof', ); }, gotLine => sub { my ($kernel, $heap, $line) = @_[ KERNEL, HEAP, ARG +0 ]; if ($line =~ /^\#eof\#$/) { # remove the wheel $heap -> { follow } -> DESTROY; delete $heap -> { follow }; } else { # Received $line, so inform the client about i +t $kernel -> post( 'IKC', 'post', "poe://$clientKernel/$clientSessio +n/executeControl", [ $line ] ); } }, }, ); }

The Message/exec.pl looks similar to this
#!perl my $cmd = sprintf '"%s" %s 1>"%s" 2>&1', $opt { command }, $opt { parameter }, $opt { stdout }; system( $cmd ); # indicate EOF open( FILE, ">>", $opt { stdout } ); print FILE "#eof#\n"; close( FILE );
Thanks in advance.
mr.

Comment on POE and Win32::Process control
Select or Download Code
Re: POE and Win32::Process control
by BrowserUk (Pope) on Feb 13, 2005 at 15:43 UTC

    At first glance I think you are using the wrong tools. I think that your task would be done much more simply using threads, but as you have only described the problems you are having with various attempts to achieve your goals with POE, rather than what your actual goals are, it is difficult to judge.

    You said:

    I want to follow the target process' output. It is extremely important that multiple users cann follow the output. Hence, I need to create an output stream (file stdout.txt)

    but it is not clear to me what you mean by: "multiple users cann follow the output"? Do you mean 'follow' as in tail -f?

    You want the script to launch a process and you want the script to be able to receive that output, but you also want the other "people", to be able to monitor that output.

    How?

  • Using concurrent copies of your script?
  • By somehow attaching to 'the one copy' of your script and your script "broadcasting" the output to all listeners that attached?
  • By having the program write it's output directly (or via indirection) to a file and your script 'tail-ing' that file after it has launched the process--so that other people can also monitor it using tail -f or whatever they choose?
  • Have your script launch the process and capture it's output and then, as well as doing whatever it needs to with it, also write it to a file so that other peope can monitor that file using tail or whatever?

    Each of these approaches is possible, but which is appropriate depends entirely upon what your requirements are.

    And I think any of them would be much simpler done using threads than POE--under win32 at least.


    Examine what is said, not who speaks.
    Silence betokens consent.
    Love the truth but pardon error.
      > what your actual goals are

      My actual goal is the implementation of a peer-to-peer framework. On top of this framework is a central executive node. Nodes are servers (database servers, analysis servers, collaboration servers etc.) and clients (desktop computers). The peers communicate with each other using messages (xml) and chat services. You see, events are the basis for the inter-node activities.

      So, this is the reason for POE, the multitasking and networking framework for Perl (...) POE is a framework for cooperative, event driven multitasking in Perl. Other languages have similar frameworks. Python has Twisted. TCL has "the event loop".

      .
      > but it is not clear to me what you mean by: > "multiple users cann follow the output"? > Do you mean 'follow' as in tail -f?

      Yes. Imagine, one user triggers a job from a client node. The central executive processes or possibly forwards the execution request , tracks and feeds back the output to the requesting node. Other nodes/ users are informed about the execution (utilizing chat services). Furthermore, the execution request has been enqueued in the central executive's message queue. Users can send read requests. If processing is already done, they simply receive the output. If processing is not done, yet, they see the same (following) output of the process.

      > You want the script to launch a process and you > want the script to be able to receive that output, > but you also want the other "people", to be able > to monitor that output. > How?

      Exactly.

      It is clear now, that the executors and output recipients reside on different network nodes.

      > - Using concurrent copies of your script?

      No.

      > - By somehow attaching to 'the one copy' of > your script and your script "broadcasting" the > output to all listeners that attached?

      Somehow. The central executive node actually triggers the request, receives the output and broadcasts the output.

      > - By having the program write it's output directly > (or via indirection) to a file and your script > 'tail-ing' that file after it has launched the > process--so that other people can also monitor it > using tail -f or whatever they choose?

      Somehow. See the above comment.

      > - Have your script launch the process and capture > it's output and then, as well as doing whatever it > needs to with it, also write it to a file so that > other peope can monitor that file using tail or > whatever?

      This is exactly what I want to achive.

      > Each of these approaches is possible, but which is > appropriate depends entirely upon what your > requirements are.

      What is the best approach if you understand my goals?

        That's a complex entity, rather beyond the scope of a "quick example". There are also some ambiguities in your description.

        If processing is already done, they simply receive the output. If processing is not done, yet, they see the same (following) output of the process.

        What if a read request is received for a process that has already started and produced half it's output? Does the new listener get everything that has already been sent to the file and any other listeners, or just that output the process produces after the request is received?

        Anyway, here's how to use a thread to spawn a command and 'tee' it's output to a file, and retain a copy in memmory for your main thread to process at it convenience whilst also getting on with anything else it needs to do (like communicating with clients and monitoring queues):

        #! perl -slw use strict; use threads qw[ yield async ]; use threads::shared; my( $cmd, $file ) = @ARGV; my $done : shared = 0; my @lines : shared; async { my $pid = open my $CMD, "$cmd |" or die "$cmd : $!"; open my $fh, '>', $file or die "$file : $!"; while( <$CMD> ) { chomp; print $fh $_; ## output to the file push @lines, $_; ## and push it to a shared array } $done = 1; }->detach; my $n = 0; while( !$done ) { if( @lines ) { ## lines to be processed print pop @lines; ## process them } else { ## Else nothing to do but wait. yield; } }

        Whether that would work in conjunction with POE I'm not sure.

        Personally, I would write the whole thing using threads, but that's rather more than your OP asked for, and would take a fair amount of effort.


        Examine what is said, not who speaks.
        Silence betokens consent.
        Love the truth but pardon error.
Re: POE and Win32::Process control
by rcaputo (Chaplain) on Jun 10, 2005 at 21:27 UTC

    Just a quick followup to update the record for people reading this in the future: POE::Wheel::Run is currently supported on ActiveState Perl. It didn't at the time of your writing, though.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (7)
As of 2014-12-20 07:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (95 votes), past polls