1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
#!/usr/bin/perl
=head1 NAME
loadcheck
=head1 DESCRIPTION
Only takes email transactions if the system load is at or below a
specified level.
If this is running on a system that provides /kern/loadavg or
/proc/loadavg it will be used instead of the 'uptime' command.
Once a load value is determined, it is cached for a period of time.
See the cache_time below.
Since fork/exec is expensive in perl, if using the 'uptime' method,
use cache_time to avoid increasing your load on every connection.
=head1 CONFIG
max_load
This is the 1 minute system load where we won't take transactions
if our load is higher than this value. (Default: 7)
cache_time
A recently determined load value will be cached and used for the
assigned number of seconds. (Default: 10)
uptime
The path to the command 'uptime' if different than the default.
(Default: /usr/bin/uptime)
Example:
loadcheck cache_time 30
loadcheck max_load 7 uptime /usr/bin/uptime
=head1 SEE ALSO
Original version: http://www.nntp.perl.org/group/perl.qpsmtpd/2006/01/msg4422.html
Variant with caching: http://www.nntp.perl.org/group/perl.qpsmtpd/2006/03/msg4710.html
Steve Kemp's announcement of an alternate load limiter: http://www.nntp.perl.org/group/perl.qpsmtpd/2008/03/msg7814.html
=head1 AUTHOR
Written by Peter Eisch <peter@boku.net>.
=head1 CHANGES
v0.03 - msimerson - 2014-03-21
* refactored "find the way to get load avg" out of loadcheck (every
connection) into get_load_method which is run in register. If we can't
get the load average, don't register the hook.
* added BSD::getloadavg method (tested on FreeBSD)
v0.02 - github@rsiddall - resurrected from list archives
=cut
my $VERSION = 0.03;
sub register {
my ($self, $qp, @args) = @_;
$self->{_args} = { @args };
$self->{_args}{max_load} ||= 7;
$self->{_args}{uptime} ||= '/usr/bin/uptime';
$self->{_args}{cache_time} ||= 10;
$self->{_load} = -1;
$self->{_time} = 0;
$self->{_method} = $self->get_load_method();
# only register the hook if we can measure load
if (ref $self->{_method} eq 'CODE') {
$self->register_hook("connect", "loadcheck");
}
}
sub loadcheck {
my ($self, $transaction) = @_;
if (time() > ($self->{_time} + $self->{_args}{cache_time})) {
# cache value expired, update
$self->{_method}->();
$self->{_time} = time();
};
if ($self->{_load} > $self->{_args}{max_load}) {
$self->log(LOGERROR, "local load too high: $self->{_load}");
return (DENYSOFT, "Server load too high, please try again later.");
}
return (DECLINED, "continuing with load: $self->{_load}");
}
sub get_load_method {
my ($self) = @_;
eval "use BSD::getloadavg;";
if (!$@) {
return sub {
require BSD::getloadavg;
$self->{_load} = (getloadavg())[0];
$self->log(LOGDEBUG, "BSD::getloadavg reported: $self->{_load}");
}
}
if (-r '/kern/loadavg') { # *BSD
return sub {
open(LD, '<', "/kern/loadavg"); # contains fix-point scaling value
my $res = <LD>;
close LD;
my @vals = split(/ /, $res);
$self->{_load} = ($vals[0] / $vals[3]);
$self->log(LOGDEBUG, "/kern/loadavg reported: $self->{_load}");
}
}
if (-r '/proc/loadavg') { # *inux
return sub {
open(LD, "<", "/proc/loadavg"); # contains decimal value
my $res = <LD>; # contains fix-point scaling value
close LD;
$self->{_load} = (split(/ /, $res))[0];
$self->log(LOGDEBUG, "/proc/loadavg reported: $self->{_load}");
}
}
if (-x $self->{_args}{uptime}) {
return sub {
# the various formats returned:
# 10:33AM up 2:06, 1 user, load averages: 6.55, 3.76, 2.48
# 12:29am 2 users, load average: 0.05, 0.05, 0.06
# 12:30am up 5 days, 12:43, 1 user, load average: 0.00, 0.00, 0.00
my $res = `$self->{_args}{uptime}`;
if ($res =~ /aver\S+: (\d+\.\d+)/) {
$self->{_load} = $1;
$self->log(LOGDEBUG, "$self->{_args}{uptime} reported: $self->{_load}");
}
}
}
$self->log(LOGERROR, "unable to acquire system load");
return;
};
|