#!/bin/bash

# mrtg-data, based on a perl script by Nemo from 1999.
# Copyright (c) 2003 Aaron Haviland <orion@parsed.net>
# http://parsed.net/mrtg-data/
#
# 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., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307  USA

# Version 0.1
# First release

# 0.1.1
# - fixed bug where the interface stats weren't being read properly.
#   unknown cause.
# - subtract buffers from used memory. don't subtract cached.

# 0.1.2 - 20031109
# - some more bashism speedups added

# 0.1.3 - 20070729
# - added support for /proc/sys/kernel/random

# We need these for everything. Get them out of the way.
UPTIME=$(< /proc/uptime)
UPTIME=${UPTIME% *}
# /60? i don't know.

HOSTNAME=$(< /proc/sys/kernel/hostname)

VERSION=0.1.2
function showhelp () {
	cat <<-EOF
$(basename $0) v$VERSION - addon utility for MRTG

Syntax: $(basename $0) [family] [options] [device]

Where family is one of:
 inet - network activity
 disk - space usage
 load - system load average
 mem  - memory and swap usage
 activity - disk activity
 io - io_status (linode only)
 entropy - random number entropy pool usage
 
Examples of devices are:
 Family   Devices
 inet     eth0 (default), ppp0, lo
 disk     total (default), /, /usr/local/, /tmp
           (specify the mountpoint of the partition)
 load     n/a
 mem      n/a
 activity 98 (ubd is device major 98)
 io	  n/a
 entropy  n/a

Options:
 Family Options
 inet   -p     Show packets instead of bytes.

EOF
exit 0;
}

# we can use linux/netdevice.h
# struct net_device_stats
# unsigned long rx_bytes, rx_packets, tx_bytes, tx_packets

#alternately, see the source for ifconfig (in net-tools)

function inet () {
	if [[ "${1}" = "-p" ]]; then
		DOPACKETS=1
		shift
	else
		DOPACKETS=0
	fi
		
	# if no interface is specified, use eth0
	if [[ "${1}z" = "z" ]]; then
		IFACE="eth0"
	else
		IFACE="${1}"
	fi
	TMP="$(grep "$IFACE:" /proc/net/dev)"
	
	declare -a NETDEV="(${TMP#*:})"
	BYTESIN=${NETDEV[0]}
	PACKETSIN=${NETDEV[1]}
	BYTESOUT=${NETDEV[8]}
	PACKETSOUT=${NETDEV[9]}
	
	if [[ $DOPACKETS -eq 1 ]]; then
		echo $PACKETSIN
		echo $PACKETSOUT
	else
		echo $BYTESIN
		echo $BYTESOUT
	fi
}

function mem () {
	KERNVER=$(uname -r)
	case $KERNVER in
		2.2*|2.4*)
			declare -a MEM="($(grep "^Mem:" /proc/meminfo))"
			declare -a SWAP="($(grep "^Swap:" /proc/meminfo))"
			echo "$[ ${MEM[2]} - ${MEM[5]}]"
			echo "${SWAP[2]}"
			;;
		2.6*)
			declare -a MEMTOT="($(grep "^MemTotal:" /proc/meminfo))"
			declare -a MEMFREE="($(grep "^MemFree:" /proc/meminfo))"
			declare -a SWAPTOT="($(grep "^SwapTotal:" /proc/meminfo))"
			declare -a SWAPFREE="($(grep "^SwapFree:" /proc/meminfo))"

			echo "$[ ( ${MEMTOT[1]} - ${MEMFREE[1]} ) * 1024 ]"
			echo "$[ ( ${SWAPTOT[1]} - ${SWAPFREE[1]} ) * 1024 ]"
			;;
		esac
}

function io (){
	declare -a IO="($(< /proc/io_status))"
	TMP=${IO[2]#*=}
	echo $(($TMP / 1000))
	echo ${IO[3]#*=}
}

function entropy () {
	declare -a ENTROPY="($(< /proc/sys/kernel/random/entropy_avail))"
	declare -a POOLSIZE="($(< /proc/sys/kernel/random/poolsize))"
	echo $ENTROPY
	echo $POOLSIZE
}

function load () {
	declare -a LOADAVG="($(< /proc/loadavg))"
	echo ${LOADAVG[0]/./}
	echo ${LOADAVG[3]/*\//}
}

# FIXME
# i'm sorry. devfs messes us up. if the path to your partition is too
# long, df wraps.
 
function disk () {
	if [[ "${1}" = "total" || "${1}z" = "z" ]] ; then
		df -k -t ext3 -t ext2 -t vfat -t reiserfs | \
		while read -a i; do
			if [[ "${i[0]}" != "Filesystem" ]]; then
				total=$[ $total + ${i[1]} ]
				used=$[ $used + ${i[2]} ]
				echo $used
				echo $total
			fi
		done | tail -n 2
	else
		declare -a i="($(df -k $1 | tail -n1))"
		echo ${i[2]}
		echo ${i[3]}
	fi
}

# on 2.2, we use /proc/stat
# disk_rio

# on 2.4, we use /proc/stat
# disk_io (major,minor):(read+write-requests,read-requests,read-sectors,write-requests,write-sectors)

# on 2.6, we can use /proc/diskstats
# major minor device reads 0 0 0 writes 0 0 0 0 0 0
# or /sys/block/device/[partition/]stat
# device:
# reads 0 0 0 writes 0 0 0 0 0 0
# partition:
# reads 0 writes 0
# sysfs is slightly faster (.004-.005s vs .004-.010s) and wouldn't
# require an external grep. we prefer that.

function activity () {
	KERNVER=$(uname -r)
	case $KERNVER in
		2.2.*)
			echo "Linux 2.2 support isn't written at this time"
			;;
		2.4.*)
			TMP=$(grep "($1," /proc/stat)
			TMP=${TMP##*(}
			declare -a ACTIVITY="(${TMP//,/ })"
			echo "${ACTIVITY[1]}"
			echo "${ACTIVITY[3]}"
			;;
		2.5.*|2.6.*)
			if [[ -d /sys/block ]]; then
				TMP1="/sys/block/$1/stat"
				TMP2="/sys/block/${1:0:$[${#1} - 1 ]}/$1/stat"
				if [[ -e $TMP1 ]]; then
					declare -a j="($(< $TMP1))"
					echo ${j[0]}
					echo ${j[4]}
				elif [[ -e $TMP2 ]]; then
					declare -a j="($(< $TMP2))"
					echo ${j[0]}
					echo ${j[2]}
				fi
			else
				declare -a TMP="($(grep $1 /proc/diskstats | head -n1))"
				if [[ "${1/[0-9]/}" = "$1" ]]; then
					# we're dealing with the drive
					echo ${TMP[3]}
					echo ${TMP[7]}
				else
					# we're dealing with a partition
					echo ${TMP[3]}
					echo ${TMP[5]}
				fi	
			fi
			;;
	esac
}
	

if [[ "${1}z" = "z" ]]; then
	showhelp
else
	case $1 in
		inet)
			inet $2 $3
			;;
		disk)
			disk $2
			;;
		mem)
			mem
			;;
		load)
			load
			;;
		activity)
			activity $2
			;;
		io)
			io
			;;
		entropy)
			entropy
			;;
		uptime)
			echo $[ ${UPTIME%.*} / 60 ]
			echo 0
			;;
		*)
			showhelp
			;;
	esac
fi
echo $UPTIME
echo $HOSTNAME
exit 0;
