File: loadcheck

package info (click to toggle)
qpsmtpd 0.94-4
  • links: PTS
  • area: main
  • in suites: bullseye, buster
  • size: 2,284 kB
  • sloc: perl: 17,176; sh: 543; makefile: 186; sql: 100
file content (158 lines) | stat: -rw-r--r-- 4,380 bytes parent folder | download | duplicates (4)
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;
};