#! /bin/sh -e # graph collected DCC statistics in rrdtool files. # [-x] debugging # [-q] quiet # [-B] make big graphs # [-G db] make graph of database size # [-G db-min] make graph of database size without maximum size # [-G traffic-noratio] database size without spam ratios # [-G traffic] mail message rates and spam ratios # [-G ratio] spam ratios # [-h dcc_homedir] # [-T /usr/local/bin/rrdtool] # see the FreeBSD package or elsewhere # [-O rrdopts] additional rrdtool options for all graphs # [-t title] for graphs; '%1' is replaced with the type of graph # [-s span] time covered by graphs. # The default is "1day,1week,1month,1year" # [-S stop-epoch] end of the graph # [-y vresol] day, minute, ... vertical access for messages # gname base file name for graphs, - for stdout # rrd RRD database for the graph # The rrd files must be initialzed with dcc-stats-init, which is called # automatically by dcc-stats-collect. Data must be collected every # 10 minutes with dcc-stats-collect. The rrd files should be in # /var/dcc/stats # Copyright (c) 2012 by Rhyolite Software, LLC # # This agreement is not applicable to any entity which sells anti-spam # solutions to others or provides an anti-spam solution as part of a # security solution sold to other entities, or to a private network # which employs the DCC or uses data provided by operation of the DCC # but does not provide corresponding data to other users. # # Permission to use, copy, modify, and distribute this software without # changes for any purpose with or without fee is hereby granted, provided # that the above copyright notice and this permission notice appear in all # copies and any distributed versions or copies are either unchanged # or not called anything similar to "DCC" or "Distributed Checksum # Clearinghouse". # # Parties not eligible to receive a license under this agreement can # obtain a commercial license to use DCC by contacting Rhyolite Software # at sales@rhyolite.com. # # A commercial license would be for Distributed Checksum and Reputation # Clearinghouse software. That software includes additional features. This # free license for Distributed ChecksumClearinghouse Software does not in any # way grant permision to use Distributed Checksum and Reputation Clearinghouse # software # # THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL # WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC # BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES # OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, # ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Rhyolite Software DCC 1.3.152-1.88 $Revision$ # Generated automatically from dcc-stats-graph.in by configure. DCC_HOMEDIR=/var/dcc DEBUG= RRDTOOL=/usr/local/bin/rrdtool # check the args once to get the home directory while getopts "xqBdRmh:G:T:O:t:s:S:y:" c; do case $c in x) set -x; DEBUG=-x=;; h) DCC_HOMEDIR="$OPTARG";; *) ;; esac done . $DCC_HOMEDIR/dcc_conf BIG= GRAPH_DB= GRAPH_TRAFFIC= GRAPH_RATIO= GRAPH_REP= GRAPH_BADREP= GRAPH_SET= RRDOPTS="--color CANVAS#e0e0e0" TITLE_SET= SPANS_SET= SPANS="1day,1week,1month,1year" STOP= YRESOL=86400 YLABEL=day USAGE="`basename $0`: [-xqB] [-h homedir] [-T rrdtool] [-O rrdopts] [-G type] [t title] [-s spans] [-S stop-epoch] [-y day|hour|min|sec] gname rrd" OPTIND=1 while getopts "xqBdRmh:G:T:O:t:s:S:y:" c; do case $c in x) ;; # handled above q) exec 1>/dev/null;; h) ;; # handled above B) BIG=yes;; d) GRAPH_SET=yes; GRAPH_DB=yes;; # obsolete R) GRAPH_RATIO=;; # obsolete m) GRAPH_SET=yes; GRAPH_TRAFFIC=yes; GRAPH_RATIO=yes;; # obsolete G) GRAPH_SET=yes case "$OPTARG" in db) GRAPH_DB=yes;; db-min) GRAPH_DB=db-min;; traffic-noratio) GRAPH_TRAFFIC=yes;; traffic) GRAPH_TRAFFIC=yes; GRAPH_RATIO=yes;; ratio) GRAPH_RATIO=yes;; *) echo "$USAGE" 1>&2; exit 1;; esac ;; T) RRDTOOL="$OPTARG";; O) RRDOPTS="$RRDOPTS $OPTARG";; t) TITLE_SET=yes; TITLE_PAT="$OPTARG";; s) SPANS_SET=yes; SPANS="$OPTARG";; S) if expr "$OPTARG" : '[0-9]*$' >/dev/null; then STOP=$OPTARG else echo "$OPTARG is a bad number of seconds since the Epoch" 1>&2 exit 1 fi ;; y) case "$OPTARG" in day) YRESOL=86400; YLABEL=day;; hour) YRESOL=3600; YLABEL=hour;; min) YRESOL=60; YLABEL=min;; sec) YRESOL=1; YLABEL=sec;; esac ;; *) echo "$USAGE" 1>&2; exit 1;; esac done shift `expr $OPTIND - 1 || true` if test "$#" -lt 1; then echo "$USAGE" 1>&2 exit 1 fi if test -z "$GRAPH_SET"; then GRAPH_RATIO=yes # bug compatible with old versions fi BASE_DIR="$DCC_HOMEDIR/stats" cd "$BASE_DIR" GNAME="$1" if test "$#" -ge 2; then # assume .rrd file is same as the graph name if the .rrd file is absent shift fi FILE="$1" # trim unneeded directory names BASE_FILE=`expr \( \( "$FILE" : '\(.*\)-REP.rrd' \) \| \ \( "$FILE" : '\(.*\).rrd' \) \) \| "$FILE"` FILE="$BASE_FILE.rrd" if test ! -s "$FILE"; then echo "\"$FILE\" is not a good rrd file" 1>&2 exit 1 fi if test "$TITLE_SET" != yes; then if test "X$GNAME" != X-; then TITLE_PAT="%1 at $GNAME" else TITLE_PAT="%1" fi fi RRDGRAPH="$RRDTOOL graph" RRDVERSION=`$RRDTOOL version \ | sed -n -e 's/^RRDtool *\([0-9]*\)\.\([0-9]*\) .*/\10000 + \200/p' \ -e 's/^RRDtool *\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\10000 + \200 + \3/p'` if test -z "RRDVERSION"; then RRDVERSION=999999 else RRDVERSION=`expr $RRDVERSION` fi PCENT="/,100,*,0,100,LIMIT" if test -n "$BIG"; then XYEAR_MONTHS=1 GSIZE="--width 600 --height 240" P_YGRID= # % or spam ratio vertical grid M_YGRID= # messages vertical grid H_YGRID="--alt-autoscale-max" # database vertical grid M_YLABEL="message/$YLABEL" AVGFMT="%.0lf/$YLABEL" else XYEAR_MONTHS=2 GSIZE="--width 200 --height 40" P_YGRID="--y-grid 25:2" if test $RRDVERSION -le 10099; then M_YGRID="--alt-y-mrtg" H_YGRID="--alt-y-mrtg" else M_YGRID= H_YGRID= fi M_YLABEL=msgs/$YLABEL AVGFMT="%.1lf %s/$YLABEL" fi PCENTFMT="%.0lf%%" TSFMT='%Y/%m/%d %R %Z' # use only a few colors to try to be portable C_GREEN='#00ff7f' C_YELLOW='#ffff00' C_PINK='#ffb6c1' C_INDIANRED='#ff6a6a' C_RED2='#ee0000' C_BLUE='#0000ff' C_SKY_BLUE='#87cefa' C_ORANGE='#ffa500' C_DARK_ORANGE='#ff8c00' C_BLACK='#000000' FTYPE=png ATTRIBS="$GSIZE --imgformat PNG --lower-limit 0" # find good ending dates date2ts () { if test "$3" -eq 0; then eval $1=now $2="' '" return fi NEW_END=$3 if test -n "$4"; then NEW_END=`expr $NEW_END - $NEW_END % $4 || true` fi eval $1=$NEW_END if NEW_TS=`date -r $NEW_END "+$TSFMT" 2>/dev/null`; then : ; else # deal with systems that do not have `date -r` NEW_TS=`/usr/bin/perl -e "use POSIX qw(strftime); \ print strftime "$TSFMT", localtime($LAST);"` fi if test $RRDVERSION -ge 10100; then NEW_TS=`echo "$NEW_TS" | sed -e 's/:/\\\:/g'` fi eval $2="'COMMENT:$NEW_TS'" } STEP= HAVE_MAX= HAVE_TRAP= eval `$RRDTOOL info $FILE \ | sed -n -e 's/^step = \([0-9][0-9]*\)/STEP=\1/p' \ -e 's/^rra.*cf = .MAX.*/HAVE_MAX=yes/p' \ -e 's/ds.trapped.*DERIVE.*/HAVE_TRAP=yes/p'` LAST=`$RRDTOOL last $FILE` if test -n "$STOP" -a "$LAST" -gt 0"$STOP"; then LAST="$STOP" fi # avoid odd times when individual servers were polled LAST=`expr $LAST - $LAST % $STEP || true` date2ts END COMMENT_END $LAST date2ts END_DAY COMMENT_END_DAY $LAST 86400 for DUR in `echo $SPANS | tr ',' ' '`; do case $DUR in 1d*) DUR=1day SPAN=24h SECS=86400 XGRID="--x-grid HOUR:1:HOUR:2:HOUR:2:0:%k" # as the "rdtool graph" man page suggests, don't be fooled # by daylight savings time ;; 1w*) DUR=1week SPAN=168h SECS=604800 # 24*3600 = 86400 if test -n "$BIG"; then XGRID="--x-grid HOUR:6:DAY:1:DAY:1:86400:%a\ %m/%d" else XGRID="--x-grid HOUR:6:DAY:1:DAY:1:86400:%a" fi # as the "rdtool graph" man page suggests, don't be fooled # by daylight savings time ;; 1m*) DUR=1month SPAN=$DUR SECS=2678400 XGRID="--x-grid WEEK:1:WEEK:1:WEEK:1:0:%b/%d" ;; 1y*) DUR=1year SPAN=$DUR SECS=31622400 # label every month on big graphs and every other on small # 28*24*60*60 = 2419200 XGRID="--x-grid MONTH:1:YEAR:1:MONTH:$XYEAR_MONTHS:2419200:%b" ;; 2y*) DUR=2years SPAN=$DUR SECS=63244800 if test "$XYEAR_MONTHS" = 2; then # small graph with 1 label/year # 365*24*60*60 = 31536000 = year XGRID="--x-grid YEAR:1:YEAR:1:YEAR:1:31536000:%Y" else # label every other month on big graphs # 28*24*60*60 = 2419200 XYEAR_MONTHS=2 XGRID="--x-grid MONTH:1:YEAR:1:MONTH:2:2419200:%b" fi ;; *) case $DUR in 3y*) DUR=3years SECS=94867200 ;; 4y*) DUR=4years SECS=126489600 ;; # assume everything else is the 5 year maximum in the RRD files *) DUR=5years SECS=158112000 ;; esac SPAN=$DUR if test "$XYEAR_MONTHS" = 2; then # small graph with 1 label/year # 365*24*60*60 = 31536000 = year XGRID="--x-grid YEAR:1:YEAR:1:YEAR:1:31536000:%Y" else # big graph with 1 label/year XGRID="--x-grid MONTH:1:MONTH:12:MONTH:12:0:%b/%y" fi ;; esac if test -n "$STOP"; then EOD=`expr $LAST + $SECS || true` if test $STOP -gt $EOD; then echo "`date -r $STOP` is after end of data on `date -r $EOD`" 1>&2 exit 1 fi # quickly get the oldest timestamp FIRST=`$RRDTOOL dump $FILE | head -200 \ | sed -n -e '1,/ .*/\1/p' \ | head -1` if test "$FIRST" -gt "$STOP"; then echo "`date -r $STOP` is before the start of data on `date -r $FIRST`" 1>&2 exit 1 fi fi ONAME=- if test $YRESOL -eq 1; then YUNIT="0,1e12,LIMIT" else YUNIT="$YRESOL,*,0,1e12,LIMIT" fi # to suppress labels for missing data, get # MAX_TRAP=maximum trapped value # PERCENT_TRAP=percent trapped spam # MAX_REP=maxiumum reputuations # PERENT_REP=percent reputations*10 RRDCMDS="DEF:reports=$FILE:reports:AVERAGE CDEF:greports=reports,$YUNIT PRINT:greports:MAX:%0.lf" SEDCMDS="-e s/print\[0]=/MAX_RPTS=/" SED_MAX_REP=1 SED_PERENT_REP=2 MAX_RPTS=0 DEF_TRAP= SUB_TRAP= MAX_TRAP=0 PERCENT_TRAP=0 if test -z "$GRAPH_REP$GRAPH_BADREP" \ -a -n "$HAVE_TRAP" -a -n "$HAVE_MAX"; then DEF_TRAP="DEF:trap=$FILE:trapped:AVERAGE" SUB_TRAP='trap,-,' RRDCMDS="$RRDCMDS $DEF_TRAP CDEF:gtrap=trap,$YUNIT PRINT:gtrap:MAX:%.0lf CDEF:tp=trap,reports,/,100,* PRINT:tp:MAX:%.0lf" SEDCMDS="$SEDCMDS -e s/print\[1]=/MAX_TRAP=/ -e s/print\[2]=/PERCENT_TRAP=/" SED_MAX_REP=3 SED_PERENT_REP=4 fi if test -z "$GRAPH_REP$GRAPH_BADREP"; then eval `$RRDTOOL graphv - --end $END --start end-$SPAN $RRDCMDS \ | sed -e 's/nan/0/' -e 's/ //g' $SEDCMDS` fi setlabel () { if test $1; then eval $2="'$3'" else eval $2= fi } if test -n "$BIG"; then LABEL_OK="not bulk" LABEL_BULK="possible spam" LABEL_SPAM="likely spam" setlabel "$PERCENT_TRAP -ge 1" LABEL_TRAP "trapped spam" setlabel "$PERCENT_TRAP -ge 1" LABEL_TRAP_RATIO "trapped spam" else LABEL_OK="not bulk" LABEL_BULK="possible" LABEL_SPAM="likely" setlabel "$PERCENT_TRAP -ge 10" LABEL_TRAP "trap" fi if test "$GRAPH_TRAFFIC" = yes; then if test "X$GNAME" != X-; then ONAME=$GNAME-spam.$DUR.$FTYPE echo "$ONAME: " | tr -d '\012' fi TITLE=`echo "$TITLE_PAT" | sed -e 's/%1/Mail Checked/g'` TRAFFIC="$DEF_TRAP 'DEF:bulk=$FILE:bulk:AVERAGE' 'DEF:spam=$FILE:spam:AVERAGE' 'DEF:reports=$FILE:reports:AVERAGE'" if test -n "$DEF_TRAP"; then TRAFFIC="$TRAFFIC 'CDEF:gtrap=trap,$YUNIT' 'AREA:gtrap$C_YELLOW:$LABEL_TRAP'" fi TRAFFIC="$TRAFFIC 'CDEF:gspam=spam,${SUB_TRAP}$YUNIT' 'AREA:gspam$C_PINK:$LABEL_SPAM:STACK' 'CDEF:gbulk=bulk,spam,-,$YUNIT' 'AREA:gbulk$C_INDIANRED:$LABEL_BULK:STACK' 'CDEF:gok=reports,bulk,-,$YUNIT' 'AREA:gok$C_SKY_BLUE:$LABEL_OK:STACK'" LEGEND="'COMMENT:\j'" if test "$MAX_RPTS" -le 0; then LEGEND="$LEGEND '$COMMENT_END\c'" else if test -n "$LABEL_TRAP"; then LEGEND="$LEGEND 'GPRINT:gtrap:AVERAGE:$AVGFMT'" fi LEGEND="$LEGEND 'GPRINT:gspam:AVERAGE:$AVGFMT' 'GPRINT:gbulk:AVERAGE:$AVGFMT' 'GPRINT:gok:AVERAGE:$AVGFMT'" LEGEND="$LEGEND'\j' 'CDEF:total=reports,$YUNIT' 'GPRINT:total:AVERAGE:$AVGFMT total' '$COMMENT_END\j'" fi eval $RRDGRAPH $ONAME "$RRDOPTS" --end $END --start end-$SPAN \ $ATTRIBS "--title '$TITLE'" \ $XGRID $M_YGRID --vertical-label $M_YLABEL \ $TRAFFIC $LEGEND test "X$GNAME" = X- && exit fi if test "$GRAPH_RATIO" = yes; then if test "X$GNAME" != X-; then ONAME=$GNAME-spam-ratio.$DUR.$FTYPE echo "$ONAME: " | tr -d '\012' fi TITLE=`echo "$TITLE_PAT" | sed -e 's/%1/Spam Ratio/g'` RATIOS="$DEF_TRAP 'DEF:reports=$FILE:reports:AVERAGE' 'DEF:bulk=$FILE:bulk:AVERAGE' 'CDEF:pcentbulk=bulk,${SUB_TRAP}reports,${SUB_TRAP}$PCENT' 'DEF:spam=$FILE:spam:AVERAGE' 'CDEF:pcentspam=spam,${SUB_TRAP}reports,${SUB_TRAP}$PCENT' 'AREA:pcentbulk$C_INDIANRED:$LABEL_BULK' 'AREA:pcentspam$C_PINK:$LABEL_SPAM'" if test -n "$DEF_TRAP"; then RATIOS="$RATIOS 'CDEF:pcenttrap=trap,reports,$PCENT' 'LINE:pcenttrap$C_YELLOW:$LABEL_TRAP_RATIO'" fi LEGEND="'COMMENT:\j'" if test "$MAX_RPTS" -gt 0; then LEGEND="$LEGEND 'CDEF:ppcentbulk=pcentbulk,pcentspam,-' 'GPRINT:ppcentbulk:AVERAGE:$PCENTFMT' 'GPRINT:pcentspam:AVERAGE:$PCENTFMT'" if test -n "$LABEL_TRAP_RATIO"; then LEGEND="$LEGEND 'GPRINT:pcenttrap:AVERAGE:$PCENTFMT'" fi LEGEND="$LEGEND'\j'" if test -n "$BIG"; then LEGEND="$LEGEND 'GPRINT:pcentbulk:AVERAGE:\t$PCENTFMT possible+likely'" else LEGEND="$LEGEND 'GPRINT:pcentbulk:AVERAGE:$PCENTFMT possible+likely'" fi fi LEGEND="$LEGEND '$COMMENT_END'" eval $RRDGRAPH $ONAME "$RRDOPTS" \ --end $END --start end-$SPAN \ $ATTRIBS "--title '$TITLE'" \ $XGRID $P_YGRID --upper-limit 100 \ $RATIOS $LEGEND test "X$GNAME" = X- && exit fi # database size graph if test -n "$GRAPH_DB" -a \( -n "$SPANS_SET" -o $SPAN != 24h \); then if test "X$GNAME" != X-; then ONAME=$GNAME-hashes.$DUR.$FTYPE echo "$ONAME: " | tr -d '\012' fi TITLE=`echo "$TITLE_PAT" | sed -e 's/%1/Checksums/g'` # show only the minimum values for old RRD files if test "$GRAPH_DB" = yes; then if test -z "$HAVE_MAX"; then GRAPH_DB=db-min fi fi if test "$GRAPH_DB" = db-min; then DISPLAY="DEF:minhash=$FILE:hashes:MIN AREA:minhash$C_PINK" else DISPLAY="DEF:minhash=$FILE:hashes:MIN DEF:maxhash=$FILE:hashes:MAX AREA:maxhash$C_INDIANRED:max AREA:minhash$C_PINK:min" fi if test -z "$BIG"; then LEGEND="'$COMMENT_END_DAY'" else LEGEND="'$COMMENT_END_DAY\c'" fi # take the database values from the last server eval $RRDGRAPH $ONAME "$RRDOPTS" \ --end $END_DAY --start end-$SPAN \ $ATTRIBS --step 86400 "--title '$TITLE'" \ $XGRID $H_YGRID $DISPLAY $LEGEND fi done