Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / jchkmail / jchkmail_counters_ @ 17f78427

Historique | Voir | Annoter | Télécharger (17,6 ko)

1
#!/usr/bin/perl
2
# -*- perl -*-
3

    
4
=pod
5

    
6
=encoding UTF-8
7

    
8
=head1 NAME
9

    
10
  jchkmail_counters_
11

    
12
=head1 APPLICABLE SYSTEMS
13

    
14
  MTAs running j-chkmail mail filter - http://www.j-chkmail.org
15

    
16
=head1 CONFIGURATION
17

    
18
  [jchkmail_counters_*]
19
    user root
20

    
21
The following environment variables may be defined :
22

    
23
    # disable some instances of the plugin
24
    env.disable    no
25

    
26
=head1 BUGS/GOTCHAS
27

    
28
  None known, but some improvements to do...
29

    
30
=head1 AUTHOR
31

    
32
    Jose-Marcio Martins da Cruz - mailto:Jose-Marcio.Martins@mines-paristech.fr
33
    Ecole Nationale Superieure des Mines de Paris
34

    
35
  This plugin was inspired by the work of Christophe Decor
36
    Christophe Decor - mailto:decor@supagro.inra.fr
37
    INRA - Institut National de Recherche Agronomique
38

    
39
=head1 COPYRIGHT
40
  Copyright Jose-Marcio Martins da Cruz
41

    
42
=head1 VERSION
43

    
44
  1.0 - Feb 2014
45

    
46
=head1 MAGIC MARKERS
47

    
48
 #%# family=contrib
49
 #%# capabilities=autoconf suggest
50

    
51
=cut
52

    
53
use strict;
54
use warnings;
55

    
56
use lib $ENV{'MUNIN_LIBDIR'};
57
use Munin::Plugin;
58

    
59
my $SMCF     = "/etc/mail/jchkmail/j-chkmail.cf";
60
my $FSTATS   = "/var/jchkmail/files/j-stats";
61
my $WORKDIR  = "/var/jchkmail/wdb";
62
my $CONSTDIR = "/var/jchkmail/cdb";
63

    
64
# ------------------------------------------------
65
#
66
#
67
my $debug = 0;
68

    
69
my $AppName = $0;
70
$AppName = `basename $AppName`;
71
chomp $AppName;
72

    
73
my $AppExt = undef;
74
if ($AppName =~ /_([a-z0-9.-]+)$/i) {
75
  $AppExt = $1;
76
}
77

    
78
my $disable = defined $ENV{'disable'} &&  $ENV{'disable'} =~ /yes|oui/i;
79

    
80
my %StaticData = ();
81
GetStaticData(\%StaticData);
82

    
83
my $AppType = "";
84
if (defined $AppExt && exists $StaticData{config}{"$AppExt+type"}) {
85
  $AppType = $StaticData{config}{"$AppExt+type"};
86
}
87

    
88
# valider results from Munin::Plugin
89
# statefile is used only by jchkmail_counters_ratiospamham
90
my $StateFile = undef;
91
if (exists $ENV{MUNIN_PLUGSTATE} && defined $ENV{MUNIN_PLUGSTATE}) {
92
  $StateFile = "$ENV{MUNIN_PLUGSTATE}/$AppName.state-joe";
93
}
94
if (exists $ENV{statefile} && defined $ENV{statefile}) {
95
  $StateFile = $ENV{statefile};
96
}
97

    
98
# ------------------------------------------------
99
#
100
#
101
if ($#ARGV >= 0 && $ARGV[0] eq "autoconf") {
102

    
103
  unless (-f $SMCF) {
104
    print "no\n";
105
    exit 0;
106
  }
107
  print "yes\n";
108
  exit 0;
109
}
110

    
111
# ------------------------------------------------
112
#
113
#
114
if ($#ARGV >= 0 && $ARGV[0] eq "suggest") {
115

    
116
  # results from /var/jchkmail/files/j-stats
117
  if (-f $SMCF && -f $FSTATS) {
118
    my %Data = ();
119
    my $line = GetStatsLine(\%Data);
120
    exit 1 unless defined $line && $line ne "";
121

    
122
    foreach my $k (keys %{$StaticData{config}}) {
123
      next unless $k =~ /(.*)[+]type$/;
124
      my $graph = $1;
125
      my $type  = $StaticData{config}{$k};
126
      if ($type =~ /^(counters|ratio)$/) {
127
        next unless exists $StaticData{config}{$graph . "+data"};
128
        my $data = $StaticData{config}{$graph . "+data"};
129
        $data =~ tr#,/# #;
130
        my @fields = split " ", $data;
131
        my $ok     = 1;
132
        my $bad    = "";
133
        for my $f (@fields) {
134
          unless (exists $Data{$f}) {
135
            $ok = 0;
136
            $bad .= " $f";
137
          }
138
        }
139
        if ($ok) {
140
          print "$graph\n" if $ok;
141
        } else {
142
          printf "# %-12s - Lacking fields : %s\n", $graph, $bad;
143
        }
144
      }
145
    }
146
  }
147

    
148
  # results from /var/jchkmail/wdb
149
  if (-f $SMCF && (-d $WORKDIR || -d $CONSTDIR)) {
150
    foreach my $k (keys %{$StaticData{config}}) {
151
      next unless $k =~ /(.*)[+]type$/;
152
      my $graph = $1;
153
      my $type  = $StaticData{config}{$k};
154
      if ($type =~ /^(dbcounters)$/) {
155
        next unless exists $StaticData{config}{$graph . "+data"};
156
        my $data = $StaticData{config}{$graph . "+data"};
157
        $data =~ tr#,/# #;
158
        my @fields = split " ", $data;
159
        my $ok     = 1;
160
        my $bad    = "";
161
        for my $f (@fields) {
162
          unless (-f "$WORKDIR/$f.db" || -f "$CONSTDIR/$f.db") {
163
            $ok = 0;
164
            $bad .= " $f";
165
          }
166
        }
167
        if ($ok) {
168
          print "$graph\n" if $ok;
169
        } else {
170
          printf "# %-12s - Lacking fields : %s\n", $graph, $bad;
171
        }
172
      }
173
    }
174
  }
175
  exit 0;
176
}
177

    
178
# ------------------------------------------------
179
#
180
#
181
if (defined $AppExt && $AppType eq "counters") {
182
  my %Data = ();
183
  my $line = GetStatsLine(\%Data);
184
  exit 1 unless defined $line && $line ne "";
185

    
186
  my $DataAggregate = $StaticData{config}{"$AppExt+data"};
187
  printf "# Data Aggregate : %s\n", $DataAggregate;
188
  my @Values = split " ", $DataAggregate;
189
  if ($#ARGV >= 0 && $ARGV[0] eq "config") {
190
    my @Keys = grep {$_ =~ /^$AppExt-/;} keys %{$StaticData{config}};
191
    foreach my $k (@Keys) {
192
      my $v = $StaticData{config}{$k};
193
      $k =~ s/^$AppExt-//;
194
      printf "%-16s %s\n", $k, $v;
195
    }
196
    printf "graph_order %s\n", (join " ", sort @Values);
197
    foreach my $k (@Values) {
198
      if (exists $Data{$k}) {
199
        my $label = $k;
200
        if (exists $StaticData{label}{$k}) {
201
          $label = $StaticData{label}{$k};
202
        }
203
        printf "%s.label %s\n", $k, $label;
204
        if (exists $StaticData{label}{"$k.info"}) {
205
          $label = $StaticData{label}{"$k.info"};
206
        }
207
        printf "%s.info %s\n", $k, $label;
208
        printf "%s.min 0\n", $k;
209
        printf "%s.type DERIVE\n", $k;
210
      }
211
    }
212
    exit 0;
213
  }
214

    
215
  if ($#ARGV < 0) {
216
    foreach my $k (@Values) {
217
      if (exists $Data{$k}) {
218
        printf "%s.value %s\n", $k, $Data{$k};
219
      }
220
    }
221
    exit 0;
222
  }
223
  exit 1;
224
}
225

    
226
# ------------------------------------------------
227
#
228
#
229
if (defined $AppExt && $AppType eq "ratio") {
230
  my %Data = ();
231

    
232
  my $line = GetStatsLine(\%Data);
233
  exit 1 unless defined $line && $line ne "";
234

    
235
  my $DataAggregate = $StaticData{config}{"$AppExt+data"};
236
  printf "# Data Aggregate : %s\n", $DataAggregate;
237
  my ($Ratios, $Base, undef) = split "/", $DataAggregate;
238
  exit 1 unless defined $Ratios && defined $Base;
239

    
240
  my @Values = split ",",    $Ratios;
241
  my @Den    = split "[ ]+", $Base;
242

    
243
  if ($#ARGV >= 0 && $ARGV[0] eq "config") {
244
    my @Keys = grep {$_ =~ /^$AppExt-/;} keys %{$StaticData{config}};
245
    foreach my $k (@Keys) {
246
      my $v = $StaticData{config}{$k};
247
      $k =~ s/^$AppExt-//;
248
      printf "%-16s %s\n", $k, $v;
249
    }
250

    
251
    my $nr = 0;
252
    printf "graph_order %s\n", (join " ", sort @Values);
253
    foreach my $num (@Values) {
254
      printf "# running for %s\n", $num;
255
      my @Num = split "[ ]+", $num;
256

    
257
      my $k = sprintf "%s%02d", $AppExt, $nr++;
258
      my $label = $k;
259
      if (exists $StaticData{label}{$k}) {
260
        $label = $StaticData{label}{$k};
261
      }
262
      printf "%s.label %s\n", $k, $label;
263
      if (exists $StaticData{label}{"$k.info"}) {
264
        $label = $StaticData{label}{"$k.info"};
265
      }
266
      printf "%s.info %s\n", $k, $label;
267
      printf "%s.min 0\n",   $k;
268
      printf "%s.max 100\n", $k;
269
      printf "%s.type GAUGE\n", $k;
270
    }
271
    exit 0;
272
  }
273

    
274
  if ($#ARGV < 0) {
275
    my %Old = ();
276
    my %Diff = ();
277
    if (EvalBoolean($StaticData{config}{"$AppExt+state"})) {
278
      ReadState(\%Old);
279
      %Diff = ();
280
      DiffFromState(\%Old, \%Data, \%Diff);
281
    } else {
282
      %Diff = %Data;
283
    }
284
    if ($debug) {
285
      foreach my $k (sort keys %Data) {
286
        printf "# DIFF %-16s %12d %12d %12d\n", $k, $Old{$k}, $Data{$k},
287
          $Diff{$k};
288
      }
289
    }
290
    my $nr = 0;
291
    foreach my $num (@Values) {
292
      printf "# running for %s\n", $num;
293
      my @Num = split "[ ]+", $num;
294
      my $n   = 0;
295
      my $d   = 0;
296
      foreach my $k (@Num) {
297
        if (exists $Diff{$k}) {
298
          $n += $Diff{$k};
299
        }
300
      }
301
      foreach my $k (@Den) {
302
        if (exists $Diff{$k}) {
303
          $d += $Diff{$k};
304
        }
305
      }
306
      my $k = sprintf "%s%02d", $AppExt, $nr++;
307
      printf "%s.value %s\n", $k, $d > 0 ? ((100. * $n) / $d) : 0;
308
    }
309
    if (EvalBoolean($StaticData{config}{"$AppExt+state"})) {
310
      SaveState(\%Data);
311
    }
312
    exit 0;
313
  }
314
  exit 1;
315
}
316

    
317
# ------------------------------------------------
318
#
319
#
320
if (defined $AppExt && $AppType eq "dbcounters") {
321
  my $DataAggregate = $StaticData{config}{"$AppExt+data"};
322
  printf "# Data Aggregate : %s\n", $DataAggregate;
323
  my @Values = split " ", $DataAggregate;
324
  if ($#ARGV >= 0 && $ARGV[0] eq "config") {
325
    my @Keys = grep {$_ =~ /^$AppExt-/;} keys %{$StaticData{config}};
326
    foreach my $k (@Keys) {
327
      my $v = $StaticData{config}{$k};
328
      $k =~ s/^$AppExt-//;
329
      printf "%-16s %s\n", $k, $v;
330
    }
331
    printf "graph_order %s\n", (join " ", sort @Values);
332
    foreach my $k (@Values) {
333
      if (1) {
334
        my $label = $k;
335
        if (exists $StaticData{label}{$k}) {
336
          $label = $StaticData{label}{$k};
337
        }
338
        printf "%s.label %s\n", $k, $label;
339
        if (exists $StaticData{label}{"$k.info"}) {
340
          $label = $StaticData{label}{"$k.info"};
341
        }
342
        printf "%s.info %s\n", $k, $label;
343
        printf "%s.type GAUGE\n", $k;
344
      }
345
    }
346
    exit 0;
347
  }
348

    
349
  if ($#ARGV < 0) {
350
    foreach my $k (@Values) {
351
      my $dbfile = "$WORKDIR/$k.db";
352
      $dbfile = "$CONSTDIR/$k.db" unless -f $dbfile;
353
      next unless -f $dbfile;
354
      my @DATA = `j-makemap -c -b $dbfile`;
355
      chomp @DATA;
356
      my $nrec = 0;
357
      foreach my $line (@DATA) {
358
        if ($line =~ /\s+(\d+)\s+records/) {
359
          $nrec = $1;
360
          printf "%s.value %s\n", $k, $nrec;
361
          last;
362
        }
363
      }
364
    }
365
    exit 0;
366
  }
367
  exit 1;
368
}
369

    
370
# ------------------------------------------------
371
#
372
#
373
sub GetStatsLine {
374
  my ($h, undef) = @_;
375
  return undef unless -f $FSTATS;
376
  my $line = `tail -1 $FSTATS`;
377
  return undef unless defined $line && $line ne "";
378

    
379
  my @Values = split / /, $line;
380
  my $time = time();
381
  if ($Values[0] =~ /^\d+$/) {
382
    $time = shift @Values;
383
  }
384
  $h->{timestamp} = $time;
385
  foreach my $lx (@Values) {
386
    $lx = lc $lx;
387
    if ($lx =~ /(\S+)=\((\d+)\)/) {
388
      $h->{$1} = $2;
389
    }
390
  }
391
  return $line;
392
}
393

    
394
# ------------------------------------------------
395
#
396
#
397
sub ReadState {
398
  my ($h, undef) = @_;
399
  return 0 unless defined $h;
400

    
401
  return 0 unless defined $StateFile && $StateFile ne "" && -f $StateFile;
402

    
403
  open FSTATE, "< $StateFile" || return 0;
404
  while (my $s = <FSTATE>) {
405
    chomp $s;
406
    next if $s =~ /^\s*$/;
407
    next if $s =~ /^\s*#/;
408
    my ($a, $b) = split " ", $s;
409
    $b = 0 unless defined $b;
410
    $h->{$a} = $b;
411
  }
412
  close FSTATE;
413
  {
414
    my %St = Munin::Plugin::restore_state();
415
    foreach my $k (sort keys %St) {
416
      printf "# %-20s %s\n", $k, $St{$k};
417
    }
418
  }
419
  return 1;
420
}
421

    
422
# ------------------------------------------------
423
#
424
#
425
sub SaveState {
426
  my ($h, undef) = @_;
427

    
428
  return 0 unless defined $StateFile && $StateFile ne "";
429
  my @ST = qw();
430
  open FSTATE, "> $StateFile" || return 0;
431
  foreach my $k (keys %{$h}) {
432
    push @ST, (sprintf "%s %s\n", $k, $h->{$k});
433
    printf FSTATE "%-20s %s\n", $k, $h->{$k};
434
  }
435
  close FSTATE;
436

    
437
  Munin::Plugin::save_state(%$h);
438
  return 1;
439
}
440

    
441
# ------------------------------------------------
442
#
443
#
444
sub DiffFromState {
445
  my ($old, $new, $delta, undef) = @_;
446
  return 0 unless defined $old && defined $new;
447
  $delta = $new unless defined $delta;
448

    
449
  return 0 unless exists $old->{timestamp};
450
  return 0 unless exists $new->{timestamp};
451
  my $dt = $new->{timestamp} - $old->{timestamp};
452
  return 0 unless $dt > 1;
453

    
454
  $delta->{timestamp} = $new->{timestamp};
455
  foreach my $k (sort keys %{$new}) {
456
    next if $k eq "timestamp";
457
    if (exists $old->{$k}) {
458
      $delta->{$k} = $new->{$k} - $old->{$k};
459
      $delta->{$k} *= 300. / $dt;
460
    } else {
461
      $delta->{$k} = 0.;
462
    }
463
  }
464
  return 1;
465
}
466

    
467
# ------------------------------------------------
468
#
469
#
470
sub GetStaticData {
471
  my ($h, undef) = @_;
472
  my @DATA = <DATA>;
473
  my @SOPT = qw();
474
  chomp @DATA;
475
  my $in  = 0;
476
  my $key = "";
477
  foreach my $s (@DATA) {
478
    if ($key eq "") {
479
      if ($s =~ /==\s+BEGIN\s+(\S+)\s+==/) {
480
        $key = $1;
481
      }
482
      next;
483
    }
484
    if ($s =~ /==\s+END\s+(\S*\s+)?==/) {
485
      $key = "";
486
      next;
487
    }
488
    next if $s =~ /^\s*$/;
489
    push @{$h->{$key}{_data_}}, $s;
490
    my ($a, $b) = split " ", $s, 2;
491
    next unless defined $a && $a ne "";
492
    $b = "" unless defined $b;
493
    $h->{$key}{$a} = $b;
494
  }
495
}
496

    
497
# ------------------------------------------------
498
#
499
#
500
sub EvalBoolean {
501
  my ($v, undef) = @_;
502

    
503
  # return defined $v && $v =~ /^(1|yes|true|oui|vrai)$/i;
504
  if (defined $v) {
505
    return 1 if $v =~ /^(1|yes|true|oui|vrai)$/i;
506
  }
507
  return 0;
508
}
509

    
510
# ------------------------------------------------
511
#
512
#
513
__DATA__
514

    
515
== BEGIN label ==
516
conn                      Connections
517
conn.info                 SMTP Connections per time unit
518
msgs                      Messages
519
msgs.info                 Messages per time unit
520
rcpt                      Recipients
521
rcpt.info                 Recipients per time unit
522

    
523
greymsgs                  Greylisted Messages
524
greymsgs.info             Greylisted Messages per time unit
525
greyrcpt                  Greylisted Recipients
526
greyrcpt.info             Greylisted Recipients per time unit
527

    
528
bayesham                  Legitimate (Stat Filter)
529
bayesham.info             Legitimate Messages - Statistical Filter
530
bayesspam                 Unwanted (Stat Filter)
531
bayesspam.info            Unwanted Messages - Statistical Filter
532

    
533
matching                  Pattern Matching
534
matching.info             Unwanted Messages - Pattern Matching Filter
535
urlbl                     URL Blacklist
536
urlbl.info                Unwanted Messages - URL Blacklist
537

    
538
throttle                  Connection Rate
539
throttle.info             Connections rejected by connection rate
540
badrcpt                   Bad Recipients
541
badrcpt.info              Connections rejected - excessive bad recipients
542
localuser                 Internal addresses
543
localuser.info            Messages sent to internal use only addresses
544
badmx                     Bad Sender MX
545
badmx.info                Messages rejected - Bad Sender MX
546
rcptrate                  Recipient Rate
547
rcptrate.info             Messages rejected by recipient rate
548

    
549
files                     Files
550
files.info                Files
551
xfiles                    X-Files
552
xfiles.info               X-Files
553

    
554
kbytes                    Volume (KBytes)
555
kbytes.info               Volume (KBytes) per time unit
556

    
557
j-greyvalid               Grey Validated records
558
j-greyvalid.info          Grey Validated records
559
j-greypend                Grey Waiting records
560
j-greypend.info           Grey Waiting records
561
j-greywhitelist           Grey Whitelisted records
562
j-greywhitelist.info      Grey Whitelisted records
563
j-greyblacklist           j-greyblacklist
564
j-greyblacklist.info      j-greyblacklist
565

    
566
ratiospamham              Ratio Messages
567
ratiospamham.info         Ratio Messages
568
ratiospamham00            Legitimate messages
569
ratiospamham00.info       Legitimate messages
570
ratiospamham01            Unwanted messages
571
ratiospamham01.info       Unwanted messages
572

    
573
== END label ==
574

    
575

    
576
== BEGIN config ==
577
activity-graph_title        j-chkmail - Filter activity
578
activity-graph_category     mail
579
activity-graph_vlabel       counts per second
580
activity-graph_scale        no
581
activity+data               conn msgs rcpt
582
activity+type               counters
583
activity+enable             yes
584

    
585
greylisting-graph_title     j-chkmail - Greylisting activity
586
greylisting-graph_category  mail
587
greylisting-graph_vlabel    counts per second
588
greylisting-graph_scale     no
589
greylisting+data            greymsgs greyrcpt
590
greylisting+type            counters
591
greylisting+enable          yes
592

    
593
statfilter-graph_title      j-chkmail - Statistical classification
594
statfilter-graph_category   mail
595
statfilter-graph_vlabel     counts per second
596
statfilter-graph_scale      no
597
statfilter+data             bayesham bayesspam
598
statfilter+type             counters
599
statfilter+enable           yes
600

    
601
contentfilter-graph_title      j-chkmail - Content Filtering
602
contentfilter-graph_category   mail
603
contentfilter-graph_vlabel     counts per second
604
contentfilter-graph_scale      no
605
contentfilter+data             bayesspam matching urlbl
606
contentfilter+type             counters
607
contentfilter+enable           yes
608

    
609
behaviour-graph_title       j-chkmail - Behaviour filtering - rejections
610
behaviour-graph_category    mail
611
behaviour-graph_vlabel      counts per second
612
behaviour-graph_scale       no
613
behaviour+data              throttle badrcpt localuser badmx rcptrate
614
behaviour+type              counters
615
behaviour+enable            yes
616

    
617
xfiles-graph_title          j-chkmail - Suspected files filtering
618
xfiles-graph_category       mail
619
xfiles-graph_vlabel         counts per second
620
xfiles-graph_scale          no
621
xfiles+data                 files xfiles
622
xfiles+type                 counters
623
xfiles+enable               yes
624

    
625
volume-graph_title          j-chkmail - Volume handled by the filter
626
volume-graph_category       mail
627
volume-graph_vlabel         KBytes per second
628
volume-graph_scale          no
629
volume+data                 kbytes
630
volume+type                 counters
631
volume+enable               yes
632

    
633
ratiospamham-graph_title       j-chkmail - Statistical classification
634
ratiospamham-graph_category    mail
635
ratiospamham-graph_vlabel      Ratio (%)
636
ratiospamham-graph_scale       yes
637
ratiospamham-graph_args        --lower-limit 0 --upper-limit 100 --rigid
638
ratiospamham+data              bayesham, bayesspam / bayesham bayesspam
639
ratiospamham+type              ratio
640
ratiospamham+state             yes
641
ratiospamham+enable            yes
642

    
643
greydb-graph_title             j-chkmail - Greylisting Database Size
644
greydb-graph_category          mail
645
greydb-graph_vlabel            records
646
greydb-graph_scale             no
647
greydb-graph_args              --lower-limit 0
648
greydb+data                    j-greypend j-greyvalid j-greywhitelist
649
greydb+type                    dbcounters
650
greydb+enable                  yes
651

    
652
cdb-graph_title                j-chkmail - Static databases
653
cdb-graph_category             mail
654
cdb-graph_vlabel               records
655
cdb-graph_scale                no
656
cdb-graph_args                 --lower-limit 0
657
cdb+data                       j-policy j-urlbl j-bayes j-rcpt
658
cdb+type                       dbcounters
659
cdb+enable                     yes
660

    
661
== END config ==