More bug-fixes, GSASL DIGEST-MD5 now works.
[exim.git] / src / util / ratelimit.pl
1 #!/usr/bin/perl -wT
2
3 use strict;
4
5 sub usage () {
6 print <<END;
7 usage: ratelimit.pl <period> <regex> logfile
8
9 The aim of this script is to compute clients' peak sending rates
10 from an Exim log file, using the same formula as Exim's ratelimit
11 ACL condition. This is so that you can get an idea of a reasonable
12 limit setting before you deploy the restrictions.
13
14 This script isn't perfectly accurate, because the time stamps in
15 Exim's log files are only accurate to a second whereas internally
16 Exim computes sender rates to the accuracy of your computer's clock
17 (typically 10ms).
18
19 The log files to be processed can be specified on the command line
20 after the other arguments; if no filenames are specified the script
21 will read from stdin.
22
23 The first command line argument is the smoothing period, as defined by
24 the documentation for the ratelimit ACL condition. The second argumetn
25 is a regular expression.
26
27 Each line is matched against the regular expression. Lines that do not
28 match are ignored. The regex may contain 0, 1, or 2 () capturing
29 sub-expressions.
30
31 If there are no () sub-expressions, then every line that matches is
32 used to compute a single rate. Its maximum value is reported when the
33 script finishes.
34
35 If there is one () sub-expression, then the text matched by the
36 sub-expression is used to identify a rate lookup key, similar to the
37 lookup key used by the ratelimit ACL condition. For example, you might
38 write a regex to match the client IP address, or the authenticated
39 username. Separate rates are computed for each different client and
40 the maximum rate for each client is reported when the script finishes.
41
42 If there are two () sub-expressions, then the text matched by the
43 first sub-expression is used to identify a rate lookup key as above,
44 and the second is used to match the message size recorded in the log
45 line, e.g. " S=(\\d+) ". In this case the byte rate is computed instead
46 of the message rate, similar to the per_byte option of the ratelimit
47 ACL condition.
48 END
49 exit 1;
50 }
51
52 sub iso2unix (@) {
53 my ($y,$m,$d,$H,$M,$S,$zs,$zh,$zm) = @_;
54 use integer;
55 $y -= $m < 3;
56 $m += $m < 3 ? 10 : -2;
57 my $z = defined $zs ? "${zs}1" * ($zh * 60 + $zm) : 0;
58 my $t = $y/400 - $y/100 + $y/4 + $y*365
59 + $m*367/12 + $d - 719499;
60 return $t * 86400
61 + $H * 3600
62 + $M * 60
63 + $S
64 - $z;
65 }
66
67 my $debug = 0;
68 my $progress = 0;
69 while (@ARGV && $ARGV[0] =~ /^-\w+$/) {
70 $debug = 1 if $ARGV[0] =~ s/(-\w*)d(\w*)/$1$2/;
71 $progress = 1 if $ARGV[0] =~ s/(-\w*)p(\w*)/$1$2/;
72 shift if $ARGV[0] eq "-";
73 }
74
75 usage if @ARGV < 2;
76
77 my $progtime = "";
78
79 my $period = shift;
80
81 my $re_txt = shift;
82 my $re = qr{$re_txt}o;
83
84 my %time;
85 my %rate;
86 my %max;
87
88 sub debug ($) {
89 my $key = shift;
90 printf "%s\t%12d %8s %5.2f %5.2f\n",
91 $_, $time{$key}, $key, $max{$key}, $rate{$key};
92 }
93
94 while (<>) {
95 next unless $_ =~ $re;
96 my $key = $1 || "";
97 my $size = $2 || 1.0;
98 my $time = iso2unix
99 ($_ =~ m{^(\d{4})-(\d\d)-(\d\d)[ ]
100 (\d\d):(\d\d):(\d\d)[ ]
101 (?:([+-])(\d\d)(\d\d)[ ])?
102 }x);
103 if ($progress) {
104 my $prog_now = substr $_, 0, 14;
105 if ($progtime ne $prog_now) {
106 $progtime = $prog_now;
107 print "$progtime\n";
108 }
109 }
110 if (not defined $time{$key}) {
111 $time{$key} = $time;
112 $rate{$key} = 0.0;
113 $max{$key} = 0.0;
114 debug $key if $debug;
115 next;
116 }
117 # see acl_ratelimit() for details of the following
118 my $interval = $time - $time{$key};
119 $interval = 1e-9 if $interval <= 0.0;
120 my $i_over_p = $interval / $period;
121 my $a = exp(-$i_over_p);
122 $time{$key} = $time;
123 $rate{$key} = $size * (1.0 - $a) / $i_over_p + $a * $rate{$key};
124 $max{$key} = $rate{$key} if $rate{$key} > $max{$key};
125 debug $key if $debug;
126 }
127
128 print map {
129 " " x (20 - length) .
130 "$_ : $max{$_}\n"
131 } sort {
132 $max{$a} <=> $max{$b}
133 } keys %max;
134
135 # eof