check_email_loop.pl 9.8 KB

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