check_email_loop.pl 10 KB

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