check_snmp_procs.pl 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  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);
  40. my $PROGNAME="check_snmp_procs";
  41. my $REVISION="1.0";
  42. #
  43. my $opt_authprotocol;
  44. my $opt_authpassword;
  45. my $opt_community = 'ma4read';
  46. my $opt_critical;
  47. my $opt_help;
  48. my $opt_host = 'euler';
  49. my $opt_oidname = 'hrSWRunName';
  50. my $opt_port = 161;
  51. my $opt_privpassword;
  52. my $opt_regexp = 0;
  53. my $opt_snmp_version = '2c';
  54. my $opt_timeout = $utils::TIMEOUT;
  55. my $opt_username;
  56. my $opt_verbose;
  57. my $opt_version;
  58. my $opt_wanted_procs;
  59. my $opt_warning;
  60. #
  61. my $max_no_processes = 999999;
  62. my $session;
  63. my $error;
  64. my $no_procs;
  65. my $exit_status;
  66. #
  67. my @wanted_procs;
  68. my %current_process_list;
  69. #
  70. my %OIDS = (hrSWRunName => '1.3.6.1.2.1.25.4.2.1.2',
  71. hrSWRunPath => '1.3.6.1.2.1.25.4.2.1.4');
  72. my %OIDS_L = (hrSWRunName => length($OIDS{hrSWRunName}),
  73. hrSWRunPath => length($OIDS{hrSWRunPath}));
  74. #
  75. $ENV{'PATH'}='';
  76. $ENV{'BASH_ENV'}='';
  77. $ENV{'ENV'}='';
  78. #
  79. Getopt::Long::Configure('bundling');
  80. if (GetOptions(
  81. "a:s" => \$opt_authprotocol, "authprotocol:s" => \$opt_authprotocol,
  82. "A:s" => \$opt_authpassword, "authpassword:s" => \$opt_authpassword,
  83. "C:s" => \$opt_community, "community:s" => \$opt_community,
  84. "c:s" => \$opt_critical, "critical:s" => \$opt_critical,
  85. "h" => \$opt_help, "help" => \$opt_help,
  86. "H:s" => \$opt_host, "hostname:s" => \$opt_host,
  87. "o:s" => \$opt_oidname, "oidname:s" => \$opt_oidname,
  88. "P=s" => \$opt_password, "password=s" => \$opt_password,
  89. "p=i" => \$opt_port, "port=i" => \$opt_port,
  90. "r" => \$opt_regexp, "regexp" => \$opt_regexp,
  91. "S" => \$opt_snmp_version, "snmpversion" => \$opt_snmp_version,
  92. "t=i" => \$opt_timeout, "timeout=i" => \$opt_timeout,
  93. "U=s" => \$opt_username, "username=s" => \$opt_username,
  94. "v" => \$opt_verbose, "verbose" => \$opt_verbose,
  95. "V" => \$opt_version, "version" => \$opt_version,
  96. "N=s" => \$opt_wanted_procs, "names=s" => \$opt_wanted_procs,
  97. "w:s" => \$opt_warning, "warning:s" => \$opt_warning)
  98. == 0) {
  99. print_usage();
  100. exit $ERRORS{'UNKNOWN'};
  101. }
  102. if ($opt_version) {
  103. print_revision($PROGNAME, "\$Revision: 1771 $REVISION \$");
  104. exit $ERRORS{'OK'};
  105. }
  106. if ($opt_help) {
  107. print_help();
  108. exit $ERRORS{'OK'};
  109. }
  110. if (! utils::is_hostname($opt_host)){
  111. usage();
  112. exit $ERRORS{'UNKNOWN'};
  113. }
  114. ($longest_wanted, @wanted_procs) = parse_wanted_procs($opt_verbose, $opt_wanted_procs, $opt_warning, $opt_critical);
  115. $SIG{'ALRM'} = sub {
  116. print "Timeout: No Answer from Client\n";
  117. exit $ERRORS{'UNKNOWN'};
  118. };
  119. alarm($opt_timeout);
  120. ($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);
  121. $exit_status = compare_process_list($opt_regexp, \%current_process_list, @wanted_procs);
  122. if ($opt_verbose) {
  123. print_info($longest_current, \%current_process_list, $longest_wanted, @wanted_procs);
  124. }
  125. exit($exit_status);
  126. #
  127. sub compare_process_list {
  128. my($regexp, $current_process_list, @wanted_procs) = @_;
  129. my($proc, $i, $no_running_procs, @warning, @critical);
  130. my $exit = $ERRORS{'OK'};
  131. for ($i = 0; $i <= $#wanted_procs; $i++) {
  132. $proc = $wanted_procs[$i];
  133. $no_running_procs = get_running_procs($regexp, $$proc{name}, $current_process_list);
  134. $$proc{no_matches} += $no_running_procs;
  135. if (($no_running_procs >= $$proc{warn_low}) &&
  136. ($no_running_procs <= $$proc{warn_high})) {
  137. push(@warning, $$proc{name} . "($no_running_procs)");
  138. if ($exit != $ERRORS{'CRITICAL'}) {
  139. $exit = $ERRORS{'WARNING'};
  140. }
  141. } elsif (($no_running_procs < $$proc{minimum}) ||
  142. ($no_running_procs >= $$proc{critical_low}) &&
  143. ($no_running_procs <= $$proc{critical_high})) {
  144. push(@critical, $$proc{name} . "($no_running_procs)");
  145. $exit = $ERRORS{'CRITICAL'};
  146. }
  147. }
  148. print "SNMPPROC ";
  149. if ($#critical >= 0) {
  150. print "CRITICAL:";
  151. } elsif ($#warning >= 0) {
  152. print "WARNING:";
  153. } else {
  154. print "OK";
  155. }
  156. foreach $i (@critical) {
  157. print " $i";
  158. }
  159. if (($#critical >= 0) &&
  160. ($#warning >= 0)) {
  161. print " WARNING:";
  162. }
  163. foreach $i (@warning) {
  164. print " $i";
  165. }
  166. print "\n";
  167. return $exit;
  168. }
  169. #
  170. sub get_running_procs {
  171. my($regex, $name, $process_list) = @_;
  172. my $count = 0;
  173. my $process;
  174. $count = 0;
  175. if ($regex) {
  176. foreach $process (keys %{$process_list}) {
  177. if ($process =~ /$name/) {
  178. $count += $$process_list{$process};
  179. }
  180. }
  181. } else {
  182. if (!defined($count = $$process_list{$name})) {
  183. $count = 0;
  184. }
  185. }
  186. return $count;
  187. }
  188. #
  189. sub get_process_list {
  190. my($verbose, $host, $username, $privpassword, $authprotocol, $authpassword, $community, $port, $oidname, $snmp_version) = @_;
  191. my(%process_list, %process_pid_list, $result);
  192. my $process_list_longest = 1, $not_done = 1;
  193. my(@args, @oids, $oid, $name);
  194. ($session, $error) = Net::SNMP->session(
  195. -hostname => $host,
  196. -community => $community,
  197. -port => $port,
  198. -version => $snmp_version,
  199. defined($privpassword) ? (-privpassword => $privpassword) : (),
  200. defined($authpassword) ? (-authpassword => $authpassword) : (),
  201. defined($authprotocol) ? (-authprotocol => $authprotocol) : (),
  202. defined($username) ? (-username => $username) : ());
  203. if (!defined($session)) {
  204. print ("UNKNOWN: $error\n");
  205. exit $ERRORS{'UNKNOWN'};
  206. }
  207. @args = (-varbindlist => [$OIDS{$oidname}]);
  208. if ($session->version == SNMP_VERSION_1) {
  209. while (defined($session->get_next_request(@args))) {
  210. $oid = (keys(%{$session->var_bind_list}))[0];
  211. last if (!oid_base_match($OIDS{$oidname}, $oid));
  212. $name = $session->var_bind_list->{$oid};
  213. $process_list{$name}++;
  214. if ($verbose && ($process_list_longest < length($name))) {
  215. $process_list_longest = length($name);
  216. }
  217. @args = (-varbindlist => [$oid]);
  218. }
  219. } else {
  220. push(@args, -maxrepetitions => 25);
  221. while ($not_done && defined($session->get_bulk_request(@args))) {
  222. @oids = oid_lex_sort(keys(%{$session->var_bind_list}));
  223. foreach $oid (@oids) {
  224. if (!oid_base_match($OIDS{$oidname}, $oid)) {
  225. $not_done = 0;
  226. } else {
  227. $name = $session->var_bind_list->{$oid};
  228. $process_list{$name}++;
  229. if ($verbose && ($process_list_longest < length($name))) {
  230. $process_list_longest = length($name);
  231. }
  232. if ($session->var_bind_list->{$oid} eq 'endOfMibView') {
  233. $not_done = 0;
  234. }
  235. }
  236. }
  237. if ($not_done) {
  238. @args = (-maxrepetitions => 25, -varbindlist => [pop(@oids)]);
  239. }
  240. }
  241. }
  242. if ($session->error() ne '') {
  243. print ("UNKNOWN: " . $session->error() . "\n");
  244. exit $ERRORS{'UNKNOWN'};
  245. }
  246. $session->close;
  247. return($process_list_longest, %process_list);
  248. }
  249. #
  250. sub parse_wanted_procs {
  251. my($verbose, $wanted_procs, $warning, $critical) = @_;
  252. my(@procs, $process, $i, $critical_low, $critical_high, $warn_low, $warn_high, $process_name, $process_min);
  253. my(@process_array, @warn_array, @critical_array);
  254. my $exit = 0;
  255. my $longest_name = 1;
  256. if (defined($wanted_procs)) {
  257. @process_array = split(/,/, $wanted_procs);
  258. }
  259. if (defined($warning)) {
  260. @warn_array = split(/,/, $warning);
  261. }
  262. if (defined($critical)) {
  263. @critical_array = split(/,/, $critical);
  264. }
  265. if( defined($warning) && $#process_array != $#warn_array ) {
  266. print "Error: Number of entries in process list($#process_array) and warn list($#warn_array) don't match\n";
  267. exit $ERRORS{'UNKNOWN'};
  268. }
  269. if( defined($critical) && $#process_array != $#critical_array ) {
  270. print "Error: Number of entries in process list and critical list don't match\n";
  271. exit $ERRORS{'UNKNOWN'};
  272. }
  273. for ($i = 0; $i <= $#process_array; $i++) {
  274. if ((($process_name, $process_min) = split(/:/, $process_array[$i])) != 2) {
  275. $process_min = 1;
  276. }
  277. if ($verbose && ($longest_name < length($process_name))) {
  278. $longest_name = length($process_name);
  279. }
  280. if (defined($critical_array[$i])) {
  281. if ((($critical_low, $critical_high) = split(/:/, $critical_array[$i])) != 2) {
  282. $critical_high = $critical_low;
  283. } else {
  284. if ($critical_high eq "") {
  285. $critical_high = $max_no_processes;
  286. }
  287. if ($critical_low eq "") {
  288. $critical_low = 0;
  289. }
  290. }
  291. } else {
  292. $critical_low = -1;
  293. $critical_high = -1;
  294. }
  295. if (defined($warn_array[$i])) {
  296. if ((($warn_low, $warn_high) = split(/:/, $warn_array[$i])) != 2) {
  297. $warn_high = $warn_low;
  298. } else {
  299. if ($warn_high eq "") {
  300. $warn_high = $max_no_processes;
  301. }
  302. if ($warn_low eq "") {
  303. $warn_low = 0;
  304. }
  305. }
  306. } else {
  307. $warn_low = -1;
  308. $warn_high = -1;
  309. }
  310. if ($critical_low > $critical_high) {
  311. print "Error: $process_name critical low($critical_low) is larger than high($critical_high)\n";
  312. $exit = 1;
  313. }
  314. if ($warn_low > $warn_high) {
  315. print "Error: $process_name warn low($warn_low) is larger than high($warn_high)\n";
  316. $exit = 1;
  317. }
  318. if (@critical_array &&
  319. ($process_min > $critical_low)) {
  320. print "Error: $process_name minimum($process_min) is larger than critical low($critical_low)\n";
  321. $exit = 1;
  322. }
  323. if (@warn_array &&
  324. ($process_min > $warn_low)) {
  325. print "Error: $process_name minimum($process_min) is larger than warn low($warn_low)\n";
  326. $exit = 1;
  327. }
  328. if (@warn_array && @critical_array &&
  329. ((($warn_low >= $critical_low) && ($warn_low <= $critical_high)) ||
  330. (($warn_high >= $critical_low) && ($warn_high <= $critical_high)))) {
  331. print "Error: $process_name warn levels($warn_low:$warn_high) overlap with critical levels($critical_low:$critical_high)\n";
  332. $exit = 1;
  333. }
  334. push(@procs,{
  335. name => $process_name,
  336. critical => defined($critical),
  337. critical_low => $critical_low,
  338. critical_high => $critical_high,
  339. minimum => $process_min,
  340. warning => defined($warning),
  341. warn_low => $warn_low,
  342. warn_high => $warn_high});
  343. }
  344. if ($exit) {
  345. exit $ERRORS{'UNKNOWN'};
  346. }
  347. return($longest_name, @procs);
  348. }
  349. #
  350. sub print_info {
  351. my ($longest_current, $current_process_list, $longest_wanted, @wanted_procs) = @_;
  352. if ($longest_wanted < 7) {
  353. $longest_wanted = 7;
  354. } else {
  355. $longest_wanted++;
  356. }
  357. printf("%s---------------------------------------------\n", "-" x $longest_wanted);
  358. printf("|%-" . $longest_wanted . "s | | Min | Warn | Critical |\n", "Process");
  359. printf("|%-" . $longest_wanted . "s | Qty | Procs| Low | High | Low | High |\n", "Name");
  360. printf("%s---------------------------------------------\n", "-" x $longest_wanted);
  361. for (my $temp=0; $temp <= $#wanted_procs; $temp++) {
  362. printf("|%-" . $longest_wanted . "s |%6d|%6d|%6d|%6d|%6d|%6d|\n",
  363. $wanted_procs[$temp]{name},
  364. $wanted_procs[$temp]{no_matches},
  365. $wanted_procs[$temp]{minimum},
  366. $wanted_procs[$temp]{critical_low},
  367. $wanted_procs[$temp]{critical_high},
  368. $wanted_procs[$temp]{warn_low},
  369. $wanted_procs[$temp]{warn_high});
  370. }
  371. printf("%s---------------------------------------------\n\n", "-" x $longest_wanted);
  372. if ($longest_current < 7) {
  373. $longest_current = 7;
  374. } else {
  375. $longest_current++;
  376. }
  377. printf("%s----------\n", "-" x $longest_current);
  378. printf("|%-" . $longest_current . "s | Qty |\n", "Process");
  379. printf("%s----------\n", "-" x $longest_current);
  380. foreach my $result (sort keys %{$current_process_list}) {
  381. printf("|%-" . $longest_current . "s |%6d|\n", $result,
  382. $current_process_list{$result});
  383. }
  384. printf("%s----------\n", "-" x $longest_current);
  385. return;
  386. }
  387. #
  388. sub print_usage {
  389. print "Usage:
  390. $PROGNAME -H <host> [-r] [-v]
  391. -N <processname>[:minimum][,<processname>[:minimum] ...]
  392. [-a <authprotocol>] [-A <authpassword>]
  393. [-U <username>] [-P <password>]
  394. [-o <oidname>] [ -S <snmpversion> ]
  395. [-C <snmp_community>] [-p <port>] [-t <timeout>]
  396. [-w <low>:<high>[,<low>:<high> ...]
  397. [-c <low>:<high>[,<low>:<high> ...]
  398. $PROGNAME (-h | --help) for detailed help
  399. $PROGNAME (-V | --version) for version information\n";
  400. }
  401. #
  402. sub print_help {
  403. print_revision($PROGNAME, "\$Revision: 1771 $REVISION \$");
  404. print "Copyright (c) 2003 David Alden
  405. Check if processes are running on a host via snmp
  406. ";
  407. print_usage();
  408. print "
  409. -a, --authprotocol=<authprotocol>
  410. Set the authentication protocol used for authenticated SNMPv3 messages
  411. -A, --authpassword=<authpassword>
  412. Set the authentication pass phrase used for authenticated SNMPv3 messages
  413. -c, --critical=<low>:<high>[,<low>:<high> ...]
  414. exit with CRITICAL status if number of processes is between <low> and <high>
  415. -C, --community=<snmp_community>
  416. SNMP read community (default: $opt_community)
  417. -h, --help
  418. Show this help screen
  419. -H, --host=<host>
  420. Check processes on the indiciated host
  421. -o, --oidname=<oidname>
  422. Which oid tree to search, hrSWRunName or hrSWRunPath (default: $opt_oidname)
  423. -p, --port=<port>
  424. Make connection on the indicated port (default: $opt_port)
  425. -N, --names=<processname>[:<minimum>][,<processname>[:<minimum>] ...]
  426. Process names to check, (optional) minimum number of processes (default: 1)
  427. -P, --password=<privpassword>
  428. Set the privacy pass phrase used for encrypted SNMPv3 messages
  429. -r, --regex
  430. Use regular expression match for <process>
  431. -S, --snmpversion
  432. Use snmp version specified (values: 1|2c|3, default: $opt_snmp_version)
  433. -t, --timeout
  434. Plugin time out in seconds (default: $opt_timeout)
  435. -U, --username=<securityname>
  436. Set the securityname used for encrypted SNMPv3 messages
  437. -v, --verbose
  438. Print some extra debugging information (not advised for normal operation)
  439. -V, --version
  440. Show version and license information
  441. -w, --warning=<low>:<high>[,<low>:<high> ...]
  442. exit with WARNING status if number of processes is between <low> and <high>
  443. A CRITICAL error will be indicated unless there are at least <minimum> number
  444. of processes running (unless <minimum> is set to 0 -- useful if you don't
  445. mind that there are none of the processes running).
  446. If no processes are specified, the program will still connect to the remote
  447. host and download the current list of running processes. It will then exit
  448. with an OK (unless it wasn't able to connect) -- useful if you want to make
  449. sure that the remote snmpd process is running and returning a list of procs.
  450. ";
  451. support();
  452. }