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 const char *progname = "check_procs";
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; /* bitmask of filter criteria to test against */
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 = "";
76 char *prog = "";
77 char *args = "";
78 char *fmt = "";
79 char tmp[MAX_INPUT_BUFFER];
80 const char *zombie = "Z";
82 int
83 main (int argc, char **argv)
84 {
85 char input_buffer[MAX_INPUT_BUFFER];
87 int procuid = 0;
88 int procppid = 0;
89 char procstat[8];
90 char procprog[MAX_INPUT_BUFFER];
91 char *procargs;
93 int resultsum = 0; /* bitmask of the filter criteria met by a process */
94 int found = 0; /* counter for number of lines returned in `ps` output */
95 int procs = 0; /* counter for number of processes meeting filter criteria */
96 int pos; /* number of spaces before 'args' in `ps` output */
97 int cols; /* number of columns in ps output */
99 int result = STATE_UNKNOWN;
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 #ifdef USE_PS_VARS
121 cols = sscanf (input_buffer, PS_FORMAT, PS_VARLIST);
122 #else
123 cols = sscanf (input_buffer, PS_FORMAT, procstat, &procuid,
124 &procppid, &pos, procprog);
125 #endif
126 /* Zombie processes do not give a procprog command */
127 if ( cols == 3 && strstr(procstat, zombie) ) {
128 strcpy(procprog, "");
129 cols = 4;
130 }
131 if ( cols >= 4 ) {
132 found++;
133 resultsum = 0;
134 asprintf (&procargs, "%s", input_buffer + pos);
135 strip (procargs);
136 if ((options & STAT) && (strstr (statopts, procstat)))
137 resultsum |= STAT;
138 if ((options & ARGS) && procargs && (strstr (procargs, args) == procargs))
139 resultsum |= ARGS;
140 if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0))
141 resultsum |= PROG;
142 if ((options & PPID) && (procppid == ppid))
143 resultsum |= PPID;
144 if ((options & USER) && (procuid == uid))
145 resultsum |= USER;
146 #ifdef DEBUG1
147 if (procargs == NULL)
148 printf ("%d %d %d %s %s\n", procs, procuid, procppid, procstat,
149 procprog);
150 else
151 printf ("%d %d %d %s %s %s\n", procs, procuid, procppid, procstat,
152 procprog, procargs);
153 #endif
154 if (options == resultsum)
155 procs++;
156 }
157 /* This should not happen */
158 else if (verbose) {
159 printf("Not parseable: %s", input_buffer);
160 }
161 }
163 /* If we get anything on STDERR, at least set warning */
164 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
165 if (verbose)
166 printf ("STDERR: %s", input_buffer);
167 /*Cannot use max() any more as STATE_UNKNOWN is gt STATE_CRITICAL
168 result = max (result, STATE_WARNING); */
169 if ( !(result == STATE_CRITICAL) ) {
170 result = STATE_WARNING;
171 }
172 printf ("System call sent warnings to stderr\n");
173 }
175 /* if (result == STATE_UNKNOWN || result == STATE_WARNING)
176 printf ("System call sent warnings to stderr\n");
177 */
178 (void) fclose (child_stderr);
180 /* close the pipe */
181 if (spclose (child_process)) {
182 printf ("System call returned nonzero status\n");
183 if ( !(result == STATE_CRITICAL) ) {
184 return STATE_WARNING;
185 }
186 else {
187 return result ;
188 }
189 }
191 if (options == ALL)
192 procs = found;
194 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
195 printf ("Unable to read output\n");
197 return result;
198 }
200 if (verbose && (options & STAT))
201 printf ("%s ", statopts);
202 if (verbose && (options & PROG))
203 printf ("%s ", prog);
204 if (verbose && (options & PPID))
205 printf ("%d ", ppid);
206 if (verbose && (options & USER))
207 printf ("%d ", uid);
209 if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
210 if (result == STATE_UNKNOWN)
211 result = STATE_OK;
212 printf (fmt, "OK", procs);
213 return result;
214 }
215 else if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
216 if (procs > cmax && procs < cmin) {
217 printf (fmt, "CRITICAL", procs);
218 return STATE_CRITICAL;
219 }
220 }
221 else if (cmax >= 0 && procs > cmax) {
222 printf (fmt, "CRITICAL", procs);
223 return STATE_CRITICAL;
224 }
225 else if (cmin >= 0 && procs < cmin) {
226 printf (fmt, "CRITICAL", procs);
227 return STATE_CRITICAL;
228 }
230 if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
231 if (procs > wmax && procs < wmin) {
232 printf (fmt, "CRITICAL", procs);
233 return STATE_CRITICAL;
234 }
235 }
236 else if (wmax >= 0 && procs > wmax) {
237 printf (fmt, "WARNING", procs);
238 if ( !(result == STATE_CRITICAL) ) {
239 return STATE_WARNING;
240 }
241 else {
242 return result ;
243 }
244 /*return max (result, STATE_WARNING); */
245 }
246 else if (wmin >= 0 && procs < wmin) {
247 printf (fmt, "WARNING", procs);
248 if ( !(result == STATE_CRITICAL) ) {
249 return STATE_WARNING;
250 }
251 else {
252 return result ;
253 }
254 /*return max (result, STATE_WARNING); */
255 }
257 printf (fmt, "OK", procs);
258 if ( result == STATE_UNKNOWN ) {
259 result = STATE_OK;
260 }
261 return result;
262 }
264 /* process command-line arguments */
265 int
266 process_arguments (int argc, char **argv)
267 {
268 int c = 1;
269 char *user;
270 struct passwd *pw;
271 #ifdef HAVE_GETOPT_H
272 int option_index = 0;
273 static struct option long_options[] = {
274 {"warning", required_argument, 0, 'w'},
275 {"critical", required_argument, 0, 'c'},
276 {"timeout", required_argument, 0, 't'},
277 {"status", required_argument, 0, 's'},
278 {"ppid", required_argument, 0, 'p'},
279 {"command", required_argument, 0, 'C'},
280 {"argument-array", required_argument, 0, 'a'},
281 {"help", no_argument, 0, 'h'},
282 {"version", no_argument, 0, 'V'},
283 {"verbose", no_argument, 0, 'v'},
284 {0, 0, 0, 0}
285 };
286 #endif
288 for (c = 1; c < argc; c++)
289 if (strcmp ("-to", argv[c]) == 0)
290 strcpy (argv[c], "-t");
292 while (1) {
293 #ifdef HAVE_GETOPT_H
294 c = getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:", long_options, &option_index);
295 #else
296 c = getopt (argc, argv, "Vvht:c:w:p:s:u:C:a:");
297 #endif
298 if (c == -1 || c == EOF)
299 break;
301 switch (c) {
302 case '?': /* help */
303 print_usage ();
304 exit (STATE_UNKNOWN);
305 case 'h': /* help */
306 print_help ();
307 exit (STATE_OK);
308 case 'V': /* version */
309 print_revision (progname, REVISION);
310 exit (STATE_OK);
311 case 't': /* timeout period */
312 if (!is_integer (optarg)) {
313 printf ("%s: Timeout Interval must be an integer!\n\n",
314 progname);
315 print_usage ();
316 exit (STATE_UNKNOWN);
317 }
318 timeout_interval = atoi (optarg);
319 break;
320 case 'c': /* critical threshold */
321 if (is_integer (optarg)) {
322 cmax = atoi (optarg);
323 break;
324 }
325 else if (sscanf (optarg, ":%d", &cmax) == 1) {
326 break;
327 }
328 else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2) {
329 break;
330 }
331 else if (sscanf (optarg, "%d:", &cmin) == 1) {
332 break;
333 }
334 else {
335 printf ("%s: Critical Process Count must be an integer!\n\n",
336 progname);
337 print_usage ();
338 exit (STATE_UNKNOWN);
339 }
340 case 'w': /* warning time threshold */
341 if (is_integer (optarg)) {
342 wmax = atoi (optarg);
343 break;
344 }
345 else if (sscanf (optarg, ":%d", &wmax) == 1) {
346 break;
347 }
348 else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2) {
349 break;
350 }
351 else if (sscanf (optarg, "%d:", &wmin) == 1) {
352 break;
353 }
354 else {
355 printf ("%s: Warning Process Count must be an integer!\n\n",
356 progname);
357 print_usage ();
358 exit (STATE_UNKNOWN);
359 }
360 case 'p': /* process id */
361 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
362 asprintf (&fmt, "%s%sPPID = %d", fmt, (options ? ", " : ""), ppid);
363 options |= PPID;
364 break;
365 }
366 printf ("%s: Parent Process ID must be an integer!\n\n",
367 progname);
368 print_usage ();
369 exit (STATE_UNKNOWN);
370 case 's': /* status */
371 asprintf (&statopts, "%s", optarg);
372 asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
373 options |= STAT;
374 break;
375 case 'u': /* user or user id */
376 if (is_integer (optarg)) {
377 uid = atoi (optarg);
378 pw = getpwuid ((uid_t) uid);
379 /* check to be sure user exists */
380 if (pw == NULL) {
381 printf ("UID %d was not found\n", uid);
382 print_usage ();
383 exit (STATE_UNKNOWN);
384 }
385 }
386 else {
387 pw = getpwnam (optarg);
388 /* check to be sure user exists */
389 if (pw == NULL) {
390 printf ("User name %s was not found\n", optarg);
391 print_usage ();
392 exit (STATE_UNKNOWN);
393 }
394 /* then get uid */
395 uid = pw->pw_uid;
396 }
397 user = pw->pw_name;
398 asprintf (&fmt, "%s%sUID = %d (%s)", fmt, (options ? ", " : ""),
399 uid, user);
400 options |= USER;
401 break;
402 case 'C': /* command */
403 asprintf (&prog, "%s", optarg);
404 asprintf (&fmt, "%s%scommand name %s", fmt, (options ? ", " : ""),
405 prog);
406 options |= PROG;
407 break;
408 case 'a': /* args (full path name with args) */
409 asprintf (&args, "%s", optarg);
410 asprintf (&fmt, "%s%sargs %s", fmt, (options ? ", " : ""), args);
411 options |= ARGS;
412 break;
413 case 'v': /* command */
414 verbose = TRUE;
415 break;
416 }
417 }
419 c = optind;
420 if (wmax == -1 && argv[c])
421 wmax = atoi (argv[c++]);
422 if (cmax == -1 && argv[c])
423 cmax = atoi (argv[c++]);
424 if (statopts == NULL && argv[c]) {
425 asprintf (&statopts, "%s", argv[c++]);
426 asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
427 options |= STAT;
428 }
430 return validate_arguments ();
431 }
434 int
435 validate_arguments ()
436 {
438 if (wmax >= 0 && wmin == -1)
439 wmin = 0;
440 if (cmax >= 0 && cmin == -1)
441 cmin = 0;
442 if (wmax >= wmin && cmax >= cmin) { /* standard ranges */
443 if (wmax > cmax && cmax != -1) {
444 printf ("wmax (%d) cannot be greater than cmax (%d)\n", wmax, cmax);
445 return ERROR;
446 }
447 if (cmin > wmin && wmin != -1) {
448 printf ("wmin (%d) cannot be less than cmin (%d)\n", wmin, cmin);
449 return ERROR;
450 }
451 }
453 /* if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) { */
454 /* printf ("At least one threshold must be set\n"); */
455 /* return ERROR; */
456 /* } */
458 if (options == 0) {
459 options = 1;
460 asprintf (&fmt, "%%s - %%d processes running\n");
461 }
462 else {
463 asprintf (&fmt, "%%s - %%d processes running with %s\n", fmt);
464 }
466 return options;
467 }
470 void
471 print_help (void)
472 {
473 print_revision (progname, REVISION);
474 printf
475 ("Copyright (c) %s %s <%s>\n\n%s\n",
476 COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
477 print_usage ();
478 printf
479 ("\nRequired Arguments:\n"
480 " -w, --warning=RANGE\n"
481 " generate warning state if process count is outside this range\n"
482 " -c, --critical=RANGE\n"
483 " generate critical state if process count is outside this range\n\n"
484 "Optional Filters:\n"
485 " -s, --state=STATUSFLAGS\n"
486 " Only scan for processes that have, in the output of `ps`, one or\n"
487 " more of the status flags you specify (for example R, Z, S, RS,\n"
488 " RSZDT, plus others based on the output of your 'ps' command).\n"
489 " -p, --ppid=PPID\n"
490 " Only scan for children of the parent process ID indicated.\n"
491 " -u, --user=USER\n"
492 " Only scan for proceses with user name or ID indicated.\n"
493 " -a, --argument-array=STRING\n"
494 " Only scan for ARGS that match up to the length of the given STRING\n"
495 " -C, --command=COMMAND\n"
496 " Only scan for exact matches to the named COMMAND.\n\n"
497 "RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n"
498 "specified 'max:min', a warning status will be generated if the\n"
500 "count is inside the specified range\n");}
503 void
504 print_usage (void)
505 {
506 printf
507 ("Usage:\n"
508 " check_procs -w <range> -c <range> [-s state] [-p ppid] [-u user]\n"
509 " [-a argument-array] [-C command]\n"
510 " check_procs --version\n" " check_procs --help\n");
511 }