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 int option_index = 0;
272 static struct option long_options[] = {
273 {"warning", required_argument, 0, 'w'},
274 {"critical", required_argument, 0, 'c'},
275 {"timeout", required_argument, 0, 't'},
276 {"status", required_argument, 0, 's'},
277 {"ppid", required_argument, 0, 'p'},
278 {"command", required_argument, 0, 'C'},
279 {"argument-array", required_argument, 0, 'a'},
280 {"help", no_argument, 0, 'h'},
281 {"version", no_argument, 0, 'V'},
282 {"verbose", no_argument, 0, 'v'},
283 {0, 0, 0, 0}
284 };
286 for (c = 1; c < argc; c++)
287 if (strcmp ("-to", argv[c]) == 0)
288 strcpy (argv[c], "-t");
290 while (1) {
291 c = getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:", long_options, &option_index);
293 if (c == -1 || c == EOF)
294 break;
296 switch (c) {
297 case '?': /* help */
298 print_usage ();
299 exit (STATE_UNKNOWN);
300 case 'h': /* help */
301 print_help ();
302 exit (STATE_OK);
303 case 'V': /* version */
304 print_revision (progname, REVISION);
305 exit (STATE_OK);
306 case 't': /* timeout period */
307 if (!is_integer (optarg)) {
308 printf ("%s: Timeout Interval must be an integer!\n\n",
309 progname);
310 print_usage ();
311 exit (STATE_UNKNOWN);
312 }
313 timeout_interval = atoi (optarg);
314 break;
315 case 'c': /* critical threshold */
316 if (is_integer (optarg)) {
317 cmax = atoi (optarg);
318 break;
319 }
320 else if (sscanf (optarg, ":%d", &cmax) == 1) {
321 break;
322 }
323 else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2) {
324 break;
325 }
326 else if (sscanf (optarg, "%d:", &cmin) == 1) {
327 break;
328 }
329 else {
330 printf ("%s: Critical Process Count must be an integer!\n\n",
331 progname);
332 print_usage ();
333 exit (STATE_UNKNOWN);
334 }
335 case 'w': /* warning time threshold */
336 if (is_integer (optarg)) {
337 wmax = atoi (optarg);
338 break;
339 }
340 else if (sscanf (optarg, ":%d", &wmax) == 1) {
341 break;
342 }
343 else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2) {
344 break;
345 }
346 else if (sscanf (optarg, "%d:", &wmin) == 1) {
347 break;
348 }
349 else {
350 printf ("%s: Warning Process Count must be an integer!\n\n",
351 progname);
352 print_usage ();
353 exit (STATE_UNKNOWN);
354 }
355 case 'p': /* process id */
356 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
357 asprintf (&fmt, "%s%sPPID = %d", fmt, (options ? ", " : ""), ppid);
358 options |= PPID;
359 break;
360 }
361 printf ("%s: Parent Process ID must be an integer!\n\n",
362 progname);
363 print_usage ();
364 exit (STATE_UNKNOWN);
365 case 's': /* status */
366 asprintf (&statopts, "%s", optarg);
367 asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
368 options |= STAT;
369 break;
370 case 'u': /* user or user id */
371 if (is_integer (optarg)) {
372 uid = atoi (optarg);
373 pw = getpwuid ((uid_t) uid);
374 /* check to be sure user exists */
375 if (pw == NULL) {
376 printf ("UID %d was not found\n", uid);
377 print_usage ();
378 exit (STATE_UNKNOWN);
379 }
380 }
381 else {
382 pw = getpwnam (optarg);
383 /* check to be sure user exists */
384 if (pw == NULL) {
385 printf ("User name %s was not found\n", optarg);
386 print_usage ();
387 exit (STATE_UNKNOWN);
388 }
389 /* then get uid */
390 uid = pw->pw_uid;
391 }
392 user = pw->pw_name;
393 asprintf (&fmt, "%s%sUID = %d (%s)", fmt, (options ? ", " : ""),
394 uid, user);
395 options |= USER;
396 break;
397 case 'C': /* command */
398 asprintf (&prog, "%s", optarg);
399 asprintf (&fmt, "%s%scommand name %s", fmt, (options ? ", " : ""),
400 prog);
401 options |= PROG;
402 break;
403 case 'a': /* args (full path name with args) */
404 asprintf (&args, "%s", optarg);
405 asprintf (&fmt, "%s%sargs %s", fmt, (options ? ", " : ""), args);
406 options |= ARGS;
407 break;
408 case 'v': /* command */
409 verbose = TRUE;
410 break;
411 }
412 }
414 c = optind;
415 if (wmax == -1 && argv[c])
416 wmax = atoi (argv[c++]);
417 if (cmax == -1 && argv[c])
418 cmax = atoi (argv[c++]);
419 if (statopts == NULL && argv[c]) {
420 asprintf (&statopts, "%s", argv[c++]);
421 asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
422 options |= STAT;
423 }
425 return validate_arguments ();
426 }
429 int
430 validate_arguments ()
431 {
433 if (wmax >= 0 && wmin == -1)
434 wmin = 0;
435 if (cmax >= 0 && cmin == -1)
436 cmin = 0;
437 if (wmax >= wmin && cmax >= cmin) { /* standard ranges */
438 if (wmax > cmax && cmax != -1) {
439 printf ("wmax (%d) cannot be greater than cmax (%d)\n", wmax, cmax);
440 return ERROR;
441 }
442 if (cmin > wmin && wmin != -1) {
443 printf ("wmin (%d) cannot be less than cmin (%d)\n", wmin, cmin);
444 return ERROR;
445 }
446 }
448 /* if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) { */
449 /* printf ("At least one threshold must be set\n"); */
450 /* return ERROR; */
451 /* } */
453 if (options == 0) {
454 options = 1;
455 asprintf (&fmt, "%%s - %%d processes running\n");
456 }
457 else {
458 asprintf (&fmt, "%%s - %%d processes running with %s\n", fmt);
459 }
461 return options;
462 }
465 void
466 print_help (void)
467 {
468 print_revision (progname, REVISION);
469 printf
470 ("Copyright (c) %s %s <%s>\n\n%s\n",
471 COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
472 print_usage ();
473 printf
474 ("\nRequired Arguments:\n"
475 " -w, --warning=RANGE\n"
476 " generate warning state if process count is outside this range\n"
477 " -c, --critical=RANGE\n"
478 " generate critical state if process count is outside this range\n\n"
479 "Optional Filters:\n"
480 " -s, --state=STATUSFLAGS\n"
481 " Only scan for processes that have, in the output of `ps`, one or\n"
482 " more of the status flags you specify (for example R, Z, S, RS,\n"
483 " RSZDT, plus others based on the output of your 'ps' command).\n"
484 " -p, --ppid=PPID\n"
485 " Only scan for children of the parent process ID indicated.\n"
486 " -u, --user=USER\n"
487 " Only scan for proceses with user name or ID indicated.\n"
488 " -a, --argument-array=STRING\n"
489 " Only scan for ARGS that match up to the length of the given STRING\n"
490 " -C, --command=COMMAND\n"
491 " Only scan for exact matches to the named COMMAND.\n\n"
492 "RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n"
493 "specified 'max:min', a warning status will be generated if the\n"
495 "count is inside the specified range\n");}
498 void
499 print_usage (void)
500 {
501 printf
502 ("Usage:\n"
503 " check_procs -w <range> -c <range> [-s state] [-p ppid] [-u user]\n"
504 " [-a argument-array] [-C command]\n"
505 " check_procs --version\n" " check_procs --help\n");
506 }