Code

Fix for regex input of '|', being output causing problems with Nagios' parsing of
[nagiosplug.git] / contrib / check_snmp_procs.pl
1 #!/usr/bin/perl -w
2 #
3 # check_snmp_procs.pl
4 #    Nagios script to check processes on remote host via snmp
5 #
6
7 # Copyright (c) 2003 David Alden
8 #
9 # This program is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU General Public License
11 # as published by the Free Software Foundation; either version 2
12 # of the License, or (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22 #
23 #
24 # History
25 # -------
26 #  02-25-2003 - Dave Alden <alden@math.ohio-state.edu>
27 #    Initial creation
28 #
29 #
30 # TODO
31 # ----
32 #    make it work with snmp version 3
33 #    Suggestions???
34 #
35 #use strict;
36 use Getopt::Long;
37 use Net::SNMP qw (oid_lex_sort oid_base_match SNMP_VERSION_1);
38 use lib "/usr/local/nagios/libexec";
39 use utils qw(%ERRORS &print_revision &support &usage);
41 my $PROGNAME="check_snmp_procs";
42 my $REVISION="1.0";
44 #
45 my $opt_authprotocol;
46 my $opt_authpassword;
47 my $opt_community    = 'ma4read';
48 my $opt_critical;
49 my $opt_help;
50 my $opt_host         = 'euler';
51 my $opt_oidname      = 'hrSWRunName';
52 my $opt_port         = 161;
53 my $opt_privpassword;
54 my $opt_regexp       = 0;
55 my $opt_snmp_version = '2c';
56 my $opt_timeout      = $utils::TIMEOUT;
57 my $opt_username;
58 my $opt_verbose;
59 my $opt_version;
60 my $opt_wanted_procs;
61 my $opt_warning;
63 #
64 my $max_no_processes = 999999;
65 my $session;
66 my $error;
67 my $no_procs;
68 my $exit_status;
70 #
71 my @wanted_procs;
72 my %current_process_list;
74 #
75 my %OIDS = (hrSWRunName => '1.3.6.1.2.1.25.4.2.1.2',
76             hrSWRunPath => '1.3.6.1.2.1.25.4.2.1.4');
78 my %OIDS_L = (hrSWRunName => length($OIDS{hrSWRunName}),
79               hrSWRunPath => length($OIDS{hrSWRunPath}));
81 #
82 $ENV{'PATH'}='';
83 $ENV{'BASH_ENV'}=''; 
84 $ENV{'ENV'}='';
86 #
87 Getopt::Long::Configure('bundling');
88 if (GetOptions(
89         "a:s" => \$opt_authprotocol,  "authprotocol:s"  => \$opt_authprotocol,
90         "A:s" => \$opt_authpassword,  "authpassword:s"  => \$opt_authpassword,
91         "C:s" => \$opt_community,     "community:s"     => \$opt_community,
92         "c:s" => \$opt_critical,      "critical:s"      => \$opt_critical,
93         "h"   => \$opt_help,          "help"            => \$opt_help,
94         "H:s" => \$opt_host,          "hostname:s"      => \$opt_host,
95         "o:s" => \$opt_oidname,       "oidname:s"       => \$opt_oidname,
96         "P=s" => \$opt_password,      "password=s"      => \$opt_password,
97         "p=i" => \$opt_port,          "port=i"          => \$opt_port,
98         "r"   => \$opt_regexp,        "regexp"          => \$opt_regexp,
99         "S"   => \$opt_snmp_version,  "snmpversion"     => \$opt_snmp_version,
100         "t=i" => \$opt_timeout,       "timeout=i"       => \$opt_timeout,
101         "U=s" => \$opt_username,      "username=s"      => \$opt_username,
102         "v"   => \$opt_verbose,       "verbose"         => \$opt_verbose,
103         "V"   => \$opt_version,       "version"         => \$opt_version,
104         "N=s" => \$opt_wanted_procs,  "names=s"         => \$opt_wanted_procs,
105         "w:s" => \$opt_warning,       "warning:s"       => \$opt_warning)
106     == 0) {
107         print_usage();
108         exit $ERRORS{'UNKNOWN'};
111 if ($opt_version) {
112         print_revision($PROGNAME, "\$Revision: 1771 $REVISION \$");
113         exit $ERRORS{'OK'};
116 if ($opt_help) {
117         print_help();
118         exit $ERRORS{'OK'};
121 if (! utils::is_hostname($opt_host)){
122         usage();
123         exit $ERRORS{'UNKNOWN'};
126 ($longest_wanted, @wanted_procs) = parse_wanted_procs($opt_verbose, $opt_wanted_procs, $opt_warning, $opt_critical);
128 $SIG{'ALRM'} = sub {
129         print "Timeout: No Answer from Client\n";
130         exit $ERRORS{'UNKNOWN'};
131 };
132 alarm($opt_timeout);
134 ($longest_current, %current_process_list) = get_process_list($opt_verbose, $opt_host, $opt_username, $opt_privpassword, $opt_authprotocol, $opt_authpassword, $opt_community, $opt_port, $opt_oidname, $opt_snmp_version);
136 $exit_status = compare_process_list($opt_regexp, \%current_process_list, @wanted_procs);
138 if ($opt_verbose) {
139         print_info($longest_current, \%current_process_list, $longest_wanted, @wanted_procs);
142 exit($exit_status);
146 sub compare_process_list {
148         my($regexp, $current_process_list, @wanted_procs) = @_;
149         my($proc, $i, $no_running_procs, @warning, @critical);
150         my $exit = $ERRORS{'OK'};
152         for ($i = 0; $i <= $#wanted_procs; $i++) {
154                 $proc = $wanted_procs[$i];
156                 $no_running_procs = get_running_procs($regexp, $$proc{name}, $current_process_list);
158                 $$proc{no_matches} += $no_running_procs;
160                 if (($no_running_procs >= $$proc{warn_low}) &&
161                     ($no_running_procs <= $$proc{warn_high})) {
163                         push(@warning, $$proc{name} . "($no_running_procs)");
165                         if ($exit != $ERRORS{'CRITICAL'}) {
166                                 $exit = $ERRORS{'WARNING'};
167                         }
169                 } elsif (($no_running_procs < $$proc{minimum}) ||
170                          ($no_running_procs >= $$proc{critical_low}) &&
171                          ($no_running_procs <= $$proc{critical_high})) {
173                         push(@critical, $$proc{name} . "($no_running_procs)");
175                         $exit = $ERRORS{'CRITICAL'};
176                 }
177         }
179         print "SNMPPROC ";
181         if ($#critical >= 0) {
182                 print "CRITICAL:";
183         } elsif ($#warning >= 0) {
184                 print "WARNING:";
185         } else {
186                 print "OK";
187         }
189         foreach $i (@critical) {
190                 print " $i";
191         }
193         if (($#critical >= 0) &&
194             ($#warning >= 0)) {
195                 print "  WARNING:";
196         }
198         foreach $i (@warning) {
199                 print " $i";
200         }
202         print "\n";
204         return $exit;
209 sub get_running_procs {
211         my($regex, $name, $process_list) = @_;
212         my $count = 0;
213         my $process;
215         $count = 0;
217         if ($regex) {
219                 foreach $process (keys %{$process_list}) {
221                         if ($process =~ /$name/) {
222                                 $count += $$process_list{$process};
223                         }
224                 }
227         } else {
229                 if (!defined($count = $$process_list{$name})) {
230                         $count = 0;
231                 }
232         }
234         return $count;
239 sub get_process_list {
241         my($verbose, $host, $username, $privpassword, $authprotocol, $authpassword, $community, $port, $oidname, $snmp_version) = @_;
242         my(%process_list, %process_pid_list, $result);
243         my $process_list_longest = 1, $not_done = 1;
244         my(@args, @oids, $oid, $name);
246         ($session, $error) = Net::SNMP->session(
247                 -hostname      =>  $host,
248                 -community     =>  $community,
249                 -port          =>  $port,
250                 -version       =>  $snmp_version,
251                 defined($privpassword) ? (-privpassword  =>  $privpassword) : (),
252                 defined($authpassword) ? (-authpassword  =>  $authpassword) : (),
253                 defined($authprotocol) ? (-authprotocol  =>  $authprotocol) : (),
254                 defined($username)     ? (-username      =>  $username) : ());
256         if (!defined($session)) {
257                 print ("UNKNOWN: $error\n");
258                 exit $ERRORS{'UNKNOWN'};
259         }
261         @args = (-varbindlist => [$OIDS{$oidname}]);
263         if ($session->version == SNMP_VERSION_1) {
265                 while (defined($session->get_next_request(@args))) {
267                         $oid = (keys(%{$session->var_bind_list}))[0];
269                         last if (!oid_base_match($OIDS{$oidname}, $oid));
271                         $name = $session->var_bind_list->{$oid};
272                         $process_list{$name}++;
274                         if ($verbose && ($process_list_longest < length($name))) {
275                                 $process_list_longest = length($name);
276                         }
278                         @args = (-varbindlist => [$oid]);
279                 }
281         } else {
283                 push(@args, -maxrepetitions => 25);
285                 while ($not_done && defined($session->get_bulk_request(@args))) {
287                         @oids = oid_lex_sort(keys(%{$session->var_bind_list}));
289                         foreach $oid (@oids) {
290                                 if (!oid_base_match($OIDS{$oidname}, $oid)) {
292                                         $not_done = 0;
294                                 } else {
296                                         $name = $session->var_bind_list->{$oid};
297                                         $process_list{$name}++;
299                                         if ($verbose && ($process_list_longest < length($name))) {
300                                                 $process_list_longest = length($name);
301                                         }
303                                         if ($session->var_bind_list->{$oid} eq 'endOfMibView') {
304                                                 $not_done = 0;
305                                         }
306                                 }
307                         }
309                         if ($not_done) {
310                                 @args = (-maxrepetitions => 25, -varbindlist => [pop(@oids)]);
311                         }
312                 }
313         }
315         if ($session->error() ne '') {
316                 print ("UNKNOWN: " . $session->error() . "\n");
317                 exit $ERRORS{'UNKNOWN'};
318         }
320         $session->close;
322         return($process_list_longest, %process_list);
327 sub parse_wanted_procs {
329         my($verbose, $wanted_procs, $warning, $critical) = @_;
331         my(@procs, $process, $i, $critical_low, $critical_high, $warn_low, $warn_high, $process_name, $process_min);
332         my(@process_array, @warn_array, @critical_array);
333         my $exit = 0;
334         my $longest_name = 1;
336         if (defined($wanted_procs)) {
337                 @process_array = split(/,/, $wanted_procs);
338         }
340         if (defined($warning)) {
341                 @warn_array = split(/,/, $warning);
342         }
344         if (defined($critical)) {
345                 @critical_array = split(/,/, $critical);
346         }
348         if( defined($warning) && $#process_array != $#warn_array ) {
350                 print "Error: Number of entries in process list($#process_array) and warn list($#warn_array) don't match\n";
351                 exit $ERRORS{'UNKNOWN'};
352         }
354         if( defined($critical) && $#process_array != $#critical_array ) {
356                 print "Error: Number of entries in process list and critical list don't match\n";
357                 exit $ERRORS{'UNKNOWN'};
358         }
360         for ($i = 0; $i <= $#process_array; $i++) {
362                 if ((($process_name, $process_min) = split(/:/, $process_array[$i])) != 2) {
364                         $process_min = 1;
365                 }
367                 if ($verbose && ($longest_name < length($process_name))) {
369                         $longest_name = length($process_name);
370                 }
372                 if (defined($critical_array[$i])) {
373                         if ((($critical_low, $critical_high) = split(/:/, $critical_array[$i])) != 2) {
375                                 $critical_high = $critical_low;
377                         } else {
379                                 if ($critical_high eq "") {
380                                         $critical_high = $max_no_processes;
381                                 }
383                                 if ($critical_low eq "") {
384                                         $critical_low = 0;
385                                 }
386                         }
387                 } else {
389                         $critical_low = -1;
390                         $critical_high = -1;
391                 }
393                 if (defined($warn_array[$i])) {
394                         if ((($warn_low, $warn_high) = split(/:/, $warn_array[$i])) != 2) {
396                                 $warn_high = $warn_low;
398                         } else {
400                                 if ($warn_high eq "") {
401                                         $warn_high = $max_no_processes;
402                                 }
404                                 if ($warn_low eq "") {
405                                         $warn_low = 0;
406                                 }
407                         }
408                 } else {
410                         $warn_low = -1;
411                         $warn_high = -1;
412                 }
414                 if ($critical_low > $critical_high) {
415                         print "Error: $process_name critical low($critical_low) is larger than high($critical_high)\n";
416                         $exit = 1;
417                 }
419                 if ($warn_low > $warn_high) {
420                         print "Error: $process_name warn low($warn_low) is larger than high($warn_high)\n";
421                         $exit = 1;
422                 }
424                 if (@critical_array &&
425                     ($process_min > $critical_low)) {
426                         print "Error: $process_name minimum($process_min) is larger than critical low($critical_low)\n";
427                         $exit = 1;
428                 }
430                 if (@warn_array &&
431                     ($process_min > $warn_low)) {
432                         print "Error: $process_name minimum($process_min) is larger than warn low($warn_low)\n";
433                         $exit = 1;
434                 }
436                 if (@warn_array && @critical_array &&
437                     ((($warn_low >= $critical_low) && ($warn_low <= $critical_high)) ||
438                      (($warn_high >= $critical_low) && ($warn_high <= $critical_high)))) {
440                         print "Error: $process_name warn levels($warn_low:$warn_high) overlap with critical levels($critical_low:$critical_high)\n";
441                         $exit = 1;
442                 }
444                 push(@procs,{
445                         name          => $process_name,
446                         critical      => defined($critical),
447                         critical_low  => $critical_low,
448                         critical_high => $critical_high,
449                         minimum       => $process_min,
450                         warning       => defined($warning),
451                         warn_low      => $warn_low,
452                         warn_high     => $warn_high});
453         }
455         if ($exit) {
456                 exit $ERRORS{'UNKNOWN'};
457         }
459         return($longest_name, @procs);
464 sub print_info {
466         my ($longest_current, $current_process_list, $longest_wanted, @wanted_procs) = @_;
468         if ($longest_wanted < 7) {
469                 $longest_wanted = 7;
470         } else {
471                 $longest_wanted++;
472         }
474         printf("%s---------------------------------------------\n", "-" x $longest_wanted);
475         printf("|%-" . $longest_wanted . "s |      |  Min |     Warn    |   Critical  |\n", "Process");
476         printf("|%-" . $longest_wanted . "s | Qty  | Procs|  Low | High |  Low | High |\n", "Name");
477         printf("%s---------------------------------------------\n", "-" x $longest_wanted);
479         for (my $temp=0; $temp <= $#wanted_procs; $temp++) {
481                 printf("|%-" . $longest_wanted . "s |%6d|%6d|%6d|%6d|%6d|%6d|\n",
482                         $wanted_procs[$temp]{name},
483                         $wanted_procs[$temp]{no_matches},
484                         $wanted_procs[$temp]{minimum},
485                         $wanted_procs[$temp]{critical_low},
486                         $wanted_procs[$temp]{critical_high},
487                         $wanted_procs[$temp]{warn_low},
488                         $wanted_procs[$temp]{warn_high});
489         }
491         printf("%s---------------------------------------------\n\n", "-" x $longest_wanted);
493         if ($longest_current < 7) {
494                 $longest_current = 7;
495         } else {
496                 $longest_current++;
497         }
499         printf("%s----------\n", "-" x $longest_current);
500         printf("|%-" . $longest_current . "s |  Qty |\n", "Process");
501         printf("%s----------\n", "-" x $longest_current);
503         foreach my $result (sort keys %{$current_process_list}) {
505                 printf("|%-" . $longest_current . "s |%6d|\n", $result,
506                         $current_process_list{$result});
507         }
508         printf("%s----------\n", "-" x $longest_current);
510         return;
515 sub print_usage {
516     print "Usage:
517  $PROGNAME -H <host> [-r] [-v]
518                   -N <processname>[:minimum][,<processname>[:minimum] ...]
519                   [-a <authprotocol>] [-A <authpassword>]
520                   [-U <username>] [-P <password>]
521                   [-o <oidname>] [ -S <snmpversion> ]
522                   [-C <snmp_community>] [-p <port>] [-t <timeout>]
523                   [-w <low>:<high>[,<low>:<high> ...]
524                   [-c <low>:<high>[,<low>:<high> ...]
525        $PROGNAME (-h | --help) for detailed help
526        $PROGNAME (-V | --version) for version information\n";
531 sub print_help {
532         print_revision($PROGNAME, "\$Revision: 1771 $REVISION \$");
533         print "Copyright (c) 2003 David Alden
535 Check if processes are running on a host via snmp
537 ";
539         print_usage();
541         print "
542 -a, --authprotocol=<authprotocol>
543    Set the authentication protocol used for authenticated SNMPv3 messages
544 -A, --authpassword=<authpassword>
545    Set the authentication pass phrase used for authenticated SNMPv3 messages
546 -c, --critical=<low>:<high>[,<low>:<high> ...]
547    exit with CRITICAL status if number of processes is between <low> and <high>
548 -C, --community=<snmp_community>
549    SNMP read community (default: $opt_community)
550 -h, --help
551    Show this help screen
552 -H, --host=<host>
553    Check processes on the indiciated host
554 -o, --oidname=<oidname>
555    Which oid tree to search, hrSWRunName or hrSWRunPath (default: $opt_oidname)
556 -p, --port=<port>
557    Make connection on the indicated port (default: $opt_port)
558 -N, --names=<processname>[:<minimum>][,<processname>[:<minimum>] ...]
559    Process names to check, (optional) minimum number of processes (default: 1)
560 -P, --password=<privpassword>
561    Set the privacy pass phrase used for encrypted SNMPv3 messages
562 -r, --regex
563    Use regular expression match for <process>
564 -S, --snmpversion
565    Use snmp version specified (values: 1|2c|3, default: $opt_snmp_version)
566 -t, --timeout
567    Plugin time out in seconds (default: $opt_timeout)
568 -U, --username=<securityname>
569    Set the securityname used for encrypted SNMPv3 messages
570 -v, --verbose
571    Print some extra debugging information (not advised for normal operation)
572 -V, --version
573    Show version and license information
574 -w, --warning=<low>:<high>[,<low>:<high> ...]
575    exit with WARNING status if number of processes is between <low> and <high>
578 A CRITICAL error will be indicated unless there are at least <minimum> number
579 of processes running (unless <minimum> is set to 0 -- useful if you don't
580 mind that there are none of the processes running).
582 If no processes are specified, the program will still connect to the remote
583 host and download the current list of running processes.  It will then exit
584 with an OK (unless it wasn't able to connect) -- useful if you want to make
585 sure that the remote snmpd process is running and returning a list of procs.
588 ";
589         support();