If you want to "whack" the shell and all of its subprocesses, place
the shell into its own process group. That way, when it forks off
other processes, they will be in the same group, and you can send a
signal to all of them in one fell swoop by killing zero minus the
process group's ID. (You might want to check out the perlipc man
page for more on this stuff.)
Here's some code that shows how to do it. First, we need a
troublemaker program to test out our mighty kung-fu on. Here's a
shell script that spawns five subprocesses that all emit annoying
output. We'll love telling this guy to "kick the oxygen
habit" later.
#!/bin/bash
# File: forkage.sh
for f in $(seq 1 5); do
( while : ; do echo $f; sleep 1; done ) &
done
wait
And now the Perl code that runs the troublemaker, reads output
from it (and its spawn), and after 20 lines kills 'em all off
to rescue us from the annoying brood:
#!/usr/bin/perl
use warnings;
use strict;
$SIG{CHLD} = 'IGNORE'; # to make sure there are no zombies
sub run_shell(@) {
my $pid = open my $pipe, "-|";
die "can't open forked pipe': $!" unless defined $pid;
if ($pid) {
# parent
return { pid => $pid, pipe => $pipe };
}
# child
open STDERR, ">&STDOUT" or die "can't dup 2>&1: $!";
setpgrp; # move into its own process group
exec @_; # give control of child to command
die "exec @_ failed: $!"; # should never get here
}
my $subshell = run_shell qw(./forkage.sh);
my $output_from_subshell = $subshell->{pipe};
my $lines_left = 20;
print while defined($_ = <$output_from_subshell>) and $lines_left--;
kill "HUP", -$subshell->{pid};
The run_shell function is where most of the magic
happens. It serves as a wrapper around open that
gives us more control over the resulting child process.
It forks off a child process that has its
STDOUT wired to $pipe, which the parent can
read from later. The child then starts a new process group with the
same ID as its own pid via setpgrp and finally uses
exec to run the command we passed in as arguments.
Meanwhile, the parent returns from the call to
run_shell with a hash containing the pid of and pipe from
the child. The parent grabs the pipe's filehandle and reads 20 lines
from it, printing each one in passing. Then, getting sick of all the
chatter, it sends a hang-up signal to every process in the child's
process group, thus shutting their annoying behavior down.
Hope this helps.
|