#!/usr/local/bin/perl -w
# -*- perl -*-

# Cricket: a configuration, polling and data display wrapper for RRD files
#
#    Copyright (C) 1998 Jeff R. Allen and WebTV Networks, Inc.
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

# This is a smart wrapper for collector.
# It understands the concept of "subtree sets". It takes one
# argument, the subtree set it will process. It rolls the logfiles
# for that set, then runs the collector on that subtree set.

# it reads it's subtree sets from a config file, by default a file
# in the cricket install directory named 'subtree-sets'. You can
# and should put the file somewhere else, and use the -cf argument
# to tell collect-subtrees where to find it.

BEGIN {
    $gInstallRoot = (($0 =~ m:^(.*/):)[0] || "./") . ".";
}

use Getopt::Long;

$gCF = "$gInstallRoot/subtree-sets";
GetOptions( "cf=s" => \$gCF );

$gLogDir = "$ENV{'HOME'}/log";
$gBase = "$ENV{'HOME'}/config";
$gKeepLogs = 20;
$gUser = getlogin || getpwuid($<);	# idiom from perlfunc manpage

open(S, "<$gCF") ||
	die("Could not read $gCF file.\n");

while (<S>) {
	chomp;

	# nuke comments and blank lines
	s/^\s*#.*$//;
	next if (/^\s*$/);

	if (/^\s*set\s+(.*):/i) {
		$gCurSet = $1;
		next;
	} elsif (/^\s*base:\s+(.*)/i) {
		$gBase = $1;
		if ($gBase !~ m#^/#) {
			$gBase = "$ENV{'HOME'}/$gBase";
		}
	} elsif (/^\s*logdir:\s+(.*)/i) {
		$gLogDir = $1;
		if ($gLogDir !~ m#^/#) {
			$gLogDir = "$ENV{'HOME'}/$gLogDir";
		}
	} else {
		my($subtree) = $_;
		$subtree =~ s/^\s*//;
		if (! defined($gCurSet)) {
			warn("No set lines found. Ignoring subtree $subtree.\n");
		} else {
			push @{$gSets{$gCurSet}}, $subtree;
		}
	}
}
close(S);

# if none specified, do them all
if ($#ARGV == -1) {
	@ARGV = sort(keys(%gSets));
}

foreach $s (@ARGV) {
	my($subtreeRef) = $gSets{$s};
	if (! defined($subtreeRef)) {
		print STDERR "$0: unknown subtree name: $s\n";
		next;
	}

	my($lockfile) = "/tmp/$gUser-subtree-$s";

	if (-f $lockfile) {
		print STDERR "Subtree $s is currently being processed." .
						" If this is a mistake,\n";
		print STDERR "use \"rm $lockfile\" to unlock it.\n";
		next;
	}

	# lock it
	system("touch $lockfile");

	rollLogs($s);
	$logfile = "$gLogDir/$s.0";
	$args = "-base $gBase " . join(" ", @{$subtreeRef});
	system("$gInstallRoot/collector $args > $logfile 2>&1");

	# unlock it
	unlink($lockfile);

	# scan the logfile for errors and send them to stderr
	# so that cron sends them to the admin.

	if (open(L, "<$logfile")) {
		while (<L>) {
			# keep 5 lines of history
			push @hist, $_;
			shift @hist if ($#hist+1 > 5);

			# errors are marked with a * after the date:
			# 	[21-Dec-1998 15:57:54*] RRD error ...
			if (/\*\] /) {
				if ($lastWasError) {
					print STDERR $_;
				} else {
					print STDERR "\nError and the lines leading up to it:\n";
					print STDERR @hist;
				}
				$lastWasError = 1;
			} else {
				if ($skipLine) {
					print STDERR "\n";
					$skipLine = 0;
				}
				$lastWasError = 0;
			}
		}
		close(L);
	} else {
		warn("Could not scan logfile $logfile for errors: $!\n");
	}
}

sub rollLogs {
	my($logName) = @_;
	my($i) = $gKeepLogs;

	while ($i > 0) {
		rename("$gLogDir/$logName." . ($i-1),
				"$gLogDir/$logName." . $i);	
		$i--;
	}
}

exit 0;
