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

I am running a remote process watching script (nothing more than 'ssh user@host pslist.exe' - yes, the damn remote host is a WinNT with Cygwin+OpenSSH)
my $pid = open(PSLIST, " ssh -2 $user\@ $peer ./pslist | ") || die "Remote process list gathering error:\n$!.\n"; while(<PSLIST>){ chomp($_); push(@pslist, $_); } close(PSLIST); foreach (@service){ # .. check the existence of a process }
Unfortunately, sometimes the spawned process simply does not finish. In order to prevent processes hanging out there, I wish to set a timeout, but I would need to set an asynchronous process (spam the ssh session, wait for its output in a loop, while incrementing a counter; when the counter exceeds the limit, just kill it). So, the question: there is a way to implement this kind of asynchronous communication?

Replies are listed 'Best First'.
Re: Read a process output asynchronously
by zengargoyle (Deacon) on Oct 14, 2003 at 21:50 UTC

    i've been playing with POE, how about this?

    use strict; use warnings; use POE qw( Wheel::Run ); POE::Session->create( inline_states => { _start => \&_start, got_line => \&got_line, timeout => \&timeout, }, ); sub _start { my ( $kernel, $heap ) = @_[ KERNEL, HEAP ]; $heap->{wheel_run} = POE::Wheel::Run->new( Program => <<_SH_, /bin/sh -c 'for i in 1 2 3 4; do echo kick; sleep 4; done; sleep 600' _SH_ StdoutEvent => 'got_line', ); $heap->{seen_input} = 0; $heap->{timeout} = 10; $heap->{start} = time; $kernel->delay_set( timeout => $heap->{timeout} ); } sub got_line { my ( $heap, $line ) = @_[ HEAP, ARG0 ]; warn sprintf "%02d line: $line$/", time - $heap->{start}; $heap->{seen_input} = 1; } sub timeout { my ( $heap, $kernel ) = @_[ HEAP, KERNEL ]; warn sprintf "%02d timeout_check$/", time - $heap->{start}; unless ( $heap->{seen_input} ) { warn "\tseems to have died$/"; $heap->{wheel_run}->kill; delete $heap->{wheel_run}; return; } warn "\tseems to still be kicking$/"; $heap->{seen_input} = 0; $kernel->delay_set( timeout => $heap->{timeout} ); } POE::Kernel->run; __END__ 00 line: kick 04 line: kick 08 line: kick 10 timeout_check seems to still be kicking 12 line: kick 20 timeout_check seems to still be kicking 30 timeout_check seems to have died

    i'm sure there's a cleaner way that i haven't learned yet.

Re: Read a process output asynchronously
by etcshadow (Priest) on Oct 15, 2003 at 01:20 UTC
    If you mean simply putting a timeout on the read, then you can do it like this using the standard method for timeouts (alarm, $sig{ALRM}, die). I didn't actually test it, though.
    my @pslist = (); while (!@pslist) { local $SIG{ALRM} = { die "TIMEOUT" }; my $pid; eval { alarm 5; # arrange for a wake-up call in 5 seconds $pid = open(PSLIST, " ssh -2 $user\@ $peer ./pslist | ") || die "Remote process list gathering error:\n$!.\n"; while(<PSLIST>){ chomp($_); push(@pslist, $_); } close(PSLIST); alarm 0; # clear the alarm, if we got this far ok } if ($@ =~ /TIMEOUT/) { # abort the attempt, and try again kill 'TERM' => $pid; @pslist = (); } elsif ($@) { die $@; } } foreach (@service){ # .. check the existence of a process }

    There is also at least one module on cpan for doing timeouts... but it's the sort of thing that is simple enough that you usually just wanna do it inline, rather than bringing in a module for it.


    ------------
    :Wq
    Not an editor command: Wq
Re: Read a process output asynchronously
by delirium (Chaplain) on Oct 15, 2003 at 00:54 UTC
    Parallel::ForkManager handles timeouts and asynchronous processes, although you may have to tweak things around for a good fit with what you are trying to do.

    Here is some sample code I threw together last week for a similar issue.

Re: Read a process output asynchronously
by tedrek (Pilgrim) on Oct 15, 2003 at 03:49 UTC

    It seems that for this specific problem you should be able to use IO::Select ( which uses select ( the 3 or 4 arg version ) ). It would go a little something like this:

    #!/usr/bin/perl -w use strict; use IO::Select; my $timeout = '10'; # We'll wait for up to 10 seconds for input. my @pslist; my $pid = open(my $shell, " ssh -2 $user\@ $peer ./pslist | ") || die "Remote process list gathering error:\n$!.\n"; my $select = IO::Select->new($shell); my $handle; while($handle = $select->can_read($timeout)){ chomp($_ = <$shell>); last unless $_; push(@pslist, $_); } close($shell); unless ($handle) {abort();} foreach (@service){ # .. check the existence of a process }
    I tried to swear at perl and it compiled!