#!/usr/bin/perl -w

use strict;

# What is this program called, for error-messages and file-names
$main::prog = 'program-collector';
# Which collector is this
$main::collector = 'program';
# Where is the default configuration dir
$main::config_dir = '/etc/remstats/config';
# What to use for a path if there is none.  If run-remstats2 is being
# given an empty path or incorrect path, you should modify config/environment
# to supply the correct one.
$main::default_path = '/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin';

# - - -   Version History   - - -

$main::version = (split(' ', '$Revision: 1.5 $'))[1];

# - - -   Setup   - - -

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

# Parse the command-line
&parse_command_line();

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

%main::uphosts = &get_uphosts if ($main::use_uphosts);

# Make sure that we haven't been stopped on purpose
exit 0 if( &check_stop_file());

@main::hosts = &select_hosts( \@main::hosts, \@main::groups, \@main::keys);

# Make sure we've got a path
unless( defined $ENV{PATH}) {
	&error("No path? Useing default.");
	$ENV{PATH} = $main::default_path;
}

# - - -   Mainline   - - -

my ($host, $ip, $realrrd, $wildrrd, $fixedrrd, $executable, $now,
	$start_time, $run_time);

$start_time = time();
$main::entries_collected = $main::entries_used = $main::requests = 0;
my $tmpfile = $main::config{DATADIR} .'/LAST/'. $main::collector .'.'. $$;
my $lastfile = $main::config{DATADIR} .'/LAST/'. $main::collector;
open (TMP, ">$tmpfile") or &abort("can't open $tmpfile: $!");

foreach $host (@main::hosts) {
	next unless( &host_collected_by( $host, $main::collector));
	next if ($host eq '_remstats_');

	# Ignore this host if it's down and using uphosts file
	if ($main::use_uphosts and not defined $main::uphosts{$host}) {
		&debug("$host is down(uphosts); skipped") if ($main::debug);
		next;
	}
	$ip = &get_ip($host);
	unless (defined $ip) {
		&debug("no IP number for $host; skipped") if( $main::debug);
		next;
	}

	&debug("doing host $host") if ($main::debug);

	# Collect the info
	foreach $realrrd (@{$main::config{HOST}{$host}{RRDS}}) {
		($wildrrd, undef, $fixedrrd) = &get_rrd($realrrd);
		next unless( &rrd_collected_by( $wildrrd, $main::collector));

		# Check whether it's at all time to collect data
		unless ($main::force_collection or
				&check_collect_time( $host, $wildrrd, $fixedrrd)) {
			&debug("  not time yet for $realrrd($wildrrd): skipped")
				if ($main::debug>1);
			next;
		}

		# Collect it
		&collect_rrd($host, $ip, $realrrd, $wildrrd);
	}
}

# Now remstats instrumentation info
$now = time();
$run_time = $now - $start_time;
print <<"EOD_INSTRUMENTATION";
_remstats_ $now ${main::collector}-collector:requests $main::requests
_remstats_ $now ${main::collector}-collector:collected $main::entries_collected
_remstats_ $now ${main::collector}-collector:used $main::entries_used
_remstats_ $now ${main::collector}-collector:runtime $run_time
EOD_INSTRUMENTATION

close(TMP) or &abort("can't open $tmpfile: $!");
rename $tmpfile, $lastfile or &abort("can't rename $tmpfile to $lastfile: $!");

exit 0;

#------------------------------------------------------- collect_rrd ---
sub collect_rrd {
	my ($host, $ip, $realrrd, $wildrrd) = @_;
	my ($start_time, $now, $value, $executable, $extra, $name, $rows, $command);

	# Now parse the extra field.  There may be an optional description
	# DESC="ddd" and the required part is the command to be run.
	$extra = $main::config{HOST}{$host}{EXTRA}{$realrrd};

	# XXX This shouldn't be necessary since it's an unused feature (having
	# descriptions on RRDs).  And nothing shows them anywhere.

	# Remove any description
	$extra =~ s/\s*desc=('[^']+'|"[^"]+")\s*//i;

	# Whatever is left is the command
	$command = $extra;

	# Make sure we have an executable executable :-)
	($executable) = split(' ', $command);
	$executable =~ s/^\s*//; # trim leading ...
	$executable =~ s/\s*$//; # and trailing white-space
	&find_executable_in_path( $executable) or next;

	# Get the data
	$start_time = time();
	open(PIPE,"$command|") or do {
		&error("can't open pipe from '$command' for $host:$realrrd: $!");
		return undef;
	};
	$rows = 0;
	while( defined( $value = <PIPE>)) {
		chomp $value;
		next if( $value =~ /^\s*$/); # ignore blank lines

		# Pull out the name, if there is one, or fake one if there isn't
		($name, $value) = split(' ', $value, 2);
		if( defined $name && ! defined $value) {
			$value = $name;
			if( ++$rows > 1) {
				$name = $realrrd . '-' . $rows;
			}
			else { $name = $realrrd; }
		}

		# Here are your values
		$now = time();
		print "$host $now $name $value\n";
		print TMP "$host $now $name $value\n";
		print "$host $now $name-response ", ($now - $start_time), "\n";
		print TMP "$host $now $name-response ", ($now - $start_time), "\n";
	
		++$main::requests;
		++$main::entries_collected;
		++$main::entries_used;
	}
	close(PIPE);
}

#----------------------------------------------------------------- debug ---
sub debug {
	print STDERR 'DEBUG: ', @_, "\n";
}

#------------------------------------------------------------------ abort ---
sub abort {
	print STDERR 'ABORT: ', @_, "\n";
	exit 1;
}

#------------------------------------------------------------------ error ---
sub error {
	print STDERR 'ERROR: ', @_, "\n";
}

#----------------------------------------------------------------- usage ---
sub usage {
	print STDERR <<"EOD_USAGE";
$main::prog version $main::version from remstats 1.0.9b
usage: $0 [options]
where options are:
    -d nnn  enable debugging output at level 'nnn'
    -f fff  use 'fff' for config-dir [$main::config_dir]
    -F      force collection even if it is not time
   	-G GGG  only try hosts from group 'GGG', a comma-separated list
    -h      show this help
    -H HHH  only try hosts from 'HHH', a comma-separated list
    -K KKK  only try hosts with key(s) 'KKK', a comma-separated list
    -u      ignore uphosts file
EOD_USAGE
	exit 0;
}

#------------------------------------------------------ parse_command_line ---
sub parse_command_line {
	my %opt = ();
	getopts('d:f:FG:hH:K:u', \%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( defined $opt{'F'}) { $main::force_collection = 1; }
	else { $main::force_collection = 0; }
	if( defined $opt{'G'}) { @main::groups = split(',', $opt{'G'}); }
	if( defined $opt{'H'}) { @main::hosts = split(',', $opt{'H'}); }
	if( defined $opt{'K'}) { @main::keys = split(',', $opt{'K'}); }
	if( defined $opt{'u'}) { $main::use_uphosts = 0; }
	else { $main::use_uphosts = 1; }

	# No buffering when debugging
	if ($main::debug) { $| = 1; }

}

#------------------------------------------------- find_executable_in_path ---
sub find_executable_in_path {
	my $executable = shift @_;
	my( $file, $dir);

	# Check the cache of names
	if( defined $main::which{$executable}) {
		return $main::which{$executable};
	}
	
	# It's already a /full/path/name?
	elsif( -x $executable) {
		$main::which{$executable} = $executable;
		return $executable;
	}

	# Search the path for it
	for $dir (split(':', $ENV{PATH})) {
		$file = $dir . '/' . $executable;
		if( -x $file) {
			$main::which{$executable} = $file;
			return $file;
			next;
		}
	}

	# It's not there.  Complain.
	&error("executable $executable is not in path or is not executable");
	return undef;

}
