Code

Fix for regex input of '|', being output causing problems with Nagios' parsing of
[nagiosplug.git] / contrib / check_email_loop.pl
1 #!/usr/bin/perl -w
2 #
3 # $Id: check_email_loop.pl 1290 2005-11-29 23:21:06Z harpermann $
4 #
5 # (c)2000 Benjamin Schmid, blueshift@gmx.net (emergency use only ;-)
6 # Copyleft by GNU GPL
7 #
8 #
9 # check_email_loop Nagios Plugin
10 #
11 # This script sends a mail with a specific id in the subject via
12 # an given smtp-server to a given email-adress. When the script
13 # is run again, it checks for this Email (with its unique id) on
14 # a given pop3 account and send another mail.
15
16 #
17 # Example: check_email_loop.pl -poph=mypop -popu=user -pa=password
18 #          -smtph=mailer -from=returnadress@yoursite.com
19 #          -to=remaileradress@friend.com -pendc=2 -lostc=0
20 #
21 # This example will send eacht time this check is executed a new
22 # mail to remaileradress@friend.com using the SMTP-Host mailer.
23 # Then it looks for any back-forwarded mails in the POP3 host
24 # mypop. In this Configuration CRITICAL state will be reached if  
25 # more than 2 Mails are pending (meaning that they did not came 
26 # back till now) or if a mails got lost (meaning a mail, that was
27 # send later came back prior to another mail).
28
29 # Michael Markstaller, mm@elabnet.de various changes/additions
30 # MM 021003: fixed some unquoted strings
31 # MM 021116: fixed/added pendwarn/lostwarn
32 # MM 030515: added deleting of orphaned check-emails 
33 #                                         changed to use "top" instead of get to minimize traffic (required changing match-string from "Subject: Email-ping [" to "Email-Ping ["
35 use Net::POP3;
36 use Net::SMTP;
37 use strict;
38 use Getopt::Long;
39 &Getopt::Long::config('auto_abbrev');
41 # ----------------------------------------
43 my $TIMEOUT = 120;
44 my %ERRORS = ('OK' , '0',
45               'WARNING', '1',
46               'CRITICAL', '2');
47               'UNKNOWN' , '3');
49 my $state = "UNKNOWN";
50 my ($sender,$receiver, $pophost, $popuser, $poppasswd, $smtphost,$keeporphaned);
51 my ($poptimeout,$smtptimeout,$pinginterval,$maxmsg)=(60,60,5,50);
52 my ($lostwarn, $lostcrit,$pendwarn, $pendcrit,$debug);
53 $debug = 0;
55 # Internal Vars
56 my ($pop,$msgcount,@msglines,$statinfo,@messageids,$newestid);
57 my (%other_smtp_opts);
58 my ($matchcount,$statfile) = (0,"check_email_loop.stat");
60 # Subs declaration
61 sub usage;
62 sub messagematchs;
63 sub nsexit;
65 # Just in case of problems, let's not hang Nagios
66 $SIG{'ALRM'} = sub {
67      print ("ERROR: $0 Time-Out $TIMEOUT s \n");
68      exit $ERRORS{"UNKNOWN"};
69 };
70 alarm($TIMEOUT);
73 # Evaluate Command Line Parameters
74 my $status = GetOptions(
75                         "from=s",\$sender,
76                         "to=s",\$receiver,
77                         "debug", \$debug,
78                         "pophost=s",\$pophost,
79                         "popuser=s",\$popuser,
80                         "passwd=s",\$poppasswd,
81                         "poptimeout=i",\$poptimeout,
82                         "smtphost=s",\$smtphost,
83                         "smtptimeout=i",\$smtptimeout,
84                         "statfile=s",\$statfile,
85                         "interval=i",\$pinginterval,
86                         "lostwarn=i",\$lostwarn,
87                         "lostcrit=i",\$lostcrit,
88                         "pendwarn=i",\$pendwarn,
89                         "pendcrit=i",\$pendcrit,
90                         "maxmsg=i",\$maxmsg,
91                         "keeporphaned=s",\$keeporphaned,
92                         );
93 usage() if ($status == 0 || ! ($pophost && $popuser && $poppasswd &&
94         $smtphost && $receiver && $sender ));
96 # Try to read the ids of the last send emails out of statfile
97 if (open STATF, "$statfile") {
98   @messageids = <STATF>;
99   chomp @messageids;
100   close STATF;
103 # Try to open statfile for writing 
104 if (!open STATF, ">$statfile") {
105   nsexit("Failed to open mail-ID database $statfile for writing",'CRITICAL');
108 # Ok - check if it's time to release another mail
110 # ...
112 # creating new serial id
113 my $serial = time();
114 $serial = "ID#" . $serial . "#$$";
117 # sending new ping email
118 %other_smtp_opts=();
119 if ( $debug == 1  ) {
120     $other_smtp_opts{'Debug'} = 1;
123 my $smtp = Net::SMTP->new($smtphost,Timeout=>$smtptimeout, %other_smtp_opts) 
124   || nsexit("SMTP connect timeout ($smtptimeout s)",'CRITICAL');
125 ($smtp->mail($sender) &&
126  $smtp->to($receiver) &&
127  $smtp->data() &&
128  $smtp->datasend("To: $receiver\nSubject: E-Mail Ping [$serial]\n\n".
129                  "This is an automatically sent E-Mail.\n".
130                  "It is not intended for a human reader.\n\n".
131                  "Serial No: $serial\n") &&
132  $smtp->dataend() &&
133  $smtp->quit
134  ) || nsexit("Error delivering message",'CRITICAL');
136 # no the interessting part: let's if they are receiving ;-)
138 $pop = Net::POP3->new( $pophost, 
139                  Timeout=>$poptimeout) 
140        || nsexit("POP3 connect timeout (>$poptimeout s, host: $pophost)",'CRITICAL');
142 $msgcount=$pop->login($popuser,$poppasswd);
144 $statinfo="$msgcount mails on POP3";
146 nsexit("POP3 login failed (user:$popuser)",'CRITICAL') if (!defined($msgcount));
148 # Check if more than maxmsg mails in pop3-box
149 nsexit(">$maxmsg Mails ($msgcount Mails on POP3); Please delete !",'WARNING') if ($msgcount > $maxmsg);
151 my ($mid, $nid);
152 # Count messages, that we are looking 4:
153 while ($msgcount > 0) {
154   @msglines = @{$pop->top($msgcount,1)};
155   for (my $i=0; $i < scalar @messageids; $i++) {
156     if (messagematchsid(\@msglines,$messageids[$i])) { 
157       $matchcount++;
158       # newest received mail than the others, ok remeber id.
159       if (!defined $newestid) { 
160         $newestid = $messageids[$i];
161       } else {
162             $messageids[$i] =~ /\#(\d+)\#/;
163         $mid = $1;
164             $newestid =~ /\#(\d+)\#/;
165         $nid = $1;
166         if ($mid > $nid) { 
167           $newestid = $messageids[$i]; 
168         }
169       }
170       $pop->delete($msgcount);  # remove E-Mail from POP3 server
171       splice @messageids, $i, 1;# remove id from List
172           last;                     # stop looking in list
173         } 
174   } 
175         # Delete orphaned Email-ping msg
176         my @msgsubject = grep /^Subject/, @msglines;
177         chomp @msgsubject;
178         # Scan Subject if email is an Email-Ping. In fact we match and delete also successfully retrieved messages here again.
179         if (!defined $keeporphaned && $msgsubject[0] =~ /E-Mail Ping \[/) {
180             $pop->delete($msgcount);  # remove E-Mail from POP3 server
181         }
183         $msgcount--;
186 $pop->quit();  # necessary for pop3 deletion!
188 # traverse through the message list and mark the lost mails
189 # that mean mails that are older than the last received mail.
190 if (defined $newestid) {
191   $newestid =~ /\#(\d+)\#/;
192   $newestid = $1;
193   for (my $i=0; $i < scalar @messageids; $i++) {
194     $messageids[$i] =~ /\#(\d+)\#/;
195     my $akid = $1;
196     if ($akid < $newestid) {
197       $messageids[$i] =~ s/^ID/LI/; # mark lost
198     }
199   }
202 # Write list to id-Database
203 foreach my $id (@messageids) {
204   print STATF  "$id\n";
206 print STATF "$serial\n";     # remember send mail of this session
207 close STATF;
209 # ok - count lost and pending mails;
210 my @tmp = grep /^ID/, @messageids;
211 my $pendingm = scalar @tmp;
212 @tmp = grep /^LI/, @messageids;
213 my $lostm = scalar @tmp; 
215 # Evaluate the Warnin/Crit-Levels
216 if (defined $pendwarn && $pendingm > $pendwarn) { $state = 'WARNING'; }
217 if (defined $lostwarn && $lostm > $lostwarn) { $state = 'WARNING'; }
218 if (defined $pendcrit && $pendingm > $pendcrit) { $state = 'CRITICAL'; }
219 if (defined $lostcrit && $lostm > $lostcrit) { $state = 'CRITICAL'; }
221 if ((defined $pendwarn || defined $pendcrit || defined $lostwarn 
222      || defined $lostcrit) && ($state eq 'UNKNOWN')) {$state='OK';}
225 # Append Status info
226 $statinfo = $statinfo . ", $matchcount mail(s) came back,".
227             " $pendingm pending, $lostm lost.";
229 # Exit in a Nagios-compliant way
230 nsexit($statinfo);
232 # ----------------------------------------------------------------------
234 sub usage {
235   print "check_email_loop 1.1 Nagios Plugin - Real check of a E-Mail system\n";
236   print "=" x 75,"\nERROR: Missing or wrong arguments!\n","=" x 75,"\n";
237   print "This script sends a mail with a specific id in the subject via an given\n";
238   print "smtp-server to a given email-adress. When the script is run again, it checks\n";
239   print "for this Email (with its unique id) on a given pop3 account and sends \n";
240   print "another mail.\n";
241   print "\nThe following options are available:\n";
242   print "   -from=text         email adress of send (for mail returnr on errors)\n";
243   print "   -to=text           email adress to which the mails should send to\n";
244   print "   -pophost=text      IP or name of the POP3-host to be checked\n";
245   print "   -popuser=text      Username of the POP3-account\n";
246   print "   -passwd=text       Password for the POP3-user\n";
247   print "   -poptimeout=num    Timeout in seconds for the POP3-server\n";
248   print "   -smtphost=text     IP oder name of the SMTP host\n";
249   print "   -smtptimeout=num   Timeout in seconds for the SMTP-server\n";
250   print "   -statfile=text     File to save ids of messages ($statfile)\n";
251   print "   -interval=num      Time (in minutes) that must pass by before sending\n";
252   print "                      another Ping-mail (gibe a new try);\n"; 
253   print "   -lostwarn=num      WARNING-state if more than num lost emails\n";
254   print "   -lostcrit=num      CRITICAL \n";
255   print "   -pendwarn=num      WARNING-state if more than num pending emails\n";
256   print "   -pendcrit=num      CRITICAL \n";
257   print "   -maxmsg=num        WARNING if more than num emails on POP3 (default 50)\n";
258   print "   -keeporphaned      Set this to NOT delete orphaned E-Mail Ping msg from POP3\n";
259   print "   -debug             send SMTP tranaction info to stderr\n\n";
260   print " Options may abbreviated!\n";
261   print " LOST mails are mails, being sent before the last mail arrived back.\n";
262   print " PENDING mails are those, which are not. (supposed to be on the way)\n";
263   print "\nExample: \n";
264   print " $0 -poph=host -pa=pw -popu=popts -smtph=host -from=root\@me.com\n ";
265   print "      -to=remailer\@testxy.com -lostc=0 -pendc=2\n";
266   print "\nCopyleft 19.10.2000, Benjamin Schmid / 2003 Michael Markstaller, mm\@elabnet.de\n";
267   print "This script comes with ABSOLUTELY NO WARRANTY\n";
268   print "This programm is licensed under the terms of the ";
269   print "GNU General Public License\n\n";
270   exit $ERRORS{"UNKNOWN"};
273 # ---------------------------------------------------------------------
275 sub nsexit {
276   my ($msg,$code) = @_;
277   $code=$state if (!defined $code);
278   print "$code: $msg\n" if (defined $msg);
279   exit $ERRORS{$code};
282 # ---------------------------------------------------------------------
284 sub messagematchsid {
285   my ($mailref,$id) = (@_);
286   my (@tmp);
287   my $match = 0;
288  
289   # ID
290   $id =~ s/^LI/ID/;    # evtl. remove lost mail mark
291   @tmp = grep /E-Mail Ping \[/, @$mailref;
292   chomp @tmp;
293   if (($tmp[0] =~ /$id/)) 
294     { $match = 1; }
296   # Sender:
297 #  @tmp = grep /^From:\s+/, @$mailref;
298 #  if (@tmp && $sender ne "") 
299 #    { $match = $match && ($tmp[0]=~/$sender/); }
301   # Receiver:
302 #  @tmp = grep /^To: /, @$mailref;
303 #  if (@tmp && $receiver ne "") 
304 #    { $match = $match && ($tmp[0]=~/$receiver/); }
306   return $match;
309 # ---------------------------------------------------------------------