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