#!/usr/bin/perl -w

# Copyright 1999, 2000, 2001 (c) Thomas Erskine <thomas.erskine@sourceworks.com>
# See the COPYRIGHT file with the distribution.

# updater - the remstats database updater
# $Id: updater.pl,v 1.9 2001/08/28 15:22:24 remstats Exp $

# - - -   Configuration   - - -

use strict;

# What is this program called, for error-messages and file-names
$main::prog = 'updater';
# Where is the configuration dir
$main::config_dir = '/etc/remstats/config';

# - - -   Version History   - - -

(undef, $main::version) = split(' ', '$Revision: 1.9 $');

# - - -   Setup   - - -

use lib '.', '/usr/lib/remstats/lib', '/usr/lib/perl5/';
require "remstats.pl";
require "private.pl";
use RRDs;
use Getopt::Std;

# Parse the command-line
my %opt = ();
getopts('d:f:h', \%opt);

if (defined $opt{'h'}) { &usage; } # no return
if (defined $opt{'d'}) { $main::debug = $opt{'d'}; } else { $main::debug = 0; }
if (defined $opt{'f'}) { $main::config_dir = $opt{'f'}; }

if ($#ARGV != 0) { &usage; }
$main::collector = shift @ARGV;

&read_config_dir($main::config_dir, 'general', 'oids', 
	'rrds', 'groups', 'host-templates', 'hosts');

# - - -   Mainline   - - -

# collect all the data into a hash, for easier access
my ($host, $timestamp, $var, $value, %value);
while (<STDIN>) {
	chomp;
	($host, $timestamp, $var, $value) = split(' ',$_,4);
	unless (defined $value and defined $var and defined $timestamp and defined $host) {
		&debug("garbage input: $_") if ($main::debug);
		next;
	}
	$host = lc $host;
	$var = lc $var;
	$value{$host}{$var}{TIME} = $timestamp;
	$value{$host}{$var}{VALUE} = $value;
	&debug("RAW: host=$host, time=$timestamp, var=$var, value=$value") if ($main::debug);
}

# Do the updates
my ($realrrd, $ip, $wildrrd, $wildpart, $extra, $update, $alias,
	$found_data, $sub_collector, $rrdfile, $fixedrrd, $data, 
	$error, $function, $newvalue);

my ($lc_host, $lc_alias);
foreach $host (keys %{$main::config{HOST}}) {
	$ip = &get_ip($host);
	$lc_host = lc $host;
	&debug("doing updates for $host") if ($main::debug);
	foreach $realrrd (@{$main::config{HOST}{$host}{RRDS}}) {
		($wildrrd, $wildpart, $extra, $fixedrrd) = &get_rrd($realrrd);
		if (defined $main::config{COLLECTOR}{$main::collector}{$wildrrd}) {
			# it's collected by this collector
		}
		elsif ($main::config{RRD}{$wildrrd}{SOURCE} !~ /^${main::collector}=(\S+)/i){
			&debug("  $wildrrd isn't collected by $main::collector; skipped") if ($main::debug);
			next;
		}
		&debug("  rrd $realrrd") if ($main::debug);

		next unless (defined $wildrrd);
		$rrdfile = $main::config{DATADIR}.'/'.$host.'/'.$fixedrrd.'.rrd';
		unless (-f $rrdfile) {
			&error("missing RRD $rrdfile; skipped update");
			next;
		}

# Build an update packet
		undef $timestamp;
		$update = '';
		$found_data = 0;

		foreach $data (@{$main::config{RRD}{$wildrrd}{DATANAMES}}) {
			$alias = $main::config{RRD}{$wildrrd}{$data}{ALIAS};
			unless (defined $alias) {
				&error("no name defined for $realrrd($wildrrd) $data; skipped");
				next;
			}
			$alias =~ s/\*/$wildpart/ if (defined $wildpart);

# Allow configuration-specified function-calls in the alias
			if ($alias =~ /^\&([a-zA-Z_]+)\(([^\)]+)\)$/o) {
				$function = $1;
				$alias = $2;
			}
			else { undef $function; }
			$lc_alias = lc $alias;

# All values for a given RRD go in with the same timestamp
			if (defined $value{$lc_host}{$lc_alias}) {
				if (defined $function) {
					$newvalue = eval '&'. $function .'(\''.
						$value{$lc_host}{$lc_alias}{VALUE} .'\')';
					&debug("function $function: " .
						"in=$value{$lc_host}{$lc_alias}{VALUE} " .
						"out=$newvalue")
						if ($main::debug>1);
					$value{$lc_host}{$lc_alias}{VALUE} = $newvalue;
				}
				$update .= ':'. $value{$lc_host}{$lc_alias}{VALUE};
				$timestamp = $value{$lc_host}{$lc_alias}{TIME};
				$found_data = 1;
			}
			else {
				$update .= ':U';
			}

		}

# Put the data in
		if ($found_data) {
			$update = $timestamp . $update;
			&debug("  update packet: $update") if ($main::debug);
			RRDs::update $rrdfile, $update;
			$error = RRDs::error();
			$update =~ tr/\000/:/;
			if ($error) {
				&error("update for $rrdfile failed with $update");
			}
			else {
				&debug("  updated $rrdfile with $update") if ($main::debug);
			}
		}
		else {
			&debug("  no data for $realrrd on $host; skipped update") if ($main::debug);
		}
	}
}

exit 0;

#----------------------------------------------------------------- usage ---
sub usage {
	print STDERR <<"EOD_USAGE";
$main::prog version $main::version
usage: $main::prog [options] collector
where options are:
	-d nnn	enable debugging output at level 'nnn'
	-f fff	use 'fff' for config-dir [$main::config_dir]
	-h	show this help
EOD_USAGE
	exit 0;
}

#----------------------------------------------------------------- debug ---
sub debug {
	my $msg = join('', @_);
	print STDERR "DEBUG: $msg\n";
}

#----------------------------------------------------------------- abort ---
sub abort {
	my $msg = join('', @_);
	print STDERR "$main::prog: ABORT: $msg\n";
	exit 1;
}

#---------------------------------------------------- error ---
sub error {
	my $msg = join('', @_);
	print STDERR "$main::prog: ERROR $msg\n";
}
