#!/usr/bin/perl # -*- cperl -*- use strict; use warnings; use lib 'lib'; use My::SafeProcess::Base; use POSIX qw(WNOHANG); ########################################################################### # Util functions ########################################################################### # #Print message to stderr # my $verbose= 0; sub message { if ($verbose > 0){ use Time::localtime; my $tm= localtime(); my $timestamp= sprintf("%02d%02d%02d %2d:%02d:%02d", $tm->year % 100, $tm->mon+1, $tm->mday, $tm->hour, $tm->min, $tm->sec); print STDERR $timestamp, " monitor[$$]: ", @_, "\n"; } } ########################################################################### # Main program ########################################################################### my $terminated= 0; # Protect against being killed in the middle # of child creation, just set the terminated flag # to make sure the child will be killed off # when program is ready to do that $SIG{TERM}= sub { message("!Got signal @_"); $terminated= 1; }; $SIG{INT}= sub { message("!Got signal @_"); $terminated= 1; }; my $parent_pid= getppid(); my $found_double_dash= 0; while (my $arg= shift(@ARGV)){ if ($arg =~ /^--$/){ $found_double_dash= 1; last; } elsif ($arg =~ /^--verbose$/){ $verbose= 1; } else { die "Unknown option: $arg"; } } my $path= shift(@ARGV); # Executable die "usage:\n" . " safe_process.pl [opts] -- [ [...]]" unless defined $path || $found_double_dash; message("started"); #message("path: '$path'"); message("parent: $parent_pid"); # Start process to monitor my $child_pid= create_process( path => $path, args => \@ARGV, setpgrp => 1, ); message("Started child $child_pid"); eval { sub handle_signal { $terminated= 1; message("Got signal @_"); # Ignore all signals foreach my $name (keys %SIG){ $SIG{$name}= 'IGNORE'; } die "signaled\n"; }; local $SIG{TERM}= \&handle_signal; local $SIG{INT}= \&handle_signal; local $SIG{CHLD}= sub { message("Got signal @_"); kill(9, -$child_pid); my $ret= waitpid($child_pid, 0); if ($? & 127){ exit(65); # Killed by signal } exit($? >> 8); }; # Monitoring loop while(!$terminated) { # Check if parent is still alive if (kill(0, $parent_pid) < 1){ message("Parent is not alive anymore"); last; } # Wait for child to terminate but wakeup every # second to also check that parent is still alive my $ret_pid; $ret_pid= waitpid($child_pid, &WNOHANG); if ($ret_pid == $child_pid) { # Process has exited, collect return status my $ret_code= $? >> 8; message("Child exit: $ret_code"); # Exit with exit status of the child exit ($ret_code); } sleep(1); } }; if ( $@ ) { # The monitoring loop should have been # broken by handle_signal warn "Unexpected: $@" unless ( $@ =~ /signaled/ ); } # Use negative pid in order to kill the whole # process group # my $ret= kill(9, -$child_pid); message("Killed child: $child_pid, ret: $ret"); if ($ret > 0) { message("Killed child: $child_pid"); # Wait blocking for the child to return my $ret_pid= waitpid($child_pid, 0); if ($ret_pid != $child_pid){ message("unexpected pid $ret_pid returned from waitpid($child_pid)"); } } message("DONE!"); exit (1);