Rendez-vous sur Arrakiss
Le 10/02/2020 à 08:59 dans /Journal/2020/

Statistiques pour spamd - WIP

J’en ai parlé plusieurs fois ici, j’adore spamd pour embêter les spammeurs.

Je voulais depuis longtemps obtenir des statistiques afin de mieux suivre l’évolution des pièges. À ce jour, j’ai un truc qui fonctionne : spamdcharts.

J’utilise chart.js pour générer les graphiques. Ça me plait moyennement, l’idéal serait de tout tracer en SVG.

Sinon, le code est très (mais alors très) vilain : c’est du shell, c’est très lent… Toutefois, ça fait le job.

Puisque c’est “WIP” (en cours), j’envisage de :

Je pose le code ici, mais je rappelle qu’il est tout sale :

#!/bin/sh
# Author: prx <prx@ybad.name>
# licence: MIT
# Description : Generate charts about spamd(8) traps

set -e

# defaults
data=/var/spamdcharts_data 
log=/var/log/daemon
title="spamd traps stats"

# vars
fpfile=${data}/.footprint

# functions
help() {
	printf "%s\n" "usage: $0 > charts.html
	  [ -d directory where data is stored (default: ./spamdcharts_data) ]
	  [ -l spamd logs files (default: /var/log/daemon*) ]
	  [ -s relative path to stylesheet ]
	  [ -t title ]
	  [ -h help ]"

	exit
}
rand() {
	printf "%s" "$(jot -r 1 $1 $2)"
}
randcol() {
	printf "%s\n" "rgba($(rand 0 255),$(rand 0 255),$(rand 0 255), 0.3)"
}
monthtonum() {
	num=""
	case $1 in
		"Jan") num="01" ;;
		"Feb") num="02" ;;
		"Mar") num="03" ;;
		"Apr") num="04" ;;
		"May") num="05" ;;
		"Jun") num="06" ;;
		"Jul") num="07" ;;
		"Aug") num="08" ;;
		"Sep") num="09" ;;
		"Oct") num="10" ;;
		"Nov") num="11" ;;
		"Dec") num="12" ;;
	esac
	printf "%s" "${num}"
}

# parse options, override defaults
while getopts d:l:s:t:h o; do
	case "${o}" in
		d ) data="${OPTARG}" ;;
		l ) log="${OPTARG}" ;;
		s ) stylesheet="${OPTARG}" ;;
		t ) title="${OPTARG}" ;;
		h ) help ;;
	esac
done

logtodata() {
	# parse log and store data
	mkdir -p "${data}"
	y=$(date +%Y) # assume year is now
	month=""
	sec=""
	day=""
	list=""

	# last log seen at this date : month-day-time (whithout -)
	test -f "${fpfile}" && fp="$(cat ${fpfile})" || fp=0
		
	(gzcat ${log}*.gz; cat "${log}") |\
		grep "spamd.*disconnected.*lists:" |\
		tr -s " " |\
		cut -d' ' -f 1,2,3,9,12 |\
		while read -r month day t sec list; do
			month="$(monthtonum ${month})"
			day="$(printf "%02d" ${day})"

			t=$(printf "%s" "${t}" | tr -d ':')
			newfp="${y}${month}${day}${t}"

			# if line older than last run, do nothing
			test ${newfp} -gt ${fp} || continue

			fp=${newfp}
			printf "%s" "${newfp}" > "${fpfile}"

			dir="${data}/${y}/${month}/${day}"
			listfile="${dir}/${list}"

			mkdir -p "${dir}"
			# get last record if any and store sum of time wasted
			test -f "${listfile}" && sec=$((${sec} + $(cat "${listfile}")))
			printf "%s" "${sec}" > "${listfile}"
	done 
}

gencharts() {
	# this is crappy...
labels=""
points=""

cat << EOF
<!doctype html>
<html>
<head>
<title>${title}</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" 
	href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.css">
<link rel="stylesheet" type="text/css" 
	href="${stylesheet}" >
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<h1>${title}</h1>
<p>Last update : $(date +%F-%X)</p>
EOF


for year in $(ls ${data}); do
	test -d "${data}/${year}" || continue
	# line chart over year
cat << EOF
<h2>${year}</h2>
<h3>Spammers time wasted in ${year}</h3>
<div class="spamdchart">
	<canvas id="${year}linechart">
		<p>Spammers time wasted in ${year} thanks to spamd</p>
	</canvas>
</div>
<script>
var ctx = document.getElementById("${year}linechart");
var myChart = new Chart(ctx, {
	type: "line",
	data: {
EOF
	for month in $(ls "${data}/${year}"); do
		for day in $(ls "${data}/${year}/${month}"); do
			labels="${labels}\"${month}-${day}\","
			n=0
			for l in ${data}/${year}/${month}/${day}/* ; do
				n=$((${n}+$(cat ${l})))
			done
			points="${points} ${n},"
		done
	done
cat << EOF 
	labels: [${labels}],
		datasets: [{
			label: 'trap duration (s)',
			data: [ ${points} ]
		}]
	}
});
</script>
EOF

	cat << EOF
<h3>Total time wasted per blacklist</h3>
<div class="spamdchart">
	<canvas id="${year}piechart">
		<p>Spammers time wasted pie chart in ${year} thanks to spamd</p>
	</canvas>
</div>
<script>
var ctx = document.getElementById("${year}piechart");
var myChart = new Chart(ctx, {
	type: "pie",
	data: {
EOF
		
	labels=""
	points=""
	colors=""
	lists="$(find ${data} -type f ! -name ".*" |\
				awk -F'/' '{print $NF}' |\
		sort -u)"

	for l in ${lists}; do
		labels="${labels} \"${l}\","
		colors="${colors} \"$(randcol)\","
		n=0
		for s in $(find ${data} -type f -name "${l}"); do
			n=$((${n} + $(cat ${s})))
		done
		points="${points}${n},"
	done

cat << EOF
	datasets: [{
		data: [ ${points} ],
		backgroundColor: [ ${colors} ],
	}],
	labels: [ ${labels} ],
},
	options: {
		responsive:true,
	}
});
</script>
EOF

done

printf "%s\n" "</body></html>"
}

logtodata
gencharts