#!/usr/bin/perl -w
#
#  $Id: respawn,v 1.3 2002/12/13 22:51:44 dgregor Exp $
#

use Sys::Syslog qw(:DEFAULT setlogsock);
use Getopt::Std;
use POSIX 'setsid';

use strict;

# defaults
my ($max_respawn_count) = 5;
my ($max_respawn_time) = 20;
my ($respawn_too_fast_wait) = 300;

my ($usage) = <<EOF;
Usage: $0 [-h] [-F] [-c <count>] [-t <time>] [-w <wait>] <command> [<opts..>]
EOF

my (%opts);
if (!Getopt::Std::getopts('hFc:t:w:', \%opts)) {
	die $usage;
}

if (defined($opts{'h'})) {
	die $usage;
}

if (defined($opts{'c'})) {
	$max_respawn_count = $opts{'c'};
}

if (defined($opts{'t'})) {
	$max_respawn_time = $opts{'t'};
}

if (defined($opts{'w'})) {
	$respawn_too_fast_wait = $opts{'w'};
}

if (@ARGV < 1) {
	die $usage;
}

my ($command) = shift(@ARGV);
my (@command_opts) = @ARGV;

setlogsock("unix");
openlog($0, "cons,pid", "daemon"); 

if (!defined($opts{'F'})) {
	my $dir = `pwd`;
	chomp($dir);

	chdir '/'               or die "Can't chdir to /: $!\n";
	open STDIN, '/dev/null' or die "Can't read /dev/null: $!\n";
	open STDOUT, '>/dev/null'
                                       or die "Can't write to /dev/null: $!\n";
	defined(my $pid = fork) or die "Can't fork: $!\n";
	exit if $pid;
	setsid                  or die "Can't start a new session: $!\n";
	open STDERR, '>&STDOUT' or die "Can't dup stdout: $!\n";

	chdir $dir		or die "Can't chdir back to $dir: $!\n";
}

my (@respawn_times) = ();

while (1) {
	syslog("notice", "starting %s.", $command);
	system($command, @command_opts);
	syslog("notice", "%s exited.", $command);

	push(@respawn_times, time());

	while (@respawn_times > 0 &&
	    $respawn_times[0] < (time() - $max_respawn_time)) {
		shift(@respawn_times);
	}

	if (@respawn_times > $max_respawn_count) {
		syslog("err", "%s is respawning too fast.  Waiting %d.",
			$command, $respawn_too_fast_wait);
		sleep($respawn_too_fast_wait);
		@respawn_times = ();
	}
}

closelog();
