1 /******************************************************************************
2 *
3 * CHECK_PROCS.C
4 *
5 * Program: Process plugin for Nagios
6 * License: GPL
7 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
8 *
9 * $Id$
10 *
11 * Description:
12 *
13 * This plugin checks the number of currently running processes and
14 * generates WARNING or CRITICAL states if the process count is outside
15 * the specified threshold ranges. The process count can be filtered by
16 * process owner, parent process PID, current state (e.g., 'Z'), or may
17 * be the total number of running processes
18 *
19 * License Information:
20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful, but
27 * WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29 * General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34 *
35 ******************************************************************************/
37 #define PROGNAME "check_snmp"
38 #define REVISION "$Revision$"
39 #define COPYRIGHT "1999-2002"
40 #define AUTHOR "Ethan Galstad"
41 #define EMAIL "nagios@nagios.org"
42 #define SUMMARY "Check the number of currently running processes and generates WARNING or\n\
43 CRITICAL states if the process count is outside the specified threshold\n\
44 ranges. The process count can be filtered by process owner, parent process\n\
45 PID, current state (e.g., 'Z'), or may be the total number of running\n\
46 processes\n"
48 #include "config.h"
49 #include <pwd.h>
50 #include "common.h"
51 #include "popen.h"
52 #include "utils.h"
54 int process_arguments (int, char **);
55 int validate_arguments (void);
56 void print_usage (void);
57 void print_help (void);
59 int wmax = -1;
60 int cmax = -1;
61 int wmin = -1;
62 int cmin = -1;
64 int options = 0;
65 #define ALL 1
66 #define STAT 2
67 #define PPID 4
68 #define USER 8
69 #define PROG 16
70 #define ARGS 32
72 int verbose = FALSE;
73 int uid;
74 int ppid;
75 char *statopts = NULL;
76 char *prog = NULL;
77 char *args = NULL;
78 char *fmt = NULL;
79 char tmp[MAX_INPUT_BUFFER];
81 int
82 main (int argc, char **argv)
83 {
84 char input_buffer[MAX_INPUT_BUFFER];
86 int procuid = 0;
87 int procppid = 0;
88 char procstat[8];
89 char procprog[MAX_INPUT_BUFFER];
90 char *procargs;
92 int resultsum = 0;
93 int found = 0;
94 int procs = 0;
95 int pos;
97 int result = STATE_UNKNOWN;
99 procargs = malloc (MAX_INPUT_BUFFER);
101 if (process_arguments (argc, argv) == ERROR)
102 usage ("Unable to parse command line\n");
104 /* run the command */
105 if (verbose)
106 printf ("%s\n", PS_COMMAND);
107 child_process = spopen (PS_COMMAND);
108 if (child_process == NULL) {
109 printf ("Could not open pipe: %s\n", PS_COMMAND);
110 return STATE_UNKNOWN;
111 }
113 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
114 if (child_stderr == NULL)
115 printf ("Could not open stderr for %s\n", PS_COMMAND);
117 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
119 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
120 if (
121 #ifdef USE_PS_VARS
122 sscanf (input_buffer, PS_FORMAT, PS_VARLIST) >= 4
123 #else
124 sscanf (input_buffer, PS_FORMAT, procstat, &procuid, &procppid, &pos,
125 procprog) >= 4
126 #endif
127 ) {
128 found++;
129 resultsum = 0;
130 procargs = strcpy (procargs, &input_buffer[pos]);
131 strip (procargs);
132 if ((options & STAT) && (strstr (statopts, procstat)))
133 resultsum |= STAT;
134 if ((options & ARGS) && (strstr (procargs, args) == procargs))
135 resultsum |= ARGS;
136 if ((options & PROG) && (strcmp (prog, procprog) == 0))
137 resultsum |= PROG;
138 if ((options & PPID) && (procppid == ppid))
139 resultsum |= PPID;
140 if ((options & USER) && (procuid == uid))
141 resultsum |= USER;
142 #ifdef DEBUG1
143 if (procargs == NULL)
144 printf ("%d %d %d %s %s\n", procs, procuid, procppid, procstat,
145 procprog);
146 else
147 printf ("%d %d %d %s %s %s\n", procs, procuid, procppid, procstat,
148 procprog, procargs);
149 #endif
150 if (options == resultsum)
151 procs++;
152 }
153 }
155 /* If we get anything on STDERR, at least set warning */
156 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
157 if (verbose)
158 printf ("STDERR: %s", input_buffer);
159 /*Cannot use max() any more as STATE_UNKNOWN is gt STATE_CRITICAL
160 result = max (result, STATE_WARNING); */
161 if ( !(result == STATE_CRITICAL) ) {
162 result = STATE_WARNING;
163 }
164 printf ("System call sent warnings to stderr\n");
165 }
167 /* if (result == STATE_UNKNOWN || result == STATE_WARNING)
168 printf ("System call sent warnings to stderr\n");
169 */
170 (void) fclose (child_stderr);
172 /* close the pipe */
173 if (spclose (child_process)) {
174 printf ("System call returned nonzero status\n");
175 if ( !(result == STATE_CRITICAL) ) {
176 return STATE_WARNING;
177 }
178 else {
179 return result ;
180 }
181 }
183 if (options == ALL)
184 procs = found;
186 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
187 printf ("Unable to read output\n");
189 return result;
190 }
192 if (verbose && (options & STAT))
193 printf ("%s ", statopts);
194 if (verbose && (options & PROG))
195 printf ("%s ", prog);
196 if (verbose && (options & PPID))
197 printf ("%d ", ppid);
198 if (verbose && (options & USER))
199 printf ("%d ", uid);
201 if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
202 if (result == STATE_UNKNOWN)
203 result = STATE_OK;
204 printf (fmt, "OK", procs);
205 return result;
206 }
207 else if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
208 if (procs > cmax && procs < cmin) {
209 printf (fmt, "CRITICAL", procs);
210 return STATE_CRITICAL;
211 }
212 }
213 else if (cmax >= 0 && procs > cmax) {
214 printf (fmt, "CRITICAL", procs);
215 return STATE_CRITICAL;
216 }
217 else if (cmin >= 0 && procs < cmin) {
218 printf (fmt, "CRITICAL", procs);
219 return STATE_CRITICAL;
220 }
222 if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
223 if (procs > wmax && procs < wmin) {
224 printf (fmt, "CRITICAL", procs);
225 return STATE_CRITICAL;
226 }
227 }
228 else if (wmax >= 0 && procs > wmax) {
229 printf (fmt, "WARNING", procs);
230 if ( !(result == STATE_CRITICAL) ) {
231 return STATE_WARNING;
232 }
233 else {
234 return result ;
235 }
236 /*return max (result, STATE_WARNING); */
237 }
238 else if (wmin >= 0 && procs < wmin) {
239 printf (fmt, "WARNING", procs);
240 if ( !(result == STATE_CRITICAL) ) {
241 return STATE_WARNING;
242 }
243 else {
244 return result ;
245 }
246 /*return max (result, STATE_WARNING); */
247 }
249 printf (fmt, "OK", procs);
250 if ( result == STATE_UNKNOWN ) {
251 result = STATE_OK;
252 }
253 return result;
254 }
256 /* process command-line arguments */
257 int
258 process_arguments (int argc, char **argv)
259 {
260 int c = 1;
261 char *user;
262 struct passwd *pw;
263 #ifdef HAVE_GETOPT_H
264 int option_index = 0;
265 static struct option long_options[] = {
266 {"warning", required_argument, 0, 'w'},
267 {"critical", required_argument, 0, 'c'},
268 {"timeout", required_argument, 0, 't'},
269 {"status", required_argument, 0, 's'},
270 {"ppid", required_argument, 0, 'p'},
271 {"command", required_argument, 0, 'C'},
272 {"argument-array", required_argument, 0, 'a'},
273 {"help", no_argument, 0, 'h'},
274 {"version", no_argument, 0, 'V'},
275 {"verbose", no_argument, 0, 'v'},
276 {0, 0, 0, 0}
277 };
278 #endif
280 asprintf (&fmt, "");
282 for (c = 1; c < argc; c++)
283 if (strcmp ("-to", argv[c]) == 0)
284 strcpy (argv[c], "-t");
286 while (1) {
287 #ifdef HAVE_GETOPT_H
288 c = getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:", long_options, &option_index);
289 #else
290 c = getopt (argc, argv, "Vvht:c:w:p:s:u:C:a:");
291 #endif
292 if (c == -1 || c == EOF)
293 break;
295 switch (c) {
296 case '?': /* help */
297 print_usage ();
298 exit (STATE_UNKNOWN);
299 case 'h': /* help */
300 print_help ();
301 exit (STATE_OK);
302 case 'V': /* version */
303 print_revision (PROGNAME, REVISION);
304 exit (STATE_OK);
305 case 't': /* timeout period */
306 if (!is_integer (optarg)) {
307 printf ("%s: Timeout Interval must be an integer!\n\n",
308 my_basename (argv[0]));
309 print_usage ();
310 exit (STATE_UNKNOWN);
311 }
312 timeout_interval = atoi (optarg);
313 break;
314 case 'c': /* critical threshold */
315 if (is_integer (optarg)) {
316 cmax = atoi (optarg);
317 break;
318 }
319 else if (sscanf (optarg, ":%d", &cmax) == 1) {
320 break;
321 }
322 else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2) {
323 break;
324 }
325 else if (sscanf (optarg, "%d:", &cmin) == 1) {
326 break;
327 }
328 else {
329 printf ("%s: Critical Process Count must be an integer!\n\n",
330 my_basename (argv[0]));
331 print_usage ();
332 exit (STATE_UNKNOWN);
333 }
334 case 'w': /* warning time threshold */
335 if (is_integer (optarg)) {
336 wmax = atoi (optarg);
337 break;
338 }
339 else if (sscanf (optarg, ":%d", &wmax) == 1) {
340 break;
341 }
342 else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2) {
343 break;
344 }
345 else if (sscanf (optarg, "%d:", &wmin) == 1) {
346 break;
347 }
348 else {
349 printf ("%s: Warning Process Count must be an integer!\n\n",
350 my_basename (argv[0]));
351 print_usage ();
352 exit (STATE_UNKNOWN);
353 }
354 case 'p': /* process id */
355 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
356 asprintf (&fmt, "%s%sPPID = %d", (options ? ", " : ""), ppid);
357 options |= PPID;
358 break;
359 }
360 printf ("%s: Parent Process ID must be an integer!\n\n",
361 my_basename (argv[0]));
362 print_usage ();
363 exit (STATE_UNKNOWN);
364 case 's': /* status */
365 asprintf (&statopts, "%s", optarg);
366 asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
367 options |= STAT;
368 break;
369 case 'u': /* user or user id */
370 if (is_integer (optarg)) {
371 uid = atoi (optarg);
372 pw = getpwuid ((uid_t) uid);
373 /* check to be sure user exists */
374 if (pw == NULL) {
375 printf ("UID %d was not found\n", uid);
376 print_usage ();
377 exit (STATE_UNKNOWN);
378 }
379 }
380 else {
381 pw = getpwnam (optarg);
382 /* check to be sure user exists */
383 if (pw == NULL) {
384 printf ("User name %s was not found\n", optarg);
385 print_usage ();
386 exit (STATE_UNKNOWN);
387 }
388 /* then get uid */
389 uid = pw->pw_uid;
390 }
391 user = pw->pw_name;
392 asprintf (&fmt, "%s%sUID = %d (%s)", (options ? ", " : ""), fmt,
393 uid, user);
394 options |= USER;
395 break;
396 case 'C': /* command */
397 asprintf (&prog, "%s", optarg);
398 asprintf (&fmt, "%s%scommand name %s", fmt, (options ? ", " : ""),
399 prog);
400 options |= PROG;
401 break;
402 case 'a': /* args (full path name with args) */
403 asprintf (&args, "%s", optarg);
404 asprintf (&fmt, "%s%sargs %s", fmt, (options ? ", " : ""), args);
405 options |= ARGS;
406 break;
407 case 'v': /* command */
408 verbose = TRUE;
409 break;
410 }
411 }
413 c = optind;
414 if (wmax == -1 && argv[c])
415 wmax = atoi (argv[c++]);
416 if (cmax == -1 && argv[c])
417 cmax = atoi (argv[c++]);
418 if (statopts == NULL && argv[c]) {
419 asprintf (&statopts, "%s", argv[c++]);
420 asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
421 options |= STAT;
422 }
424 return validate_arguments ();
425 }
428 int
429 validate_arguments ()
430 {
432 if (wmax >= 0 && wmin == -1)
433 wmin = 0;
434 if (cmax >= 0 && cmin == -1)
435 cmin = 0;
436 if (wmax >= wmin && cmax >= cmin) { /* standard ranges */
437 if (wmax > cmax && cmax != -1) {
438 printf ("wmax (%d) cannot be greater than cmax (%d)\n", wmax, cmax);
439 return ERROR;
440 }
441 if (cmin > wmin && wmin != -1) {
442 printf ("wmin (%d) cannot be less than cmin (%d)\n", wmin, cmin);
443 return ERROR;
444 }
445 }
447 /* if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) { */
448 /* printf ("At least one threshold must be set\n"); */
449 /* return ERROR; */
450 /* } */
452 if (options == 0) {
453 options = 1;
454 asprintf (&fmt, "%%s - %%d processes running\n");
455 }
456 else {
457 asprintf (&fmt, "%%s - %%d processes running with %s\n", fmt);
458 }
460 return options;
461 }
464 void
465 print_help (void)
466 {
467 print_revision (PROGNAME, REVISION);
468 printf
469 ("Copyright (c) %s %s <%s>\n\n%s\n",
470 COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
471 print_usage ();
472 printf
473 ("\nRequired Arguments:\n"
474 " -w, --warning=RANGE\n"
475 " generate warning state if process count is outside this range\n"
476 " -c, --critical=RANGE\n"
477 " generate critical state if process count is outside this range\n\n"
478 "Optional Filters:\n"
479 " -s, --state=STATUSFLAGS\n"
480 " Only scan for processes that have, in the output of `ps`, one or\n"
481 " more of the status flags you specify (for example R, Z, S, RS,\n"
482 " RSZDT, plus others based on the output of your 'ps' command).\n"
483 " -p, --ppid=PPID\n"
484 " Only scan for children of the parent process ID indicated.\n"
485 " -u, --user=USER\n"
486 " Only scan for proceses with user name or ID indicated.\n"
487 " -a, --argument-array=STRING\n"
488 " Only scan for ARGS that match up to the length of the given STRING\n"
489 " -C, --command=COMMAND\n"
490 " Only scan for exact matches to the named COMMAND.\n\n"
491 "RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n"
492 "specified 'max:min', a warning status will be generated if the\n"
494 "count is inside the specified range\n");}
497 void
498 print_usage (void)
499 {
500 printf
501 ("Usage:\n"
502 " check_procs -w <range> -c <range> [-s state] [-p ppid] [-u user]\n"
503 " [-a argument-array] [-C command]\n"
504 " check_procs --version\n" " check_procs --help\n");
505 }