#!/usr/bin/perl -w
#
#  $Id: logstats,v 1.3 2002/07/11 20:56:52 dgregor Exp $
#

$wait = 60;
$ignore_if = 'lo\d*|vmnet\d+|sit\d+';

$uname = `uname`;
chomp($uname);

open(VMSTAT, "vmstat $wait|") ||
	die "$0: could not open vmstat: $!\n";

$skip_first_vmstat = 0;

while (<VMSTAT>) {
	$time = time();

	chomp();
	s/^\s+//;
	s/\s+$//;
	
	@a = split(/\s+/, $_);

	if ($a[0] eq "procs") {
		next;
	} elsif ($a[0] eq "r") {
		@vmstat_fmt = @a;
		next;
	} else {
		if (!$skip_first_vmstat) {
			$skip_first_vmstat++;
			next;
		}
		foreach $i (0 .. $#a) {
			$vmstat_data{$vmstat_fmt[$i]} = $a[$i];
		}
	}

	if ($uname =~ m/^OpenBSD/) {
		open(VMSTAT_F, "vmstat -f|") ||
			die "$0: could not open vmstat -f: $!\n";
	
		while (<VMSTAT_F>) {
			if (m/^(\d+) forks/) {
				$vmstat_f_data{'forks'} = $1;
			} elsif (m/^(\d+) vforks/) {
			       $vmstat_f_data{'vforks'} = $1;
			}
		}

		close(VMSTAT_F);
	} elsif ($uname =~ m/^SunOS/) {
		open(VMSTAT_S, "vmstat -s|") ||
			die "$0: could not open vmstat -s: $!\n";
	
		while (<VMSTAT_S>) {
			if (m/^\s*(\d+)\s+forks/) {
				$vmstat_s_data{'forks'} = $1;
			} elsif (m/^\s*(\d+)\s+vforks/) {
			       $vmstat_s_data{'vforks'} = $1;
			}
		}

		close(VMSTAT_S);
	}

	open(UPTIME, "uptime|") ||
		die "$0: could not open uptime: $!\n";

	while (<UPTIME>) {
		chomp();

		if (!m/
			up\s+(.*?),
			\s+(\d+)\s+users?,
			\s+load\s+averages?:
				\s+([0-9.]+),\s+([0-9.]+),\s+([0-9.]+)
			$
		/x) {
			die "could not parse uptime: \"$_\"\n";
		}

		$uptime_data{'users'} = $2;
		$uptime_data{'la_1'} = $3;
		$uptime_data{'la_5'} = $4;
		$uptime_data{'la_15'} = $5;
	}

	close(UPTIME);

	if ($uname =~ m/^SunOS/) {
		open(PSRINFO, "/usr/sbin/psrinfo|") ||
			die "$0: could not open psrinfo: $!\n";
	
		$cpus{'online'} = $cpus{'offline'} = 0;
		while (<PSRINFO>) {
			if (m/on-line/) {
				$cpus{'online'}++;
			} else {
				$cpus{'offline'}++;
			}
		}

		close(PSRINFO);
	} elsif ($uname =~ m/^Linux/ && (-r "/proc/cpuinfo")) {
		open(CPUINFO, "</proc/cpuinfo") ||
			die "$0: could not open /proc/cpuinfo: $!\n";

		$cpus{'online'} = $cpus{'offline'} = 0;
		while (<CPUINFO>) {
			if (m/^processor/) {
				$cpus{'online'}++;
			}
		}
		close(CPUINFO);
	} else {
		$cpus{'online'} = 1;
		$cpus{'offline'} = 0;
	}

	undef(%netstat_data);

	if ($uname =~ m/^(OpenBSD|SunOS)/) {
		$command = "netstat -niv";
		if ($uname =~ m/^OpenBSD/) {
			$command .= "q";
		}

		open(NETSTAT, $command . "|") ||
			die "$0: could not open $command: $!\n";

		while (<NETSTAT>) {
			chomp();

			if (m/^\s+$/) {
				next;
			}

			@stuff = split(/\s+/, $_);

			if (m/^Name/) {
				@headers = @stuff;
				if ($headers[2] eq "Net/Dest") {
					$headers[2] = "Network";
				}
				if ($headers[8] eq "Collis") {
					$headers[8] = "Colls";
				}
				next;
			}

			while (@stuff < @headers) {
				splice(@stuff, 3, 0, "");
			}

			if (!exists($netstat_data{$stuff[0]}) ||
			    $netstat_data{$stuff[0]}{'Name'} eq "<Link>") {
				for ($a = 0; $a < @headers; $a++) {
					$netstat_data{$stuff[0]}
					    {$headers[$a]} = $stuff[$a];
				}
			}
		}

		close(NETSTAT);
	} elsif ($uname =~ m/^Linux/) {
		open(IFCONFIG_A, "ifconfig -a|") ||
			die "$0: could not open ifconfig -a: $!\n";

		undef($iface);
	
		while (<IFCONFIG_A>) {
			if (m/^(\S+)/) {
				$iface = $1;
				$netstat_data{$iface}{'Name'} = $iface;
				next;
			}

			if (m/(RX|TX) packets:\s*(\d+)\s+errors:\s*(\d+)\s+dropped:\s*(\d+)\s+overruns:\s*(\d+)\s+(frame|carrier):\s*(\d+)/) {
				$dir = $1;
				$packets = $2;
				$errors = $2;
				$dropped = $2;
				$overruns = $2;
				$frame_carrier = $2;
				if ($dir eq "RX") {
					$dir = "I";
				} else {
					$dir = "O";
				}
				$netstat_data{$iface}{$dir . "pkts"} = $packets;
				$netstat_data{$iface}{$dir . "errs"} = $errors;
				$netstat_data{$iface}{$dir . "drop"} = $dropped;
				$netstat_data{$iface}{$dir . "orun"} =
					$overruns;
				$netstat_data{$iface}{($dir eq "I") ?
				    "frame" : "carrier"} = $frame_carrier;
			}
			if (m/collisions:\s*(\d+)\s+txqueuelen:\s*(\d+)/) {
				$collisions = $1;
				$txqueuelen = $2;
				$netstat_data{$iface}{"Colls"} = $collisions;
				$netstat_data{$iface}{"Queue"} = $txqueuelen;
			}
		}

		close(IFCONFIG_A);
	}

	if (defined(%vmstat_data)) {
		foreach $i (qw/procs_r procs_b procs_w page_sr faults_in faults_sy faults_cs cpu_us cpu_sy cpu_id/) {
			$i =~ m/_(.*)$/;
			if (exists($vmstat_data{$1})) {
				$stats{$i} = $vmstat_data{$1};
			}
		}
	}

	if (defined(%vmstat_f_data)) {
		foreach $i (qw/forks vforks/) {
			if (exists($vmstat_f_data{$i})) {
				$stats{$i} = $vmstat_f_data{$i};
			}
		}
	}

	if (defined(%vmstat_s_data)) {
		foreach $i (qw/forks vforks/) {
			if (exists($vmstat_s_data{$i})) {
				$stats{$i} = $vmstat_s_data{$i};
			}
		}
	}

	if (defined(%uptime_data)) {
		foreach $i (qw/uptime users la_1 la_5 la_15/) {
			if (exists($uptime_data{$i})) {
				$stats{$i} = $uptime_data{$i};
			}
		}
	}

	if (defined(%netstat_data)) {
		foreach $i (keys(%netstat_data)) {
			if ($i =~ m/^($ignore_if)$/) {
				next;
			}
			foreach $j (qw/Mtu Network Address Ipkts Ierrs Idrop Iorun Opkts Oerrs Odrop Oorun Colls Queue carrier frame/) {
				if (!exists($netstat_data{$i}{$j})) {
					next;
				}
				if ($j eq "Name") {
					next;
				}
				if ($netstat_data{$i}{$j} eq "") {
					next;
				}
				$stats{"if_" . $i . "_" . $j} =
					$netstat_data{$i}{$j};
			}
		}
	}

	push(@output, "time", $time);
	foreach $i (sort(keys(%stats))) {
		push(@output, $i, $stats{$i});
	}

	push(@output, "cpus_online", $cpus{'online'});
	push(@output, "cpus_offline", $cpus{'offline'});

	print(join(" ", @output), "\n");

	undef(%vmstat_data);
	undef(%vmstat_f_data);
	undef(%vmstat_s_data);
	undef(%uptime_data);

	undef(%stats);
	undef(@output);
}
