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

Hello,

Env: Windows XP pro, Activestate Perl 5.6.1, 5.8.

I have been following the threads involving Open3:

IPC::Open3 woes
Open3 and bad gut feeling

but I noticed an issue that was not brought up it any of them.
If you timeout a process and kill it, you really only kill the CMD.exe that Open3 spawns in order to run the command specified. The only reason I noticed this, is that I did all my testing with "ping -t", and ended up with 100s of pings running on my system.

Is there any way to track what children are spawned by CMD in order to kill off everything if the command times out?

Thanks,
Steve

Edit: BazB, changed href links to [id://] links.

Replies are listed 'Best First'.
Re: Killing of ALL children made by Open3
by Zaxo (Archbishop) on Nov 12, 2003 at 07:06 UTC

    The kill builtin will accept a negative pid as argument and signal the entire process group. That may not be portable to win32, but you can try it. Even if the negative argument trick doesn't work, you can give kill a list of pid's to signal,

    kill 'INT', keys %process; delete $process{wait()} while %process;

    After Compline,
    Zaxo

      Hello Zaxo, thanks for the reply. I tried the negative pid idea, but that does not appear to be implemented in Windows. I have been looking into ActiveStates pseudo-fork(), which claims a kill of the parent process will clean up all child processes, but fork is a whole new can of worms I would rather avoid by using Open3. I would be happy to use your code snippet, but I am not able to retrieve the pids that are spawned inside the CMD.exe which is spawned from Open3. Know any tricks for that? A real ugly way would be taking PS snapshots and seeing what pids were created in the meantime, but you could end up killing things you down want to. Thanks, Steve
Re: Killing of ALL children made by Open3
by sgifford (Prior) on Nov 12, 2003 at 16:32 UTC
    Update: I just re-read your post, and I didn't answer your question at all! Sorry; I misunderstood what you were asking. I'll give it another whack in a seperate post.

    IPC::Open3 returns the PID of the process it creates, so you can simply remember that and kill it later. For example:

    #!/usr/bin/perl -w use strict; use IPC::Open3; { my %pidlist; sub my_open3 { my $pid=open3(@_); $pidlist{$pid}=1 if $pid; warn "my_open3: Started process $pid\n"; $pid; } sub my_killkids { my($signal) = shift; warn "my_killkids: Killing processes ",join(" ",keys %pidlist),"\n +"; kill $signal, keys %pidlist; } sub my_chld { my $pid = wait; warn "my_chld: Child $pid finished.\n"; delete $pidlist{$pid}; } } $SIG{CHLD} = \&my_chld; my_open3(\*STDOUT,\*STDIN,\*STDOUT,"sleep 10"); my_open3(\*STDOUT,\*STDIN,\*STDOUT,"sleep 10"); my_open3(\*STDOUT,\*STDIN,\*STDOUT,"sleep 10"); my_killkids('INT'); sleep(1); sleep(1); sleep(1); my_killkids('KILL');
      Hello sgifford,

      I modified your code to fit my example by changing one of the my_open3's to:

      my_open3(\*STDOUT,\*STDIN,\*STDOUT,"ping -t localhost");

      I was excited that your code seemed to work off the bat (no more lingering PINGS), unfortunately, it appears that the pings are not even being created, because the CMD.exe spawned by Open3 is being killed before it can spawn the PING (I believe, not sure though).

      I tested this by throwing in a sleep(10); before the my_killkids('INT'); and sure enough PING is created and stays around after all the killing is finished.

      I appreciate the reply, I will continue to work with your code because I believe you are on the right track.

      Thanks,
      Steve

        I don't have any Windows machines to test on; it works on Linux.

        One thing I noticed is that my arguments to open3 were wrong. This works better:

        my_open3(\*STDIN,'>&STDOUT','>&STDOUT',"ping 10.0.0.1"); my_open3(\*STDIN,'>&STDOUT','>&STDOUT',"ping 10.0.0.100"); my_open3(\*STDIN,'>&STDOUT','>&STDOUT',"ping 10.0.0.2");

        Another thing to think about is that CMD.EXE and PING could both be running at the same time, so killing the PID returned by open3 might just kill CMD.EXE and leave PING alone. You might be able to avoid running CMD.EXE by giving open3 ping and its arguments as a list instead of a string:

        my_open3(\*STDIN,'>&STDOUT','>&STDOUT','ping', '10.0.0.2');
        but I'm not sure.