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 result = max_state (result, STATE_WARNING);
168 printf ("System call sent warnings to stderr\n");
169 }
171 (void) fclose (child_stderr);
173 /* close the pipe */
174 if (spclose (child_process)) {
175 printf ("System call returned nonzero status\n");
176 result = max_state (result, STATE_WARNING);
177 }
179 if (options == ALL)
180 procs = found;
182 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
183 printf ("Unable to read output\n");
185 return result;
186 }
188 if (verbose && (options & STAT))
189 printf ("%s ", statopts);
190 if (verbose && (options & PROG))
191 printf ("%s ", prog);
192 if (verbose && (options & PPID))
193 printf ("%d ", ppid);
194 if (verbose && (options & USER))
195 printf ("%d ", uid);
197 if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
198 if (result == STATE_UNKNOWN)
199 result = STATE_OK;
200 printf (fmt, "OK", procs);
201 return result;
202 }
203 else if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
204 if (procs > cmax && procs < cmin) {
205 printf (fmt, "CRITICAL", procs);
206 return STATE_CRITICAL;
207 }
208 }
209 else if (cmax >= 0 && procs > cmax) {
210 printf (fmt, "CRITICAL", procs);
211 return STATE_CRITICAL;
212 }
213 else if (cmin >= 0 && procs < cmin) {
214 printf (fmt, "CRITICAL", procs);
215 return STATE_CRITICAL;
216 }
218 if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
219 if (procs > wmax && procs < wmin) {
220 printf (fmt, "CRITICAL", procs);
221 return STATE_CRITICAL;
222 }
223 }
224 else if (wmax >= 0 && procs > wmax) {
225 printf (fmt, "WARNING", procs);
226 return max_state (result, STATE_WARNING);
227 }
228 else if (wmin >= 0 && procs < wmin) {
229 printf (fmt, "WARNING", procs);
230 return max_state (result, STATE_WARNING);
231 }
233 printf (fmt, "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 = 1;
245 char *user;
246 struct passwd *pw;
247 int option_index = 0;
248 static struct option long_options[] = {
249 {"warning", required_argument, 0, 'w'},
250 {"critical", required_argument, 0, 'c'},
251 {"timeout", required_argument, 0, 't'},
252 {"status", required_argument, 0, 's'},
253 {"ppid", required_argument, 0, 'p'},
254 {"command", required_argument, 0, 'C'},
255 {"argument-array", required_argument, 0, 'a'},
256 {"help", no_argument, 0, 'h'},
257 {"version", no_argument, 0, 'V'},
258 {"verbose", no_argument, 0, 'v'},
259 {0, 0, 0, 0}
260 };
262 for (c = 1; c < argc; c++)
263 if (strcmp ("-to", argv[c]) == 0)
264 strcpy (argv[c], "-t");
266 while (1) {
267 c = getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:", long_options, &option_index);
269 if (c == -1 || c == EOF)
270 break;
272 switch (c) {
273 case '?': /* help */
274 print_usage ();
275 exit (STATE_UNKNOWN);
276 case 'h': /* help */
277 print_help ();
278 exit (STATE_OK);
279 case 'V': /* version */
280 print_revision (progname, REVISION);
281 exit (STATE_OK);
282 case 't': /* timeout period */
283 if (!is_integer (optarg)) {
284 printf ("%s: Timeout Interval must be an integer!\n\n",
285 progname);
286 print_usage ();
287 exit (STATE_UNKNOWN);
288 }
289 timeout_interval = atoi (optarg);
290 break;
291 case 'c': /* critical threshold */
292 if (is_integer (optarg)) {
293 cmax = atoi (optarg);
294 break;
295 }
296 else if (sscanf (optarg, ":%d", &cmax) == 1) {
297 break;
298 }
299 else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2) {
300 break;
301 }
302 else if (sscanf (optarg, "%d:", &cmin) == 1) {
303 break;
304 }
305 else {
306 printf ("%s: Critical Process Count must be an integer!\n\n",
307 progname);
308 print_usage ();
309 exit (STATE_UNKNOWN);
310 }
311 case 'w': /* warning time threshold */
312 if (is_integer (optarg)) {
313 wmax = atoi (optarg);
314 break;
315 }
316 else if (sscanf (optarg, ":%d", &wmax) == 1) {
317 break;
318 }
319 else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2) {
320 break;
321 }
322 else if (sscanf (optarg, "%d:", &wmin) == 1) {
323 break;
324 }
325 else {
326 printf ("%s: Warning Process Count must be an integer!\n\n",
327 progname);
328 print_usage ();
329 exit (STATE_UNKNOWN);
330 }
331 case 'p': /* process id */
332 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
333 asprintf (&fmt, "%s%sPPID = %d", fmt, (options ? ", " : ""), ppid);
334 options |= PPID;
335 break;
336 }
337 printf ("%s: Parent Process ID must be an integer!\n\n",
338 progname);
339 print_usage ();
340 exit (STATE_UNKNOWN);
341 case 's': /* status */
342 asprintf (&statopts, "%s", optarg);
343 asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
344 options |= STAT;
345 break;
346 case 'u': /* user or user id */
347 if (is_integer (optarg)) {
348 uid = atoi (optarg);
349 pw = getpwuid ((uid_t) uid);
350 /* check to be sure user exists */
351 if (pw == NULL) {
352 printf ("UID %d was not found\n", uid);
353 print_usage ();
354 exit (STATE_UNKNOWN);
355 }
356 }
357 else {
358 pw = getpwnam (optarg);
359 /* check to be sure user exists */
360 if (pw == NULL) {
361 printf ("User name %s was not found\n", optarg);
362 print_usage ();
363 exit (STATE_UNKNOWN);
364 }
365 /* then get uid */
366 uid = pw->pw_uid;
367 }
368 user = pw->pw_name;
369 asprintf (&fmt, "%s%sUID = %d (%s)", fmt, (options ? ", " : ""),
370 uid, user);
371 options |= USER;
372 break;
373 case 'C': /* command */
374 asprintf (&prog, "%s", optarg);
375 asprintf (&fmt, "%s%scommand name %s", fmt, (options ? ", " : ""),
376 prog);
377 options |= PROG;
378 break;
379 case 'a': /* args (full path name with args) */
380 asprintf (&args, "%s", optarg);
381 asprintf (&fmt, "%s%sargs %s", fmt, (options ? ", " : ""), args);
382 options |= ARGS;
383 break;
384 case 'v': /* command */
385 verbose = TRUE;
386 break;
387 }
388 }
390 c = optind;
391 if (wmax == -1 && argv[c])
392 wmax = atoi (argv[c++]);
393 if (cmax == -1 && argv[c])
394 cmax = atoi (argv[c++]);
395 if (statopts == NULL && argv[c]) {
396 asprintf (&statopts, "%s", argv[c++]);
397 asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
398 options |= STAT;
399 }
401 return validate_arguments ();
402 }
405 int
406 validate_arguments ()
407 {
409 if (wmax >= 0 && wmin == -1)
410 wmin = 0;
411 if (cmax >= 0 && cmin == -1)
412 cmin = 0;
413 if (wmax >= wmin && cmax >= cmin) { /* standard ranges */
414 if (wmax > cmax && cmax != -1) {
415 printf ("wmax (%d) cannot be greater than cmax (%d)\n", wmax, cmax);
416 return ERROR;
417 }
418 if (cmin > wmin && wmin != -1) {
419 printf ("wmin (%d) cannot be less than cmin (%d)\n", wmin, cmin);
420 return ERROR;
421 }
422 }
424 /* if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) { */
425 /* printf ("At least one threshold must be set\n"); */
426 /* return ERROR; */
427 /* } */
429 if (options == 0) {
430 options = 1;
431 asprintf (&fmt, "%%s - %%d processes running\n");
432 }
433 else {
434 asprintf (&fmt, "%%s - %%d processes running with %s\n", fmt);
435 }
437 return options;
438 }
441 void
442 print_help (void)
443 {
444 print_revision (progname, REVISION);
445 printf
446 ("Copyright (c) %s %s <%s>\n\n%s\n",
447 COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
448 print_usage ();
449 printf
450 ("\nRequired Arguments:\n"
451 " -w, --warning=RANGE\n"
452 " generate warning state if process count is outside this range\n"
453 " -c, --critical=RANGE\n"
454 " generate critical state if process count is outside this range\n\n"
455 "Optional Filters:\n"
456 " -s, --state=STATUSFLAGS\n"
457 " Only scan for processes that have, in the output of `ps`, one or\n"
458 " more of the status flags you specify (for example R, Z, S, RS,\n"
459 " RSZDT, plus others based on the output of your 'ps' command).\n"
460 " -p, --ppid=PPID\n"
461 " Only scan for children of the parent process ID indicated.\n"
462 " -u, --user=USER\n"
463 " Only scan for proceses with user name or ID indicated.\n"
464 " -a, --argument-array=STRING\n"
465 " Only scan for ARGS that match up to the length of the given STRING\n"
466 " -C, --command=COMMAND\n"
467 " Only scan for exact matches to the named COMMAND.\n\n"
468 "RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n"
469 "specified 'max:min', a warning status will be generated if the\n"
471 "count is inside the specified range\n");}
474 void
475 print_usage (void)
476 {
477 printf
478 ("Usage:\n"
479 " check_procs -w <range> -c <range> [-s state] [-p ppid] [-u user]\n"
480 " [-a argument-array] [-C command]\n"
481 " check_procs --version\n" " check_procs --help\n");
482 }