root / plugins / dspam / dspam_ @ 17f78427
Historique | Voir | Annoter | Télécharger (21 ko)
| 1 | 429819cf | Tom Hendrikx | #!/bin/sh |
|---|---|---|---|
| 2 | # -*- sh -*- |
||
| 3 | |||
| 4 | : << =cut |
||
| 5 | |||
| 6 | =head1 NAME |
||
| 7 | |||
| 8 | dspam_ - Plugin to monitor various aspects of DSPAM performance |
||
| 9 | |||
| 10 | =head1 APPLICABLE SYSTEMS |
||
| 11 | |||
| 12 | Any system running a recent (3.8.0 or higher) DSPAM install. |
||
| 13 | |||
| 14 | =head1 CONFIGURATION |
||
| 15 | |||
| 16 | The plugin uses the output of the dspam_stats command, which is usually part |
||
| 17 | of any DSPAM install. You'll need to run this plugin as a user that has enough |
||
| 18 | rights to run dspam_stats and generate data for all users. This means that the |
||
| 19 | plugin needs to be run either as root, or as a user that has read access to |
||
| 20 | dspam.conf, and is added as a Trusted user in dspam.conf. |
||
| 21 | |||
| 22 | The following environment variables are used by this plugin: |
||
| 23 | |||
| 24 | dspam_stats - Where to find the dspam_stats binary when it's not in |
||
| 25 | $PATH (default: find anywhere in $PATH). |
||
| 26 | statefile - Where to read/write the statefile that is used to store |
||
| 27 | dspam_stats output |
||
| 28 | (default: $MUNIN_PLUGSTATE/dspam.state). |
||
| 29 | warning - When to trigger a warning (default: 95:). |
||
| 30 | critical - When to trigger a critical (default: 90:). |
||
| 31 | pattern - A pattern that is passed to grep in order to find the |
||
| 32 | DSPAM uids to display. When this variable is set, the |
||
| 33 | value of target (see USAGE) is ignored (default: empty). |
||
| 34 | description - A string describing the set of uids selected by |
||
| 35 | above pattern (default: empty). |
||
| 36 | |||
| 37 | Warning and critical values can also set on a DSPAM uid basis, use Munins |
||
| 38 | internal format for the DSPAM uid for this notation (see CONFIGURATION |
||
| 39 | EXAMPLES and USAGE for details). |
||
| 40 | |||
| 41 | =head2 CONFIGURATION EXAMPLES |
||
| 42 | |||
| 43 | [dspam*] |
||
| 44 | user root |
||
| 45 | env.dspam_stats /opt/dspam/bin/dspam_stats |
||
| 46 | env.statefile /tmp/dspam.state |
||
| 47 | |||
| 48 | [dspam_accuracy*] |
||
| 49 | env.critical 95: |
||
| 50 | env.warning 96: |
||
| 51 | |||
| 52 | # raise warning level for username@example.org |
||
| 53 | env.username_example_org_warning 97: |
||
| 54 | |||
| 55 | # show all accounts from one domain |
||
| 56 | env.pattern @example\.org |
||
| 57 | env.description domain example.org |
||
| 58 | |||
| 59 | =head1 USAGE |
||
| 60 | |||
| 61 | Link this plugin to /etc/munin/plugins/ and restart the munin-node. The link |
||
| 62 | should be in the format: dspam_<graph>_<target>, where: |
||
| 63 | |||
| 64 | 5d346e98 | jolan78 | graph - One of: accuracy, processed, absprocessed, relprocessed. |
| 65 | 429819cf | Tom Hendrikx | target - The uid that DSPAM generates in dspam_stats output, |
| 66 | but converted to Munin internal name format. Normally |
||
| 67 | this means that non-alphabetic and non-numeral characters |
||
| 68 | are replaced by an underscore. For example, |
||
| 69 | username@example.org will become username_example_org. |
||
| 70 | A special case is uid ALL, which will draw a graph for |
||
| 71 | a total of all uids, or for a list of all uids (depending |
||
| 72 | on the graph type). |
||
| 73 | NB For advanced uid selection such as 'all users of domain |
||
| 74 | 17f78427 | Lars Kruse | example.org', please see the environment variable 'pattern' |
| 75 | 429819cf | Tom Hendrikx | under CONFIGURATION. |
| 76 | |||
| 77 | =head1 INTERPRETATION |
||
| 78 | |||
| 79 | The plugin supports the following graph types: |
||
| 80 | |||
| 81 | accuracy - Shows the overall accuracy of all users as a |
||
| 82 | percentage. The overall accuracy is the number of |
||
| 83 | correctly classified messages (both ham and spam) in |
||
| 84 | relation to the number of all processed messages. |
||
| 85 | |||
| 86 | 8589c6df | klemens | absprocessed - Shows the absolute numbers of messages processed, |
| 87 | 429819cf | Tom Hendrikx | sorted by the classification that DSPAM uses. The |
| 88 | numbers are stacked, making the height of the column |
||
| 89 | display the increase of processed messages over time. |
||
| 90 | |||
| 91 | 5d346e98 | jolan78 | relprocessed - Shows the same data as dspam_absprocessed_, but as |
| 92 | messages per minute instead of ever-growing asolute |
||
| 93 | values. |
||
| 94 | |||
| 95 | fdbfa2c9 | jolan78 | processed - Shows the same data as dspam_absprocessed_, but as |
| 96 | 429819cf | Tom Hendrikx | percentage of the total amount of processed messages, |
| 97 | making it clear to see how the amounts of classified |
||
| 98 | messages are divided. |
||
| 99 | |||
| 100 | =head1 AUTHOR |
||
| 101 | |||
| 102 | Copyright 2010 Tom Hendrikx <tom@whyscream.net> |
||
| 103 | |||
| 104 | =head1 LICENSE |
||
| 105 | |||
| 106 | GPLv2 |
||
| 107 | |||
| 108 | This program is free software; you can redistribute it and/or modify |
||
| 109 | it under the terms of the GNU General Public License as published by |
||
| 110 | the Free Software Foundation; version 2 dated June, 1991. |
||
| 111 | |||
| 112 | This program is distributed in the hope that it will be useful, but |
||
| 113 | WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 114 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
| 115 | General Public License for more details. |
||
| 116 | |||
| 117 | You should have received a copy of the GNU General Public License |
||
| 118 | along with this program; if not, write to the Free Software |
||
| 119 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
||
| 120 | 02110-1301 USA. |
||
| 121 | |||
| 122 | =head1 BUGS |
||
| 123 | |||
| 124 | None known. Please report to author when you think you found something. |
||
| 125 | |||
| 126 | =head2 TODO LIST |
||
| 127 | |||
| 128 | Currently developed and tested with bash/dash on linux. |
||
| 129 | More testing might be needed with other shells and OSes. |
||
| 130 | |||
| 131 | =head1 VERSION |
||
| 132 | |||
| 133 | $Id: dspam_ 72 2010-09-15 22:09:15Z tomhendr $ |
||
| 134 | |||
| 135 | =head1 MAGIC MARKERS |
||
| 136 | |||
| 137 | #%# family=auto |
||
| 138 | #%# capabilities=autoconf suggest |
||
| 139 | |||
| 140 | =cut |
||
| 141 | |||
| 142 | # defaults for configurable settings |
||
| 143 | : ${dspam_stats:=$(which dspam_stats 2> /dev/null)}
|
||
| 144 | : ${statefile:=${MUNIN_PLUGSTATE}/dspam.state}
|
||
| 145 | : ${statefile_max_age:=180} # 3 minutes
|
||
| 146 | : ${warning:=95:}
|
||
| 147 | : ${critical:=90:}
|
||
| 148 | |||
| 149 | # include munin plugin helper |
||
| 150 | . $MUNIN_LIBDIR/plugins/plugin.sh |
||
| 151 | |||
| 152 | ####################################### |
||
| 153 | # Some generic file locking functions # |
||
| 154 | ####################################### |
||
| 155 | |||
| 156 | # |
||
| 157 | # file_is_usable $file $max_age |
||
| 158 | # Check if file is OK for usage: existing, readable and not too old |
||
| 159 | # |
||
| 160 | file_is_usable() {
|
||
| 161 | local file=$1 |
||
| 162 | local max_age=$2 |
||
| 163 | local lock=$file.lock |
||
| 164 | |||
| 165 | [ ! -f $file ] && debug statefile $file does not exist && return 66 # EX_NOINPUT |
||
| 166 | [ ! -r $file ] && debug statefile $file is not readable && return 65 # EX_DATAERR |
||
| 167 | |||
| 168 | local mtime=$(stat --format %Y $file) |
||
| 169 | local file_age=$(( $(date +%s) -mtime )) |
||
| 170 | [ $file_age -gt $max_age ] && debug file $file is too old: $file_age seconds && return 65 # EX_DATAERR |
||
| 171 | |||
| 172 | debug file $file is ok, $file_age seconds old |
||
| 173 | return 0 # EX_OK |
||
| 174 | } |
||
| 175 | |||
| 176 | # |
||
| 177 | # file_get_lock $file |
||
| 178 | # Obtain a lock for the named file |
||
| 179 | # |
||
| 180 | file_get_lock() {
|
||
| 181 | local file=$1 |
||
| 182 | local lock=$file.lock |
||
| 183 | |||
| 184 | while file_is_locked $file; do |
||
| 185 | sleep 1 |
||
| 186 | done |
||
| 187 | |||
| 188 | echo $$ > $lock |
||
| 189 | [ $? -gt 0 ] && debug failed to create lockfile $lock && return 73 # EX_CANTCREAT |
||
| 190 | |||
| 191 | debug created lock for $file |
||
| 192 | return 0 # EX_OK |
||
| 193 | } |
||
| 194 | |||
| 195 | # |
||
| 196 | # file_remove_lock $file |
||
| 197 | # Remove a set lockfile for the named file |
||
| 198 | # |
||
| 199 | file_remove_lock() {
|
||
| 200 | local file=$1 |
||
| 201 | local lock=$file.lock |
||
| 202 | |||
| 203 | rm -f $lock |
||
| 204 | [ $? -gt 0 ] && debug failed to remove lockfile $lock && return 69 # EX_UNAVAILABLE |
||
| 205 | |||
| 206 | debug removed lock for file $file |
||
| 207 | return 0 # EX_OK |
||
| 208 | } |
||
| 209 | |||
| 210 | # |
||
| 211 | # file_is_locked $file |
||
| 212 | # Check if a file is locked |
||
| 213 | # |
||
| 214 | file_is_locked() {
|
||
| 215 | local file=$1 |
||
| 216 | local lock=$1.lock |
||
| 217 | |||
| 218 | a27374ac | jolan78 | if [ -f "$lock" ];then |
| 219 | debug file $file is locked |
||
| 220 | local pid=$(cat "$lock") |
||
| 221 | if ps h -p "$pid" -o comm=|grep -q dspam_; then |
||
| 222 | return 0 # EX_OK |
||
| 223 | else |
||
| 224 | debug lock for file $file is no longer valid |
||
| 225 | file_remove_lock $file |
||
| 226 | fi |
||
| 227 | fi |
||
| 228 | 429819cf | Tom Hendrikx | debug file $file is not locked |
| 229 | return 69 # EX_UNAVAILABLE |
||
| 230 | } |
||
| 231 | |||
| 232 | #################################### |
||
| 233 | # DSPAM output processing function # |
||
| 234 | #################################### |
||
| 235 | |||
| 236 | # |
||
| 237 | # update_statefile |
||
| 238 | # Read the output of dspam_stats, convert it and write usable data to the statefile |
||
| 239 | # |
||
| 240 | update_statefile() {
|
||
| 241 | |||
| 242 | file_is_usable $statefile $statefile_max_age && return $? # return when all OK |
||
| 243 | ! file_get_lock $statefile && return $? # return when locking failed |
||
| 244 | |||
| 245 | local tmpfile=$(mktemp) |
||
| 246 | debug created tmpfile $tmpfile |
||
| 247 | |||
| 248 | debug starting $dspam_stats -t -S |
||
| 249 | local t_start=$(date +%s) |
||
| 250 | ab554066 | jolan78 | $dspam_stats -t -S | while IFS=' :' read a b c d e f g h i j k l m x; do |
| 251 | 429819cf | Tom Hendrikx | # example of output format (3.9.1 rc1) for each user: |
| 252 | #username@example.org |
||
| 253 | # TP: 0 TN: 2147 FP: 0 FN: 53 SC: 0 NC: 0 |
||
| 254 | # SHR: 0.00% HSR: 0.00% OCA: 97.59% |
||
| 255 | |||
| 256 | 8cf82404 | Tom Hendrikx | # or for short user names: |
| 257 | ab554066 | jolan78 | #vmail TP:80312082 TN:198342928 FP: 82941 FN: 57326 SC: 0 NC: 3498 |
| 258 | 8cf82404 | Tom Hendrikx | # SHR: 99.56% HSR: 0.00% OCA: 99.69% |
| 259 | |||
| 260 | 429819cf | Tom Hendrikx | case $a in |
| 261 | ab554066 | jolan78 | TP) |
| 262 | 429819cf | Tom Hendrikx | # the 2nd line |
| 263 | local tp=$b tn=$d fp=$f fn=$h sc=$j nc=$l |
||
| 264 | ;; |
||
| 265 | ab554066 | jolan78 | SHR) |
| 266 | 429819cf | Tom Hendrikx | # the 3rd line |
| 267 | local shr=$(echo $b | sed 's/%$//g') |
||
| 268 | local hsr=$(echo $d | sed 's/%$//g') |
||
| 269 | local oca=$(echo $f | sed 's/%$//g') |
||
| 270 | |||
| 271 | # we're done, write data from current user to the statefile |
||
| 272 | local clean_user=$(clean_fieldname $uid) |
||
| 273 | local state="$uid $clean_user $tp $tn $fp $fn $sc $nc $shr $hsr $oca" |
||
| 274 | echo $state >> $tmpfile |
||
| 275 | [ $? -gt 0 ] && debug failed to write data for $uid to tmpfile && return 73 # EX_CANTCREAT |
||
| 276 | debug wrote data for $uid to tmpfile: $state |
||
| 277 | ;; |
||
| 278 | *) |
||
| 279 | # the 1st line |
||
| 280 | local uid=$a |
||
| 281 | 8cf82404 | Tom Hendrikx | # data from 2nd line is also here |
| 282 | ab554066 | jolan78 | [ "$b" = "TP" ] && local tp=$c tn=$e fp=$g fn=$i sc=$k nc=$m |
| 283 | 429819cf | Tom Hendrikx | ;; |
| 284 | esac |
||
| 285 | done |
||
| 286 | local t_end=$(date +%s) |
||
| 287 | debug dspam_stats finished, runtime $((t_end - t_start)) seconds |
||
| 288 | |||
| 289 | mv $tmpfile $statefile |
||
| 290 | [ $? -gt 0 ] && debug failed to move tmpfile to $statefile && return 73 # EX_CANTCREAT |
||
| 291 | debug moved tmpfile to $statefile |
||
| 292 | |||
| 293 | file_remove_lock $statefile |
||
| 294 | |||
| 295 | return 0 # EX_OK |
||
| 296 | } |
||
| 297 | |||
| 298 | |||
| 299 | # |
||
| 300 | # abs2perc |
||
| 301 | # Outputs a percentage calculated from its two arguments |
||
| 302 | # |
||
| 303 | abs2perc() {
|
||
| 304 | # division by zero protection: debug output is merely informal and harmless ;) |
||
| 305 | [ $1 -eq 0 ] && echo 0 && debug abs2perc prevented possible division-by-zero on first argument && return 0 # EX_OK |
||
| 306 | [ $2 -eq 0 ] && echo 0 && debug abs2perc prevented possible division-by-zero on second argument && return 0 # EX_OK |
||
| 307 | |||
| 308 | echo $1 $2 | awk '{ print $1 * 100 / $2 }'
|
||
| 309 | } |
||
| 310 | |||
| 311 | # |
||
| 312 | # debug $output |
||
| 313 | # Prints debugging output when munin-run is called with --pidebug argument (i.e. when MUNIN_DEBUG is set) |
||
| 314 | # |
||
| 315 | debug() {
|
||
| 316 | if [ -n "$MUNIN_DEBUG" ]; then |
||
| 317 | echo "# DEBUG: $@" |
||
| 318 | fi |
||
| 319 | } |
||
| 320 | |||
| 321 | ######################################## |
||
| 322 | # Functions that generate munin output # |
||
| 323 | ######################################## |
||
| 324 | |||
| 325 | # |
||
| 326 | # print_autoconf |
||
| 327 | # Output for 'munin-node-configure' autoconf functionality |
||
| 328 | # |
||
| 329 | print_autoconf() {
|
||
| 330 | if [ -z "$dspam_stats" ]; then |
||
| 331 | echo "no (no dspam_stats binary found)" |
||
| 332 | elif [ ! -x $dspam_stats ]; then |
||
| 333 | echo "no ($dspam_stats found but not executable)" |
||
| 334 | else |
||
| 335 | echo yes |
||
| 336 | fi |
||
| 337 | } |
||
| 338 | |||
| 339 | # |
||
| 340 | # print_suggest |
||
| 341 | # Output for 'munin-node-configure --suggest' |
||
| 342 | # |
||
| 343 | print_suggest() {
|
||
| 344 | echo accuracy_ALL |
||
| 345 | echo processed_ALL |
||
| 346 | echo absprocessed_ALL |
||
| 347 | 5d346e98 | jolan78 | echo relprocessed_ALL |
| 348 | 429819cf | Tom Hendrikx | } |
| 349 | |||
| 350 | # |
||
| 351 | # print_config |
||
| 352 | # Output for 'munin-run <plugin> config' command. |
||
| 353 | # |
||
| 354 | print_config() {
|
||
| 355 | debug printing config for graph: $graph |
||
| 356 | |||
| 357 | while file_is_locked $statefile; do |
||
| 358 | debug statefile is locked, waiting |
||
| 359 | sleep 1 |
||
| 360 | done |
||
| 361 | |||
| 362 | case $graph in |
||
| 363 | accuracy) |
||
| 364 | if [ -n "$pattern" ]; then |
||
| 365 | debug env.pattern was set, so use it: $pattern |
||
| 366 | local uid=$description |
||
| 367 | local uid_count=$(grep $pattern $statefile | wc -l) |
||
| 368 | debug uid_count retrieved from statefile: $uid_count |
||
| 369 | elif [ "$target" = "ALL" ]; then |
||
| 370 | local pattern="-v TOTAL" |
||
| 371 | debug target=ALL: need pattern for all users but not TOTAL: $pattern |
||
| 372 | local uid="all users" |
||
| 373 | local uid_count=$(grep $pattern $statefile | wc -l) |
||
| 374 | debug uid_count retrieved from statefile: $uid_count |
||
| 375 | else |
||
| 376 | local pattern="\b$target\b" |
||
| 377 | debug target=$target: need pattern for a single user: $pattern |
||
| 378 | local uid=$(grep $pattern $statefile | cut -d' ' -f1) |
||
| 379 | debug retrieved uid value from statefile: $uid |
||
| 380 | local uid_count=1 |
||
| 381 | fi |
||
| 382 | |||
| 383 | echo "graph_title Accuracy for $uid" |
||
| 384 | e00b066f | dipohl | echo graph_category spamfilter |
| 385 | 429819cf | Tom Hendrikx | echo graph_args --base 1000 --upper-limit 100 --rigid |
| 386 | echo graph_vlabel Accuracy in % |
||
| 387 | echo "graph_info This graph shows the current DSPAM Overall Accuracy for $uid ($uid_count uids). Overall Accuracy is the percentage of messages that is classified correctly as either ham or spam." |
||
| 388 | |||
| 389 | debug starting grep for user data in statefile |
||
| 390 | local t_start=$(date +%s) |
||
| 391 | grep $pattern $statefile | while read uid clean_user x; do |
||
| 392 | echo $clean_user.label $uid |
||
| 393 | print_warning $clean_user |
||
| 394 | print_critical $clean_user |
||
| 395 | done |
||
| 396 | local t_end=$(date +%s) |
||
| 397 | debug grep finished, runtime $((t_end - t_start)) seconds |
||
| 398 | |||
| 399 | ;; |
||
| 400 | |||
| 401 | processed) |
||
| 402 | if [ -n "$pattern" ]; then |
||
| 403 | debug env.pattern was set, so use it: $pattern |
||
| 404 | local uid=$description |
||
| 405 | local uid_count=$(grep $pattern $statefile | wc -l) |
||
| 406 | debug uid_count retrieved from statefile: $uid_count |
||
| 407 | elif [ "$target" = "ALL" ]; then |
||
| 408 | local pattern="-v TOTAL" |
||
| 409 | debug target=ALL: need pattern for all users but not TOTAL: $pattern |
||
| 410 | local uid="all users" |
||
| 411 | local uid_count=$(grep $pattern $statefile | wc -l) |
||
| 412 | debug uid_count retrieved from statefile: $uid_count |
||
| 413 | else |
||
| 414 | local pattern="\b$target\b" |
||
| 415 | debug target=$target: need pattern for a single user: $pattern |
||
| 416 | local uid=$(grep $pattern $statefile | cut -d' ' -f1) |
||
| 417 | debug retrieved uid value from statefile: $uid |
||
| 418 | local uid_count=1 |
||
| 419 | fi |
||
| 420 | |||
| 421 | echo "graph_title Processed messages for $uid (%)" |
||
| 422 | e00b066f | dipohl | echo graph_category spamfilter |
| 423 | 429819cf | Tom Hendrikx | echo graph_args --base 1000 --upper-limit 100 --rigid |
| 424 | echo graph_vlabel Messages in % |
||
| 425 | echo "graph_info This graph shows the messages that DSPAM processed for $uid ($uid_count uids) in percentages of all processed messages. Messages are divided in the following categories: true positives/negatives, false positives/negatives, and corpusfed ham/spam." |
||
| 426 | echo tp.label True positives |
||
| 427 | echo tp.info Spam messages correctly classified as spam. |
||
| 428 | echo tp.draw AREASTACK |
||
| 429 | echo tn.label True negatives |
||
| 430 | echo tn.info Ham messages correctly classified as ham. |
||
| 431 | echo tn.draw AREASTACK |
||
| 432 | echo fp.label False positives |
||
| 433 | echo fp.info Ham messages incorrectly classified as spam, but corrected by the user. |
||
| 434 | echo fp.draw AREASTACK |
||
| 435 | echo fn.label False negatives |
||
| 436 | echo fn.info Spam messages incorrectly classified as ham, but corrected by the user. |
||
| 437 | echo fn.draw AREASTACK |
||
| 438 | echo sc.label Corpusfed spam |
||
| 439 | echo sc.info Spam messages from a collected corpus for training purposes. |
||
| 440 | echo sc.draw AREASTACK |
||
| 441 | echo nc.label Corpusfed ham |
||
| 442 | echo nc.info Ham messages from a collected corpus for training purposes. |
||
| 443 | echo nc.draw AREASTACK |
||
| 444 | ;; |
||
| 445 | |||
| 446 | 5d346e98 | jolan78 | *processed) |
| 447 | 429819cf | Tom Hendrikx | if [ -n "$pattern" ]; then |
| 448 | debug env.pattern was set, so use it: $pattern |
||
| 449 | local uid=$description |
||
| 450 | local uid_count=$(grep $pattern $statefile | wc -l) |
||
| 451 | debug uid_count retrieved from statefile: $uid_count |
||
| 452 | elif [ "$target" = "ALL" ]; then |
||
| 453 | local pattern="-v TOTAL" |
||
| 454 | debug target=ALL: need pattern for all users but not TOTAL: $pattern |
||
| 455 | local uid="all users" |
||
| 456 | local uid_count=$(grep $pattern $statefile | wc -l) |
||
| 457 | debug uid_count retrieved from statefile: $uid_count |
||
| 458 | else |
||
| 459 | local pattern="\b$target\b" |
||
| 460 | debug target=$target: need pattern for a single user: $pattern |
||
| 461 | local uid=$(grep $pattern $statefile | cut -d' ' -f1) |
||
| 462 | debug retrieved uid value from statefile: $uid |
||
| 463 | local uid_count=1 |
||
| 464 | fi |
||
| 465 | |||
| 466 | echo "graph_title Processed messages for $uid" |
||
| 467 | e00b066f | dipohl | echo graph_category spamfilter |
| 468 | 429819cf | Tom Hendrikx | echo graph_args --base 1000 |
| 469 | 5d346e98 | jolan78 | [ "$graph" = absprocessed ] && echo graph_vlabel Messages |
| 470 | [ "$graph" = relprocessed ] && echo graph_vlabel Messages / minute |
||
| 471 | [ "$graph" = relprocessed ] && echo graph_period minute |
||
| 472 | 429819cf | Tom Hendrikx | echo graph_total Total |
| 473 | echo "graph_info This graph shows the messages that DSPAM processed for $uid ($uid_count uids). Messages are divided in the following categories: true positives/negatives, false positives/negatives, and corpusfed ham/spam." |
||
| 474 | echo tp.label True positives |
||
| 475 | echo tp.info Spam messages correctly classified as spam. |
||
| 476 | 5d346e98 | jolan78 | [ "$graph" = relprocessed ] && echo tp.type DERIVE |
| 477 | 429819cf | Tom Hendrikx | echo tp.draw AREASTACK |
| 478 | echo tn.label True negatives |
||
| 479 | echo tn.info Ham messages correctly classified as ham. |
||
| 480 | 5d346e98 | jolan78 | [ "$graph" = relprocessed ] && echo tn.type DERIVE |
| 481 | 429819cf | Tom Hendrikx | echo tn.draw AREASTACK |
| 482 | echo fp.label False positives |
||
| 483 | echo fp.info Ham messages incorrectly classified as spam, but corrected by the user. |
||
| 484 | 5d346e98 | jolan78 | [ "$graph" = relprocessed ] && echo fp.type DERIVE |
| 485 | 429819cf | Tom Hendrikx | echo fp.draw AREASTACK |
| 486 | echo fn.label False negatives |
||
| 487 | echo fn.info Spam messages incorrectly classified as ham, but corrected by the user. |
||
| 488 | 5d346e98 | jolan78 | [ "$graph" = relprocessed ] && echo fn.type DERIVE |
| 489 | 429819cf | Tom Hendrikx | echo fn.draw AREASTACK |
| 490 | echo sc.label Corpusfed spam |
||
| 491 | echo sc.info Spam messages from a collected corpus for training purposes. |
||
| 492 | 5d346e98 | jolan78 | [ "$graph" = relprocessed ] && echo sc.type DERIVE |
| 493 | 429819cf | Tom Hendrikx | echo sc.draw AREASTACK |
| 494 | echo nc.label Corpusfed ham |
||
| 495 | echo nc.info Ham messages from a collected corpus for training purposes. |
||
| 496 | 5d346e98 | jolan78 | [ "$graph" = relprocessed ] && echo nc.type DERIVE |
| 497 | 429819cf | Tom Hendrikx | echo nc.draw AREASTACK |
| 498 | ;; |
||
| 499 | |||
| 500 | *) |
||
| 501 | debug no config available for graph: $graph, exiting with error |
||
| 502 | exit 78 # EX_CONFIG |
||
| 503 | ;; |
||
| 504 | esac |
||
| 505 | debug finished with printing config for graph: $graph |
||
| 506 | } |
||
| 507 | |||
| 508 | # |
||
| 509 | # print_fetch |
||
| 510 | # Output for 'munin-run <plugin> fetch' command: the actual data to graph. |
||
| 511 | # |
||
| 512 | print_fetch() {
|
||
| 513 | debug printing fetch for graph: $graph |
||
| 514 | |||
| 515 | while file_is_locked $statefile; do |
||
| 516 | debug statefile is locked, waiting |
||
| 517 | sleep 1 |
||
| 518 | done |
||
| 519 | |||
| 520 | case $graph in |
||
| 521 | accuracy) |
||
| 522 | if [ -n "$pattern" ]; then |
||
| 523 | debug env.pattern was set, so use it: $pattern |
||
| 524 | continue |
||
| 525 | 8cf82404 | Tom Hendrikx | elif [ $target = "ALL" ]; then |
| 526 | 429819cf | Tom Hendrikx | local pattern="-v TOTAL" |
| 527 | debug target=ALL: need pattern for all users, but not for TOTAL: $pattern |
||
| 528 | else |
||
| 529 | local pattern="\b$target\b" |
||
| 530 | debug target=$target: need pattern for a single user: $pattern |
||
| 531 | fi |
||
| 532 | |||
| 533 | debug starting grep for user data in statefile |
||
| 534 | local t_start=$(date +%s) |
||
| 535 | grep $pattern $statefile | while read x clean_user x x x x x x x x oca x; do |
||
| 536 | echo $clean_user.value $oca |
||
| 537 | done |
||
| 538 | local t_end=$(date +%s) |
||
| 539 | debug grep finished, runtime $((t_end - t_start)) seconds |
||
| 540 | ;; |
||
| 541 | |||
| 542 | processed) |
||
| 543 | if [ -n "$pattern" ]; then |
||
| 544 | debug env.pattern was set, so use it: $pattern |
||
| 545 | continue |
||
| 546 | elif [ $target = "ALL" ]; then |
||
| 547 | local pattern="TOTAL" |
||
| 548 | debug target=ALL: need pattern for TOTAL of all users: $pattern |
||
| 549 | else |
||
| 550 | local pattern="\b$target\b" |
||
| 551 | debug target=$target: need pattern for a single user: $pattern |
||
| 552 | fi |
||
| 553 | |||
| 554 | local tmpfile=$(mktemp) || debug failed to create tmpfile && debug tmpfile created at: $tmpfile |
||
| 555 | debug starting grep for user data in statefile, sending to tmpfile |
||
| 556 | local t_start=$(date +%s) |
||
| 557 | grep $pattern $statefile > $tmpfile |
||
| 558 | local t_end=$(date +%s) |
||
| 559 | debug grep finished, runtime $((t_end - t_start)) seconds |
||
| 560 | |||
| 561 | debug starting loop over data in tmpfile |
||
| 562 | while read user x tp tn fp fn sc nc x; do |
||
| 563 | debug read data for user $user: $tp $tn $fp $fn $sc $nc |
||
| 564 | all_tp=$((all_tp + tp)) |
||
| 565 | all_tn=$((all_tn + tn)) |
||
| 566 | all_fp=$((all_fp + fp)) |
||
| 567 | all_fn=$((all_fn + fn)) |
||
| 568 | all_sc=$((all_sc + sc)) |
||
| 569 | all_nc=$((all_nc + nc)) |
||
| 570 | debug calculated new totals: $all_tp $all_tn $all_fp $all_fn $all_sc $all_nc |
||
| 571 | done < $tmpfile |
||
| 572 | debug finished data loop |
||
| 573 | rm -f $tmpfile || debug failed to remove tmpfile && debug removed tmpfile |
||
| 574 | |||
| 575 | local total=$((all_tp + all_tn + all_fp + all_fn + all_sc + all_nc)) |
||
| 576 | debug calculated total of all messages: $total |
||
| 577 | echo tp.value $(abs2perc $all_tp $total) |
||
| 578 | echo tn.value $(abs2perc $all_tn $total) |
||
| 579 | echo fp.value $(abs2perc $all_fp $total) |
||
| 580 | echo fn.value $(abs2perc $all_fn $total) |
||
| 581 | echo sc.value $(abs2perc $all_sc $total) |
||
| 582 | echo nc.value $(abs2perc $all_nc $total) |
||
| 583 | ;; |
||
| 584 | |||
| 585 | 5d346e98 | jolan78 | *processed) |
| 586 | 429819cf | Tom Hendrikx | if [ -n "$pattern" ]; then |
| 587 | debug env.pattern was set, so use it: $pattern |
||
| 588 | continue |
||
| 589 | elif [ $target = "ALL" ]; then |
||
| 590 | local pattern="TOTAL" |
||
| 591 | debug target=ALL: need pattern for TOTAL of all users: $pattern |
||
| 592 | else |
||
| 593 | local pattern="\b$target\b" |
||
| 594 | debug target=$target: need pattern for a single user: $pattern |
||
| 595 | fi |
||
| 596 | |||
| 597 | local tmpfile=$(mktemp) || debug failed to create tmpfile && debug tmpfile created at: $tmpfile |
||
| 598 | debug starting grep for user data in statefile, sending to tmpfile |
||
| 599 | local t_start=$(date +%s) |
||
| 600 | grep $pattern $statefile > $tmpfile |
||
| 601 | local t_end=$(date +%s) |
||
| 602 | debug grep finished, runtime $((t_end - t_start)) seconds |
||
| 603 | |||
| 604 | debug starting loop over data in tmpfile |
||
| 605 | while read user x tp tn fp fn sc nc x; do |
||
| 606 | debug read data for user $user: $tp $tn $fp $fn $sc $nc |
||
| 607 | all_tp=$((all_tp + tp)) |
||
| 608 | all_tn=$((all_tn + tn)) |
||
| 609 | all_fp=$((all_fp + fp)) |
||
| 610 | all_fn=$((all_fn + fn)) |
||
| 611 | all_sc=$((all_sc + sc)) |
||
| 612 | all_nc=$((all_nc + nc)) |
||
| 613 | debug calculated new totals: $all_tp $all_tn $all_fp $all_fn $all_sc $all_nc |
||
| 614 | done < $tmpfile |
||
| 615 | debug finished data loop |
||
| 616 | rm -f $tmpfile || debug failed to remove tmpfile && debug removed tmpfile |
||
| 617 | |||
| 618 | echo tp.value $all_tp |
||
| 619 | echo tn.value $all_tn |
||
| 620 | echo fp.value $all_fp |
||
| 621 | echo fn.value $all_fn |
||
| 622 | echo sc.value $all_sc |
||
| 623 | echo nc.value $all_nc |
||
| 624 | ;; |
||
| 625 | |||
| 626 | *) |
||
| 627 | debug no fetch available for graph: $graph, exiting with error |
||
| 628 | exit 78 # EX_CONFIG |
||
| 629 | ;; |
||
| 630 | esac |
||
| 631 | debug finished printing fetch for graph: $graph |
||
| 632 | } |
||
| 633 | |||
| 634 | |||
| 635 | ##################### |
||
| 636 | # Main process loop # |
||
| 637 | ##################### |
||
| 638 | |||
| 639 | # show env settings |
||
| 640 | debug dspam_ plugin started, pid=$$ |
||
| 641 | debug settings: |
||
| 642 | debug - dspam_stats is set to: $dspam_stats |
||
| 643 | debug - statefile is set to: $statefile |
||
| 644 | debug - statefile_max_age is set to: $statefile_max_age |
||
| 645 | debug - warning is set to: $warning |
||
| 646 | debug - critical is set to: $critical |
||
| 647 | debug - pattern is set to: $pattern |
||
| 648 | debug - description is set to: $description |
||
| 649 | |||
| 650 | command=$1 |
||
| 651 | [ -n "$command" ] || command="fetch" |
||
| 652 | debug - command is set to: $command |
||
| 653 | |||
| 654 | graph=$(basename $0 | cut -d'_' -f2) |
||
| 655 | debug - graph is set to: $graph |
||
| 656 | |||
| 657 | target=$(basename $0 | cut -d'_' -f3-) |
||
| 658 | [ -n "$target" ] || target="ALL" |
||
| 659 | debug - target is set to: $target |
||
| 660 | |||
| 661 | debug settings completed, starting process |
||
| 662 | |||
| 663 | case $command in |
||
| 664 | autoconf) |
||
| 665 | print_autoconf |
||
| 666 | ;; |
||
| 667 | suggest) |
||
| 668 | print_suggest |
||
| 669 | ;; |
||
| 670 | config) |
||
| 671 | update_statefile |
||
| 672 | print_config |
||
| 673 | ;; |
||
| 674 | fetch) |
||
| 675 | update_statefile |
||
| 676 | print_fetch |
||
| 677 | ;; |
||
| 678 | esac |
||
| 679 | |||
| 680 | debug exiting |
||
| 681 | exit 0 # EX_OK |
