#!/bin/sh
#*/5  * * * * /home/bellard/bin/makegraphs >/dev/null

# RRD database directory
rrdlog="/var/lib/pangolin/rrd"

# Images directory
rrdgraph="/home/slitaz/www/pangolin/pics/rrd"

# Colors
rrdcolors="--color SHADEA#FFFFFF --color SHADEB#FFFFFF --color BACK#FFFFFF" 
rrdgraphargs="-aPNG -i -z --alt-y-grid -w 600 -h 100 -r $rrdcolors"

[ -d $rrdlog ] || mkdir -p $rrdlog
[ -d $rrdgraph ] || mkdir -p $rrdgraph

updatecpudata() {
	[ -e "$rrdlog/cpu.rrd" ] || rrdtool create $rrdlog/cpu.rrd --step=300 \
			DS:user:COUNTER:600:0:500000000 \
			DS:nice:COUNTER:600:0:500000000 \
			DS:system:COUNTER:600:0:500000000 \
			DS:idle:COUNTER:600:0:500000000 \
			DS:iowait:COUNTER:600:0:500000000 \
			DS:irq:COUNTER:600:0:500000000 \
			DS:softirq:COUNTER:600:0:500000000 \
			DS:celsius:GAUGE:600:0:100000 \
			RRA:AVERAGE:0.5:1:576  RRA:AVERAGE:0.5:6:672 \
			RRA:AVERAGE:0.5:24:732 RRA:AVERAGE:0.5:144:1460
	grep '^cpu' /proc/stat | while read cpu user nice system idle iowait irq softirq misc; do
		celsius=$(find /sys | grep /temp._input | xargs cat | \
			awk '{ if ($0 > max) max=$0 } END { print max/1 }')
		rrdtool update $rrdlog/cpu.rrd \
			-t celsius:nice:user:system:idle:iowait:irq:softirq \
			N:$celsius:$nice:$user:$system:$idle:$iowait:$irq:$softirq
		break
	done
}

updatecpugraph() {
	period=$1
	info="$(grep '^model name' /proc/cpuinfo | cut -d: -f2 \
		| sed 's/ * / /g' | awk '
{ s=$0 ; n++ }                    
END { if (n > 1) printf " %dx",n; print s }')"
	rrdtool graph "$rrdgraph/cpu-$period.png" --start -1$period \
		$rrdgraphargs -l 0 -u 100 -t "cpu usage per $period [$info ]" \
		-v " " \
		DEF:user=$rrdlog/cpu.rrd:user:AVERAGE \
		DEF:system=$rrdlog/cpu.rrd:system:AVERAGE \
		DEF:idle=$rrdlog/cpu.rrd:idle:AVERAGE \
		DEF:nice=$rrdlog/cpu.rrd:nice:AVERAGE \
		DEF:iowait=$rrdlog/cpu.rrd:iowait:AVERAGE \
		DEF:celsius=$rrdlog/cpu.rrd:celsius:AVERAGE \
		'CDEF:total=user,system,idle,nice,iowait,+,+,+,+' \
		'CDEF:userpct=100,user,total,/,*' \
		'CDEF:systempct=100,system,total,/,*' \
		'CDEF:idlepct=100,idle,total,/,*' \
		'CDEF:nicepct=100,nice,total,/,*' \
		'CDEF:iopct=100,iowait,total,/,*' \
		'CDEF:temp=celsius,1000,/' \
		'AREA:userpct#0000FF:user cpu' \
		'STACK:nicepct#C0C0FF:nice cpu' \
		'STACK:systempct#FF0000:system cpu' \
		'STACK:iopct#FFFF00:I/O wait' \
		'STACK:idlepct#00FF00:idle cpu' \
		'LINE1:temp#000000:temperature\g' \
		'GPRINT:temp:MAX:max %2.0lfC\j'
}

updatememgraph() {
	period=$1
	info="$(free | awk '\
{ \
  if (/Mem:/) { \
	if ($2 < 10000) printf "%d KB",$2; \
	else if ($2 < 10000000) printf "%d MB",$2/1024; \
	else printf "%d GB",$2/1024/1024; \
  } \
}')"
	info2="$(free | awk '\
{ \
  if (/Swap:/) { \
	if ($2 < 10000) printf "%d KB",$2; \
	else if ($2 < 10000000) printf "%d MB",$2/1024; \
	else printf "%d GB",$2/1024/1024; \
  } \
}')"
	rrdtool graph "$rrdgraph/memory-$period.png" --start -1$period \
		$rrdgraphargs -l 0 -u 100 \
		-t "memory usage per $period [ $info + $info2 swap ]" \
		-v " " \
		DEF:used=$rrdlog/mem.rrd:memused:AVERAGE \
		DEF:free=$rrdlog/mem.rrd:memfree:AVERAGE \
		DEF:shared=$rrdlog/mem.rrd:memshared:AVERAGE \
		DEF:buffer=$rrdlog/mem.rrd:membuffers:AVERAGE \
		DEF:cache=$rrdlog/mem.rrd:memcache:AVERAGE \
		DEF:swused=$rrdlog/mem.rrd:swapused:AVERAGE \
		DEF:swfree=$rrdlog/mem.rrd:swapfree:AVERAGE \
		'CDEF:total=used,free,+' \
		'CDEF:used2=used,buffer,cache,shared,+,+,-' \
		'CDEF:usedpct=100,used2,total,/,*' \
		'CDEF:sharedpct=100,shared,total,/,*' \
		'CDEF:bufferpct=100,buffer,total,/,*' \
		'CDEF:cachepct=100,cache,total,/,*' \
		'CDEF:freepct=100,free,total,/,*' \
		'CDEF:swtotal=swused,swfree,+' \
		'CDEF:swusedpct=100,swused,swtotal,/,*' \
		'AREA:usedpct#0000FF:used memory' \
		'STACK:sharedpct#FF7F00:shared memory' \
		'STACK:bufferpct#FF00FF:buffered memory' \
		'STACK:cachepct#FFFF00:cached memory' \
		'STACK:freepct#00FF00:free memory' \
		'LINE2:swusedpct#FF0000:used swap\g' \
		'GPRINT:swusedpct:MAX:%1.0lf%%\j'
}

updatememdata () {
	[ -e "$rrdlog/mem.rrd" ] ||
		rrdtool create "$rrdlog/mem.rrd" --step=300 \
			DS:memused:ABSOLUTE:600:0:5000000000 \
			DS:memfree:ABSOLUTE:600:0:5000000000 \
			DS:memshared:ABSOLUTE:600:0:5000000000 \
			DS:membuffers:ABSOLUTE:600:0:5000000000 \
			DS:memcache:ABSOLUTE:600:0:5000000000 \
			DS:swapused:ABSOLUTE:600:0:5000000000 \
			DS:swapfree:ABSOLUTE:600:0:5000000000 \
			RRA:AVERAGE:0.5:1:576  RRA:AVERAGE:0.5:6:672 \
			RRA:AVERAGE:0.5:24:732 RRA:AVERAGE:0.5:144:1460

	while read tag count unit; do
		case "$tag" in
		MemTotal:)  memtotal=$count;;
		MemFree:)   memfree=$count
			    memused=$(($memtotal - $memfree))
			    memshared=0;;
		MemShared:) memshared=$count;;
		Buffers:)   membuffers=$count;;
		Cached:)    memcache=$count;;
		SwapTotal:) swaptotal=$count;;
		SwapFree:)  swapfree=$count
			    swapused=$(( $swaptotal - $swapfree));;
		esac
	done < /proc/meminfo

	rrdtool update "$rrdlog/mem.rrd" \
		-t memused:memfree:memshared:membuffers:memcache:swapused:swapfree \
		"N:$memused:$memfree:$memshared:$membuffers:$memcache:$swapused:$swapfree"
}

getmax() {
	rrdtool fetch $rrdlog/$1.rrd AVERAGE | awk '\
BEGIN {max=0} \
/^[0-9]/ { \
   if ($2 != "nan" && $2 > max) max=$2; \
   if ($3 != "nan" && $3 > max) max=$3; \
} \
END { print max }' | sed 's/,/./'
}

updatediskgraph() {
	period=$1
	[ "$period" == "day" ] && maxdisk="$(getmax disk)"
	info=""
	[ -r $2 ] &&
	info="[ $(fdisk -l 2> /dev/null | grep "^Disk $2:" | \
		  sed "s|Disk $2: \(.*\), .*|\1|") ]"
		#--logarithmic --lower-limit 1 -v "Sectors/second" --units=si
	rrdtool graph "$rrdgraph/disk-$period.png" --start -1$period \
		$rrdgraphargs -t "disk access per $period $info" \
		-v "Sectors/second" --units=si \
		DEF:read=$rrdlog/disk.rrd:readsect:AVERAGE \
		DEF:write=$rrdlog/disk.rrd:writesect:AVERAGE \
		DEF:blk=$rrdlog/usagedisk.rrd:bhome:AVERAGE \
		DEF:ino=$rrdlog/usagedisk.rrd:ihome:AVERAGE \
		"CDEF:readpct=100,read,$maxdisk,/,*" \
		"CDEF:writepct=100,write,$maxdisk,/,*" \
		'AREA:readpct#0000FF:sectors read from disk' \
		'STACK:writepct#00FF00:sectors written to disk' \
		'LINE1:ino#FF00FF:inodes used in /home\g' \
		'GPRINT:ino:MAX:%1.0lf%%' \
		'LINE1:blk#FF0000:blocks used in /home\g' \
		'GPRINT:blk:MAX:%1.0lf%%\j'
}

updatediskdata() {
	dev=$1
	[ -e "$rrdlog/disk.rrd" ] ||
		rrdtool create "$rrdlog/disk.rrd" --step=300 \
			DS:readsect:COUNTER:600:0:5000000000 \
			DS:writesect:COUNTER:600:0:5000000000 \
			RRA:AVERAGE:0.5:1:576  RRA:AVERAGE:0.5:6:672 \
			RRA:AVERAGE:0.5:24:732 RRA:AVERAGE:0.5:144:1460
	[ -e "$rrdlog/iodisk.rrd" ] ||
		rrdtool create "$rrdlog/iodisk.rrd" --step=300 \
			DS:done:GAUGE:600:0:U  DS:err:GAUGE:600:0:U \
			DS:req:GAUGE:600:0:U \
			RRA:AVERAGE:0.5:1:576  RRA:AVERAGE:0.5:6:672 \
			RRA:AVERAGE:0.5:24:732 RRA:AVERAGE:0.5:144:1460
	[ -e "$rrdlog/usagedisk.rrd" ] ||
		rrdtool create "$rrdlog/usagedisk.rrd" --step=300 \
			DS:broot:GAUGE:600:0:U  DS:iroot:GAUGE:600:0:U \
			DS:bhome:GAUGE:600:0:U  DS:ihome:GAUGE:600:0:U \
			RRA:AVERAGE:0.5:1:576  RRA:AVERAGE:0.5:6:672 \
			RRA:AVERAGE:0.5:24:732 RRA:AVERAGE:0.5:144:1460

	while read major minor name readreq readsect writereq writesect misc; do
		[ $major = $(( 0x$(stat -c %t $dev) )) ] || continue
		[ $minor = $(( 0x$(stat -c %T $dev) )) ] || continue
		rrdtool update "$rrdlog/disk.rrd" -t readsect:writesect \
			N:$readsect:$writesect
	done < /proc/diskstats
	disk=${dev:0:8}
	dir=/sys/block/${disk#/dev/}/device
	done=$(printf "%d\n" $(cat $dir/iodone_cnt 2> /dev/null) )
	err=$(printf "%d\n" $(cat $dir/ioerr_cnt 2> /dev/null) )
	req=$(printf "%d\n" $(cat $dir/iorequest_cnt 2> /dev/null) )
	rrdtool update "$rrdlog/iodisk.rrd" -t done:err:req N:$done:$err:$req
	iroot=$(df -i / | sed '$!d;s/.* \([0-9]*\)% \/.*/\1/')
	broot=$(df / | sed '$!d;s/.* \([0-9]*\)% \/.*/\1/')
	ihome=$(df -i /home | sed '$!d;s/.* \([0-9]*\)% \/.*/\1/')
	bhome=$(df /home | sed '$!d;s/.* \([0-9]*\)% \/.*/\1/')
	rrdtool update "$rrdlog/usagedisk.rrd" -t broot:iroot:bhome:ihome N:$broot:$iroot:$bhome:$ihome
}

updateifgraph() {
	interface=$1
	period=$2
	rrdtool graph "$rrdgraph/$interface-$period.png" --start -1$period \
		$rrdgraphargs -t "traffic on $interface graph per $period" \
		--logarithmic -A -v "Bytes/second" --units=si \
		DEF:incoming=$rrdlog/$interface.rrd:incoming:AVERAGE \
		DEF:outgoing=$rrdlog/$interface.rrd:outgoing:AVERAGE \
		DEF:tcp=$rrdlog/proto-$interface.rrd:tcp:AVERAGE \
		'AREA:incoming#00FF00:incoming traffic\g' \
		'GPRINT:incoming:MAX:max%8.3lf %sBps' \
		'LINE1:outgoing#0000FF:outgoing traffic\g' \
		'GPRINT:outgoing:MAX:max%8.3lf %sBps' \
		'LINE1:tcp#000000:connections\g' \
		'GPRINT:tcp:MAX:max %2.0lf\j'
}

netframes() {
ifconfig $1 | grep "$2 packets" | sed -re "s/.*$3:([0-9]+).*/\1/g"
}

netstats() {
ifconfig $1 | grep bytes | sed -re "s/.*$2 bytes:([0-9]+).*/\1/g"
}

netproto()
{
	proto=${1:-tcp}
	if [ -n "$2" ]; then
		netstat -an 2> /dev/null | grep -v '0.0.0.0:*' | grep "^$proto"| grep ":$2 " | wc -l
	else
		netstat -an 2> /dev/null | grep -v '0.0.0.0:*' | grep "^$proto"| wc -l
	fi	
}

updateifdata() {
	interface=$1
	[ -e "$rrdlog/$interface.rrd" ] ||
		rrdtool create "$rrdlog/$interface.rrd" --step=300 \
			DS:incoming:COUNTER:600:0:U \
			DS:outgoing:COUNTER:600:0:U \
			RRA:AVERAGE:0.5:1:576  RRA:AVERAGE:0.5:6:672 \
			RRA:AVERAGE:0.5:24:732 RRA:AVERAGE:0.5:144:1460
	[ -e "$rrdlog/packets-$interface.rrd" ] ||
		rrdtool create "$rrdlog/packets-$interface.rrd" --step=300 \
			DS:in:COUNTER:600:0:U      DS:out:COUNTER:600:0:U \
			DS:inerr:COUNTER:600:0:U   DS:outerr:COUNTER:600:0:U \
			DS:indrop:COUNTER:600:0:U  DS:outdrop:COUNTER:600:0:U \
			DS:inov:COUNTER:600:0:U    DS:outov:COUNTER:600:0:U \
			DS:frame:COUNTER:600:0:U   DS:carrier:COUNTER:600:0:U \
			RRA:AVERAGE:0.5:1:576  RRA:AVERAGE:0.5:6:672 \
			RRA:AVERAGE:0.5:24:732 RRA:AVERAGE:0.5:144:1460
	[ -e "$rrdlog/proto-$interface.rrd" ] ||
		rrdtool create "$rrdlog/proto-$interface.rrd" --step=300 \
			DS:tcp:GAUGE:600:0:U     DS:udp:GAUGE:600:0:U \
			DS:rsync:GAUGE:600:0:U   DS:www:GAUGE:600:0:U \
			DS:ssh:GAUGE:600:0:U \
			RRA:AVERAGE:0.5:1:576  RRA:AVERAGE:0.5:6:672 \
			RRA:AVERAGE:0.5:24:732 RRA:AVERAGE:0.5:144:1460
	rx=$(netstats $interface RX)
	tx=$(netstats $interface TX)
	rrdtool update "$rrdlog/$interface.rrd" -t incoming:outgoing \
		N:${rx:-U}:${tx:-U}
	rx=$(netframes $interface RX packets)
	tx=$(netframes $interface TX packets)
	rxerr=$(netframes $interface RX errors)
	txerr=$(netframes $interface TX errors)
	rxdrop=$(netframes $interface RX dropped)
	txdrop=$(netframes $interface TX dropped)
	rxov=$(netframes $interface RX overruns)
	txov=$(netframes $interface TX overruns)
	frame=$(netframes $interface RX frame)
	carrier=$(netframes $interface TX carrier)
	rrdtool update "$rrdlog/packets-$interface.rrd" \
		-t in:out:inerr:outerr:indrop:outdrop:inov:outov:frame:carrier \
		N:${rx:-U}:${tx:-U}:${rxerr:-U}:${txerr:-U}:${rxdrop:-U}:${txdrop:-U}:${rxov:-U}:${txov:-U}:${frame:-U}:${carrier:-U}
	rsync=$(netproto tcp 873)
	www=$(netproto tcp 80)
	ssh=$(netproto tcp 22)
	tcp=$(netproto tcp)
	udp=$(netproto udp)
	rrdtool update "$rrdlog/proto-$interface.rrd" \
		-t tcp:udp:rsync:www:ssh \
		N:${tcp:-U}:${udp:-U}:${rsync:-U}:${www:-U}:${ssh:-U}
}

getdisk()
{
	local d
	local i
	d=$(stat -c %04D $1)
	for i in /dev/* ; do 
		[ $(stat -c "%02t%02T" $i) == $d ] || continue
		echo $i
		break
	done
}

###
### System graphs
###

updatecpudata
updatecpugraph day
updatecpugraph week
updatecpugraph month
updatecpugraph year

updatememdata
updatememgraph day
updatememgraph week
updatememgraph month
updatememgraph year

if [ -e /proc/diskstats ]; then
	disk=$(getdisk $0)
	updatediskdata $disk
	updatediskgraph day ${disk:0:8}
	updatediskgraph week ${disk:0:8}
	updatediskgraph month ${disk:0:8}
	updatediskgraph year ${disk:0:8}
fi

iface=$(/sbin/route -n | awk '{ if (/^0.0.0.0/) print $8 }')
updateifdata $iface
updateifgraph $iface day
updateifgraph $iface week
updateifgraph $iface month
updateifgraph $iface year

[ ! -s $rrdgraph/boot.html -o /var/log/boot.log -nt $rrdgraph/boot.html ] &&
cat > $rrdgraph/boot.html <<EOT
<html>
<body>
$(stat -c %y /var/log/dmesg.log | sed 's/\.0*//')
<span style="color: blue"><i>$(cat /proc/cmdline)</i></span>
<pre>
$(cat /var/log/dmesg.log /var/log/boot.log | \
sed	-e 's/</\&lt;/g;s/>/\&gt;/g' -e 's/.*\]R//' -e 's/.*\[?8h//' \
	-e 's|.\[1m|<b>|' -e 's|.\[0m|</b>|' -e 's|.\[[0-9][0-9Gm;]*||g' \
	-e ':a;s/^\(.\{1,68\}\)\(\[ [A-Za-z]* \]\)/\1 \2/;ta' \
	-e 's#\[ OK \]#[ <span style="color: green">OK</span> ]#' \
	-e 's#\[ Failed \]#[ <span style="color: red">Failed</span> ]#' \
	-e 's|No such .*|<span style="color: red">&</span>|' \
	-e 's|ERROR .*|<span style="color: red">&</span>|' \
	-e 's|command line: \(.*\)|command line: <span style="color: blue">\1</span>|' \
)
</pre>
</body>
</html>
EOT
