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 #include "config.h"
38 #include <pwd.h>
39 #include "common.h"
40 #include "popen.h"
41 #include "utils.h"
43 int process_arguments (int, char **);
44 int call_getopt (int, char **);
45 int validate_arguments (void);
46 void print_usage (void);
47 void print_help (char *);
49 int wmax = -1;
50 int cmax = -1;
51 int wmin = -1;
52 int cmin = -1;
54 int options = 0;
55 #define ALL 1
56 #define STAT 2
57 #define PPID 4
58 #define USER 8
59 #define PROG 16
60 #define ARGS 32
62 int verbose = FALSE;
63 int uid;
64 int ppid;
65 char *statopts = NULL;
66 char *prog = NULL;
67 char *args = NULL;
68 char *format = NULL;
69 char tmp[MAX_INPUT_BUFFER];
71 int
72 main (int argc, char **argv)
73 {
74 char input_buffer[MAX_INPUT_BUFFER];
76 int procuid = 0;
77 int procppid = 0;
78 char procstat[8];
79 char procprog[MAX_INPUT_BUFFER];
80 char *procargs;
82 int resultsum = 0;
83 int found = 0;
84 int procs = 0;
85 int pos;
87 int result = STATE_UNKNOWN;
89 procargs = malloc (MAX_INPUT_BUFFER);
91 if (process_arguments (argc, argv) == ERROR)
92 usage ("Unable to parse command line\n");
94 /* run the command */
95 if (verbose)
96 printf ("%s\n", PS_COMMAND);
97 child_process = spopen (PS_COMMAND);
98 if (child_process == NULL) {
99 printf ("Could not open pipe: %s\n", PS_COMMAND);
100 return STATE_UNKNOWN;
101 }
103 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
104 if (child_stderr == NULL)
105 printf ("Could not open stderr for %s\n", PS_COMMAND);
107 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
109 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
110 if (
111 #ifdef USE_PS_VARS
112 sscanf (input_buffer, PS_FORMAT, PS_VARLIST) >= 4
113 #else
114 sscanf (input_buffer, PS_FORMAT, procstat, &procuid, &procppid, &pos,
115 procprog) >= 4
116 #endif
117 ) {
118 found++;
119 resultsum = 0;
120 procargs = strcpy (procargs, &input_buffer[pos]);
121 strip (procargs);
122 if ((options & STAT) && (strstr (statopts, procstat)))
123 resultsum |= STAT;
124 if ((options & ARGS) && (strstr (procargs, args) == procargs))
125 resultsum |= ARGS;
126 if ((options & PROG) && (strcmp (prog, procprog) == 0))
127 resultsum |= PROG;
128 if ((options & PPID) && (procppid == ppid))
129 resultsum |= PPID;
130 if ((options & USER) && (procuid == uid))
131 resultsum |= USER;
132 #ifdef DEBUG1
133 if (procargs == NULL)
134 printf ("%d %d %d %s %s\n", procs, procuid, procppid, procstat,
135 procprog);
136 else
137 printf ("%d %d %d %s %s %s\n", procs, procuid, procppid, procstat,
138 procprog, procargs);
139 #endif
140 if (options == resultsum)
141 procs++;
142 }
143 }
145 /* If we get anything on STDERR, at least set warning */
146 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
147 if (verbose)
148 printf ("STDERR: %s", input_buffer);
149 /*Cannot use max() any more as STATE_UNKNOWN is gt STATE_CRITICAL
150 result = max (result, STATE_WARNING); */
151 if ( !(result == STATE_CRITICAL) ) {
152 result = STATE_WARNING;
153 }
154 printf ("System call sent warnings to stderr\n");
155 }
157 /* if (result == STATE_UNKNOWN || result == STATE_WARNING)
158 printf ("System call sent warnings to stderr\n");
159 */
160 (void) fclose (child_stderr);
162 /* close the pipe */
163 if (spclose (child_process)) {
164 printf ("System call returned nonzero status\n");
165 if ( !(result == STATE_CRITICAL) ) {
166 return STATE_WARNING;
167 }
168 else {
169 return result ;
170 }
171 }
173 if (options == ALL)
174 procs = found;
176 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
177 printf ("Unable to read output\n");
179 return result;
180 }
182 if (verbose && (options & STAT))
183 printf ("%s ", statopts);
184 if (verbose && (options & PROG))
185 printf ("%s ", prog);
186 if (verbose && (options & PPID))
187 printf ("%d ", ppid);
188 if (verbose && (options & USER))
189 printf ("%d ", uid);
191 if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
192 if (procs > cmax && procs < cmin) {
193 printf (format, "CRITICAL", procs);
194 return STATE_CRITICAL;
195 }
196 }
197 else if (cmax >= 0 && procs > cmax) {
198 printf (format, "CRITICAL", procs);
199 return STATE_CRITICAL;
200 }
201 else if (cmin >= 0 && procs < cmin) {
202 printf (format, "CRITICAL", procs);
203 return STATE_CRITICAL;
204 }
206 if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
207 if (procs > wmax && procs < wmin) {
208 printf (format, "CRITICAL", procs);
209 return STATE_CRITICAL;
210 }
211 }
212 else if (wmax >= 0 && procs > wmax) {
213 printf (format, "WARNING", procs);
214 if ( !(result == STATE_CRITICAL) ) {
215 return STATE_WARNING;
216 }
217 else {
218 return result ;
219 }
220 /*return max (result, STATE_WARNING); */
221 }
222 else if (wmin >= 0 && procs < wmin) {
223 printf (format, "WARNING", procs);
224 if ( !(result == STATE_CRITICAL) ) {
225 return STATE_WARNING;
226 }
227 else {
228 return result ;
229 }
230 /*return max (result, STATE_WARNING); */
231 }
233 printf (format, "OK", procs);
234 if ( result == STATE_UNKNOWN ) {
235 result = STATE_OK;
236 }
237 return result;
238 }
240 /* process command-line arguments */
241 int
242 process_arguments (int argc, char **argv)
243 {
244 int c;
246 if (argc < 2)
247 return ERROR;
249 for (c = 1; c < argc; c++)
250 if (strcmp ("-to", argv[c]) == 0)
251 strcpy (argv[c], "-t");
253 c = 0;
254 while (c += (call_getopt (argc - c, &argv[c]))) {
255 if (argc <= c)
256 break;
257 if (wmax == -1)
258 wmax = atoi (argv[c]);
259 else if (cmax == -1)
260 cmax = atoi (argv[c]);
261 else if (statopts == NULL) {
262 statopts = strscpy (statopts, argv[c]);
263 format =
264 strscat (format,
265 ssprintf (NULL, "%sSTATE = %s", (options ? ", " : ""),
266 statopts));
267 options |= STAT;
268 }
269 }
271 return validate_arguments ();
272 }
274 int
275 call_getopt (int argc, char **argv)
276 {
277 int c, i = 1;
278 char *user;
279 struct passwd *pw;
280 #ifdef HAVE_GETOPT_H
281 int option_index = 0;
282 static struct option long_options[] = {
283 {"warning", required_argument, 0, 'w'},
284 {"critical", required_argument, 0, 'c'},
285 {"timeout", required_argument, 0, 't'},
286 {"status", required_argument, 0, 's'},
287 {"ppid", required_argument, 0, 'p'},
288 {"command", required_argument, 0, 'C'},
289 {"argument-array", required_argument, 0, 'a'},
290 {"help", no_argument, 0, 'h'},
291 {"version", no_argument, 0, 'V'},
292 {"verbose", no_argument, 0, 'v'},
293 {0, 0, 0, 0}
294 };
295 #endif
297 while (1) {
298 #ifdef HAVE_GETOPT_H
299 c =
300 getopt_long (argc, argv, "+Vvht:c:w:p:s:u:C:a:", long_options,
301 &option_index);
302 #else
303 c = getopt (argc, argv, "+Vvht:c:w:p:s:u:C:a:");
304 #endif
306 if (c == EOF)
307 break;
309 i++;
310 switch (c) {
311 case 't':
312 case 'c':
313 case 'w':
314 case 'p':
315 case 's':
316 case 'a':
317 case 'u':
318 case 'C':
319 i++;
320 }
322 switch (c) {
323 case '?': /* help */
324 print_usage ();
325 exit (STATE_UNKNOWN);
326 case 'h': /* help */
327 print_help (my_basename (argv[0]));
328 exit (STATE_OK);
329 case 'V': /* version */
330 print_revision (my_basename (argv[0]), "$Revision$");
331 exit (STATE_OK);
332 case 't': /* timeout period */
333 if (!is_integer (optarg)) {
334 printf ("%s: Timeout Interval must be an integer!\n\n",
335 my_basename (argv[0]));
336 print_usage ();
337 exit (STATE_UNKNOWN);
338 }
339 timeout_interval = atoi (optarg);
340 break;
341 case 'c': /* critical threshold */
342 if (is_integer (optarg)) {
343 cmax = atoi (optarg);
344 break;
345 }
346 else if (sscanf (optarg, ":%d", &cmax) == 1) {
347 break;
348 }
349 else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2) {
350 break;
351 }
352 else if (sscanf (optarg, "%d:", &cmin) == 1) {
353 break;
354 }
355 else {
356 printf ("%s: Critical Process Count must be an integer!\n\n",
357 my_basename (argv[0]));
358 print_usage ();
359 exit (STATE_UNKNOWN);
360 }
361 case 'w': /* warning time threshold */
362 if (is_integer (optarg)) {
363 wmax = atoi (optarg);
364 break;
365 }
366 else if (sscanf (optarg, ":%d", &wmax) == 1) {
367 break;
368 }
369 else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2) {
370 break;
371 }
372 else if (sscanf (optarg, "%d:", &wmin) == 1) {
373 break;
374 }
375 else {
376 printf ("%s: Warning Process Count must be an integer!\n\n",
377 my_basename (argv[0]));
378 print_usage ();
379 exit (STATE_UNKNOWN);
380 }
381 case 'p': /* process id */
382 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
383 format =
384 strscat (format,
385 ssprintf (NULL, "%sPPID = %d", (options ? ", " : ""),
386 ppid));
387 options |= PPID;
388 break;
389 }
390 printf ("%s: Parent Process ID must be an integer!\n\n",
391 my_basename (argv[0]));
392 print_usage ();
393 exit (STATE_UNKNOWN);
394 case 's': /* status */
395 statopts = strscpy (statopts, optarg);
396 format =
397 strscat (format,
398 ssprintf (NULL, "%sSTATE = %s", (options ? ", " : ""),
399 statopts));
400 options |= STAT;
401 break;
402 case 'u': /* user or user id */
403 if (is_integer (optarg)) {
404 uid = atoi (optarg);
405 pw = getpwuid ((uid_t) uid);
406 /* check to be sure user exists */
407 if (pw == NULL) {
408 printf ("UID %d was not found\n", uid);
409 print_usage ();
410 exit (STATE_UNKNOWN);
411 }
412 }
413 else {
414 pw = getpwnam (optarg);
415 /* check to be sure user exists */
416 if (pw == NULL) {
417 printf ("User name %s was not found\n", optarg);
418 print_usage ();
419 exit (STATE_UNKNOWN);
420 }
421 /* then get uid */
422 uid = pw->pw_uid;
423 }
424 user = pw->pw_name;
425 format =
426 strscat (format,
427 ssprintf (NULL, "%sUID = %d (%s)", (options ? ", " : ""),
428 uid, user));
429 options |= USER;
430 break;
431 case 'C': /* command */
432 prog = strscpy (prog, optarg);
433 format =
434 strscat (format,
435 ssprintf (NULL, "%scommand name %s", (options ? ", " : ""),
436 prog));
437 options |= PROG;
438 break;
439 case 'a': /* args (full path name with args) */
440 args = strscpy (args, optarg);
441 format =
442 strscat (format,
443 ssprintf (NULL, "%sargs %s", (options ? ", " : ""), args));
444 options |= ARGS;
445 break;
446 case 'v': /* command */
447 verbose = TRUE;
448 break;
449 }
450 }
451 return i;
452 }
455 int
456 validate_arguments ()
457 {
459 if (wmax >= 0 && wmin == -1)
460 wmin = 0;
461 if (cmax >= 0 && cmin == -1)
462 cmin = 0;
463 if (wmax >= wmin && cmax >= cmin) { /* standard ranges */
464 if (wmax > cmax && cmax != -1) {
465 printf ("wmax (%d) cannot be greater than cmax (%d)\n", wmax, cmax);
466 return ERROR;
467 }
468 if (cmin > wmin && wmin != -1) {
469 printf ("wmin (%d) cannot be less than cmin (%d)\n", wmin, cmin);
470 return ERROR;
471 }
472 }
474 if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
475 printf ("At least one threshold must be set\n");
476 return ERROR;
477 }
479 if (options == 0) {
480 options = 1;
481 format = ssprintf (format, "%%s - %%d processes running\n");
482 }
483 else {
484 format =
485 ssprintf (format, "%%s - %%d processes running with %s\n", format);
486 }
488 return options;
489 }
492 void
493 print_help (char *cmd)
494 {
495 print_revision (cmd, "$Revision$");
496 printf
497 ("Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)\n\n"
498 "This plugin checks the number of currently running processes and\n"
499 "generates WARNING or CRITICAL states if the process count is outside\n"
500 "the specified threshold ranges. The process count can be filtered by\n"
501 "process owner, parent process PID, current state (e.g., 'Z'), or may\n"
502 "be the total number of running processes\n\n");
503 print_usage ();
504 printf
505 ("\nRequired Arguments:\n"
506 " -w, --warning=RANGE\n"
507 " generate warning state if process count is outside this range\n"
508 " -c, --critical=RANGE\n"
509 " generate critical state if process count is outside this range\n\n"
510 "Optional Filters:\n"
511 " -s, --state=STATUSFLAGS\n"
512 " Only scan for processes that have, in the output of `ps`, one or\n"
513 " more of the status flags you specify (for example R, Z, S, RS,\n"
514 " RSZDT, plus others based on the output of your 'ps' command).\n"
515 " -p, --ppid=PPID\n"
516 " Only scan for children of the parent process ID indicated.\n"
517 " -u, --user=USER\n"
518 " Only scan for proceses with user name or ID indicated.\n"
519 " -a, --argument-array=STRING\n"
520 " Only scan for ARGS that match up to the length of the given STRING\n"
521 " -C, --command=COMMAND\n"
522 " Only scan for exact matches to the named COMMAND.\n\n"
523 "RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n"
524 "specified 'max:min', a warning status will be generated if the\n"
526 "count is inside the specified range\n");}
529 void
530 print_usage (void)
531 {
532 printf
533 ("Usage:\n"
534 " check_procs -w <range> -c <range> [-s state] [-p ppid] [-u user]\n"
535 " [-a argument-array] [-C command]\n"
536 " check_procs --version\n" " check_procs --help\n");
537 }