]>
Commit | Line | Data |
---|---|---|
a8801b42 JR |
1 | #!/usr/bin/perl -w |
2 | # -*- perl -*- | |
3 | ||
4 | =head1 NAME | |
5 | ||
6 | postfix_mailstats - Plugin to monitor the number of mails delivered and | |
7 | rejected by postfix | |
8 | ||
9 | =head1 CONFIGURATION | |
10 | ||
11 | Configuration parameters for /etc/munin/postfix_mailstats, | |
12 | if you need to override the defaults below: | |
13 | ||
14 | [postfix_mailstats] | |
15 | env.logdir - Which logfile to use | |
16 | env.logfile - What file to read in logdir | |
17 | ||
18 | =head2 DEFAULT CONFIGURATION | |
19 | ||
20 | [postfix_mailstats] | |
21 | env.logdir /var/log | |
22 | env.logfile mail.log | |
23 | ||
24 | =head1 AUTHOR | |
25 | ||
26 | Records show that the plugin was contributed by Nicolai Langfeldt in | |
27 | 2003. Nicolai can't find anything in his email about this and expects | |
28 | the plugin is based on the corresponding exim plugin - to which it now | |
29 | bears no resemblence. | |
30 | ||
31 | =head1 LICENSE | |
32 | ||
33 | GPLv2 | |
34 | ||
35 | =head1 MAGIC MARKERS | |
36 | ||
37 | =begin comment | |
38 | ||
39 | These magic markers are used by munin-node-configure when installing | |
40 | munin-node. | |
41 | ||
42 | =end comment | |
43 | ||
44 | #%# family=manual | |
45 | #%# capabilities=autoconf | |
46 | ||
47 | =head1 RANDOM COMMENTS | |
48 | ||
49 | Would be cool if someone ported this to Munin::Plugin for both state | |
50 | file and log tailing. | |
51 | ||
52 | =cut | |
53 | ||
54 | use strict; | |
55 | ||
56 | use Munin::Plugin; | |
57 | ||
58 | my $statefile = $ENV{'MUNIN_PLUGSTATE'} . "/munin-plugin-postfix_mailstats.state"; | |
59 | my $pos; | |
26507458 JR |
60 | my %descriptions = ( |
61 | messages => 'Number of messages', | |
62 | recipients => 'Number of recipients', | |
63 | local => 'Outgoing local messages', | |
64 | smtp => 'Outgoing SMTP messages', | |
65 | pickup => 'Incoming local messages', | |
66 | smtpd => 'Incoming SMTP messages', | |
67 | bounce => 'Bounces handled', | |
68 | ); | |
69 | my %stats = map { $_ => 0 } keys %descriptions; | |
a8801b42 | 70 | my %rejects = (); |
26507458 JR |
71 | |
72 | if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { | |
73 | my $logfile; | |
74 | `which postconf >/dev/null 2>/dev/null`; | |
75 | if (!$?) { # if postconf returns success | |
76 | `journalctl --facility=mail --show-cursor -n 0`; | |
77 | if (!$?) { # if journalctl returns success | |
78 | print "yes\n"; | |
79 | } else { | |
80 | print "no (journalctl returned error)\n"; | |
81 | } | |
82 | } else { | |
83 | print "no (postfix not found)\n"; | |
84 | } | |
85 | ||
86 | exit 0; | |
a8801b42 JR |
87 | } |
88 | ||
89 | ||
26507458 JR |
90 | if ( -f $statefile) { |
91 | open (IN, '<', $statefile) or die "Unable to open state-file: $!\n"; | |
92 | chomp($pos = <IN>); | |
93 | chomp(my $stat_line = <IN>); | |
94 | for my $stat (split /:/, $stat_line) { | |
95 | my ($k, $v) = split /=/, $stat, 2; | |
96 | $stats{$k} = $v; | |
97 | } | |
98 | while (<IN>) { | |
99 | if (/^([0-9a-z.\-]+):(\d+)$/) { | |
100 | $rejects{$1} = $2; | |
101 | } | |
102 | } | |
103 | close IN; | |
a8801b42 JR |
104 | } |
105 | ||
26507458 JR |
106 | if (!defined $pos) { # Initial run. |
107 | my $cursor = `journalctl --facility=mail --show-cursor -n 0 | tail -n 1`; | |
108 | $pos = ($cursor =~ s/^-- cursor: //r); | |
a8801b42 JR |
109 | } |
110 | ||
26507458 JR |
111 | $pos = parseLogfile($pos); |
112 | ||
113 | if ( $ARGV[0] and $ARGV[0] eq "config" ) { | |
114 | print "graph_title Postfix message throughput\n"; | |
115 | print "graph_args --base 1000 -l 0\n"; | |
116 | print "graph_vlabel mails / \${graph_period}\n"; | |
117 | print "graph_scale no\n"; | |
26507458 JR |
118 | print "graph_category postfix\n"; |
119 | print "graph_period minute\n"; | |
120 | print "messages.label messages\n"; | |
121 | print "messages.type DERIVE\n"; | |
122 | print "messages.min 0\n"; | |
123 | ||
124 | foreach my $key (sort keys %descriptions) { | |
125 | print "$key.label $key\n"; | |
126 | print "$key.info $descriptions{$key}\n"; | |
127 | print "$key.type DERIVE\n"; | |
128 | print "$key.min 0\n"; | |
129 | } | |
130 | ||
131 | foreach my $reason (sort keys %rejects) { | |
132 | my $fieldname = clean_fieldname("r$reason"); | |
133 | print "$fieldname.label reject $reason\n"; | |
134 | print "$fieldname.type DERIVE\n"; | |
135 | print "$fieldname.min 0\n"; | |
136 | } | |
137 | exit 0; | |
a8801b42 JR |
138 | } |
139 | ||
26507458 JR |
140 | foreach my $type (sort keys %stats) { |
141 | print "$type.value " . $stats{$type} . "\n"; | |
a8801b42 | 142 | } |
26507458 JR |
143 | foreach my $reason (sort keys %rejects) { |
144 | my $fieldname = clean_fieldname("r$reason"); | |
145 | print "$fieldname.value ", $rejects{$reason}, "\n"; | |
a8801b42 JR |
146 | } |
147 | ||
148 | if(-l $statefile) { | |
26507458 | 149 | die("$statefile is a symbolic link, refusing to touch it."); |
a8801b42 JR |
150 | } |
151 | open (OUT, '>', $statefile) or die "Unable to open statefile: $!\n"; | |
26507458 JR |
152 | print OUT "$pos\n"; |
153 | print OUT join(':', map { "$_=$stats{$_}" } keys %stats) . "\n"; | |
154 | foreach my $i (sort keys %rejects) { | |
155 | print OUT "$i:", $rejects{$i}, "\n"; | |
a8801b42 JR |
156 | } |
157 | close OUT; | |
158 | ||
26507458 JR |
159 | sub parseLogfile { |
160 | my ($start) = @_; | |
161 | open(my $LOGFILE, '-|', "journalctl --facility=mail --after-cursor='$start' --show-cursor") | |
162 | or die "Unable to open journalctl for reading: $? $!\n"; | |
163 | ||
164 | while (my $line = <$LOGFILE>) { | |
165 | chomp $line; | |
166 | ||
167 | if ($line =~ /qmgr\[[^]]+\]: [0-9A-Za-z]+: from=.*, size=\d+, nrcpt=(\d+)/) { | |
168 | $stats{messages}++; | |
169 | $stats{recipients} += $1; | |
170 | } elsif ($line =~ /local\[[^]]+\]: [0-9A-Za-z]+: to=/) { | |
171 | $stats{local}++; | |
172 | } elsif ($line =~ /smtp\[[^]]+\]: [0-9A-Za-z]+: to=/) { | |
173 | $stats{smtp}++; | |
174 | } elsif ($line =~ /pickup\[[^]]+\]: [0-9A-Za-z]+: uid=/) { | |
175 | $stats{pickup}++; | |
176 | } elsif ($line =~ /smtpd\[[^]]+\]: [0-9A-Za-z]+: client=/) { | |
177 | $stats{smtpd}++; | |
178 | } elsif ($line =~ /bounce\[[^]]+\]: [0-9A-Za-z]+: /) { | |
179 | $stats{bounce}++; | |
180 | } elsif ( | |
181 | $line =~ /postfix\/smtpd.*proxy-reject: \S+ (\S+)/ || | |
182 | $line =~ /postfix\/smtpd.*reject: \S+ \S+ \S+ (\S+)/ || | |
183 | $line =~ /postfix\/cleanup.* reject: (\S+)/ || | |
184 | $line =~ /postfix\/cleanup.* milter-reject: \S+ \S+ \S+ (\S+)/ | |
185 | ) { | |
186 | $rejects{$1}++; | |
187 | } elsif ($line =~ /^-- cursor: (.+)$/) { | |
188 | $start = $1; | |
189 | } | |
190 | } | |
191 | close($LOGFILE); | |
192 | return $start; | |
a8801b42 JR |
193 | } |
194 | ||
195 | # vim:syntax=perl |