Make sure rc is initialised
[exim.git] / src / util / ratelimit.pl
CommitLineData
7546de58 1#!/usr/bin/perl -wT
7546de58
TF
2
3use strict;
4
5sub usage () {
6 print <<END;
7usage: ratelimit.pl <period> <regex> logfile
8
9The aim of this script is to compute clients' peak sending rates
10from an Exim log file, using the same formula as Exim's ratelimit
11ACL condition. This is so that you can get an idea of a reasonable
12limit setting before you deploy the restrictions.
13
14This script isn't perfectly accurate, because the time stamps in
94ebd6cb
TF
15Exim's log files are only accurate to a second whereas internally
16Exim computes sender rates to the accuracy of your computer's clock
7546de58
TF
17(typically 10ms).
18
19The log files to be processed can be specified on the command line
20after the other arguments; if no filenames are specified the script
21will read from stdin.
22
23The first command line argument is the smoothing period, as defined by
24the documentation for the ratelimit ACL condition. The second argumetn
25is a regular expression.
26
27Each line is matched against the regular expression. Lines that do not
28match are ignored. The regex may contain 0, 1, or 2 () capturing
29sub-expressions.
30
31If there are no () sub-expressions, then every line that matches is
32used to compute a single rate. Its maximum value is reported when the
33script finishes.
34
35If there is one () sub-expression, then the text matched by the
36sub-expression is used to identify a rate lookup key, similar to the
37lookup key used by the ratelimit ACL condition. For example, you might
38write a regex to match the client IP address, or the authenticated
39username. Separate rates are computed for each different client and
40the maximum rate for each client is reported when the script finishes.
41
42If there are two () sub-expressions, then the text matched by the
43first sub-expression is used to identify a rate lookup key as above,
44and the second is used to match the message size recorded in the log
45line, e.g. " S=(\\d+) ". In this case the byte rate is computed instead
46of the message rate, similar to the per_byte option of the ratelimit
47ACL condition.
48END
49 exit 1;
50}
51
52sub 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
67my $debug = 0;
68my $progress = 0;
69while (@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
75usage if @ARGV < 2;
76
77my $progtime = "";
78
79my $period = shift;
80
81my $re_txt = shift;
82my $re = qr{$re_txt}o;
83
84my %time;
85my %rate;
86my %max;
87
88sub 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
94while (<>) {
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};
e5d5a95f 119 $interval = 1e-9 if $interval <= 0.0;
7546de58
TF
120 my $i_over_p = $interval / $period;
121 my $a = exp(-$i_over_p);
7546de58
TF
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
128print map {
129 " " x (20 - length) .
130 "$_ : $max{$_}\n"
131} sort {
132 $max{$a} <=> $max{$b}
133} keys %max;
134
135# eof