#!/usr/bin/perl
#
# Perl fork() example
#  by petje and telurion
#
# Forking in Perl can be a nasty bitch, and so it was for me. 
# RTFM'ing and peeking at sources got me to this example. 
# I hope that by sharing it, it will help others.
#
# Please note: Before Perl 5.8 signal handling wasn't what is
#  supposed to be! 
#
# If you have comment on my code, please drop me a note. 
# You can find my email at http://www.painfullscratch.nl
#

use POSIX ":sys_wait_h";
use POSIX ":signal_h";

%living_childs = ();
$SIG{CHLD} = \&COUNT_KIDS;

sub SIG_KID {
	my ($sig)=@_;
	if ($sig) {
		print "I'm receiving a signal ($signame[$sig])\n"
	} else {
		print "I'm dying here! ($$)\n";
	}
}

sub COUNT_KIDS { 
	my $return_value=waitpid(-1,WNOHANG);
	my $exit_code;
	my $sig;

	while($return_value>0) {
		delete($living_childs{$return_value});
		$exit_code=WEXITSTATUS($?);
		print "Child $return_value died with exitstatus $exit_code\n";
		if (WIFSIGNALED($?)) {
			$sig=WTERMSIG($?);
			print "Child $return_value died with $signame[$sig] ($sig)\n";
		}
		$return_value=waitpid(-1,WNOHANG);
	} 
	print "I have ".scalar(keys(%living_childs))." childs now\n";
} 

for(1..10) { 
	$pid = fork();
	# fork returns the pid of it's PARENT!!
	if($pid > 0) { 
		# This is the parent code.
		$living_childs{$pid} = '1';
	} 
	else { 
		my $i;
		# This seems odd, but else it won't work properly.
		#  all signals the childs receives, will be send to SIG_KID
		for($i=SIG_HUP;$i<=SIG_USR2;$i++) {
			$SIG{$signame[$i]}=\&SIG_KID;
		}
		my $sleeptime = sprintf("%.3f",rand(10));
		print "I'm child $$ and will sleep for $sleeptime seconds.\n";
		sleep($sleeptime);
		print "I'm child $$ and done sleeping $sleeptime seconds.\n";
		exit(0);
	} 
} 


# Wait for all other childs to report.	
do {
	$kid = waitpid(-1,0);
} until $kid == -1;

print "End of life: I've got ".scalar(keys(%living_childs))." children now.\n";

exit(0);
