use warnings; use strict; use IO::Pty; use IO::Select; use POSIX ":sys_wait_h"; if (@ARGV < 2) { die "Arguments: logfile executable ..."; } exit if (fork); my $logfile; POSIX::setsid() or die "Can't start a new session: $!\n"; sub signal_handler { my $signame = shift; logMsg($logfile, "ERROR: caught signal $signame, exiting."); } $SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&signal_handler; sub terminate { my $pid = shift; my $signal = shift; kill $signal, $pid; my ($r, $starttime); $starttime = time; do { $r = waitpid($pid, &WNOHANG); } until ($r == $pid || (time - $starttime > 5)); if ($r == $pid) { return 1; } else { return undef; } } sub logMsg { my $fh = shift; my $msg = shift; my $t = scalar localtime; my $oldfh = select($fh); $|=1; print $fh "$t [$$]: $msg\n"; select($oldfh); } my $logfileName = shift @ARGV; $logfile = new IO::File ">>$logfileName" || die "Unable to open logfile $logfileName"; while (1) { my $pty = new IO::Pty; my ($readerr, $writeerr); pipe($readerr, $writeerr) || die "pipe $!\n"; if (my $pid = fork) { close($writeerr); my $select = new IO::Select; $select->add($pty); $select->add($readerr); my $run = 1; my $runtime = time; while ($run) { foreach my $fh ($select->can_read(0.25)) { my $buf; if (sysread($fh, $buf, 4096)) { logMsg($logfile, "child reports \"$buf\""); } } my $r = waitpid($pid, &WNOHANG); if ($r == $pid) { logMsg($logfile, "Warning: $pid died unexpectedly, restarting.\n"); $run = 0; } else { my @times = localtime(time); if ($times[2] == 7 && $times[1] == 0 && ($times[0] > 0 && $times[0] < 05)) { sleep 5; unless (terminate($pid, 15)) { unless (terminate($pid, 9)) { die "Unable to kill process $pid with SIGTERM or SIGKILL!"; } else { logMsg($logfile, "Warning: had to resort to SIGKILL to remove $pid.\n"); } } else { logMsg($logfile, "Info: killed $pid with SIGTERM.\n"); } $run = 0; } } } close($readerr); close($pty); } else { logMsg($logfile, "Info: starting " . join(" ", @ARGV)); close($readerr); my $slave = $pty->slave(); close $pty; $slave->set_raw(); open(STDOUT, ">&" . $slave->fileno); open(STDERR, ">&" . $writeerr->fileno); close($slave); exec(@ARGV) || die "exec: $!\n"; } }