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