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