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 #include "config.h"
38 #include <pwd.h>
39 #include "common.h"
40 #include "popen.h"
41 #include "utils.h"
43 int process_arguments (int, char **);
44 int call_getopt (int, char **);
45 int validate_arguments (void);
46 void print_usage (void);
47 void print_help (char *);
49 int wmax = -1;
50 int cmax = -1;
51 int wmin = -1;
52 int cmin = -1;
54 int options = 0;
55 #define ALL 1
56 #define STAT 2
57 #define PPID 4
58 #define USER 8
59 #define PROG 16
60 #define ARGS 32
62 int verbose = FALSE;
63 int uid;
64 int ppid;
65 char *statopts = NULL;
66 char *prog = NULL;
67 char *args = NULL;
68 char *format = NULL;
69 char tmp[MAX_INPUT_BUFFER];
71 int
72 main (int argc, char **argv)
73 {
74 char input_buffer[MAX_INPUT_BUFFER];
76 int procuid = 0;
77 int procppid = 0;
78 char procstat[8];
79 char procprog[MAX_INPUT_BUFFER];
80 char *procargs;
82 int resultsum = 0;
83 int found = 0;
84 int procs = 0;
85 int pos;
87 int result = STATE_UNKNOWN;
89 procargs = malloc (MAX_INPUT_BUFFER);
91 if (process_arguments (argc, argv) == ERROR)
92 usage ("Unable to parse command line\n");
94 /* run the command */
95 if (verbose)
96 printf ("%s\n", PS_COMMAND);
97 child_process = spopen (PS_COMMAND);
98 if (child_process == NULL) {
99 printf ("Could not open pipe: %s\n", PS_COMMAND);
100 return STATE_UNKNOWN;
101 }
103 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
104 if (child_stderr == NULL)
105 printf ("Could not open stderr for %s\n", PS_COMMAND);
107 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
109 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
110 if (
111 #ifdef USE_PS_VARS
112 sscanf (input_buffer, PS_FORMAT, PS_VARLIST) >= 4
113 #else
114 sscanf (input_buffer, PS_FORMAT, procstat, &procuid, &procppid, &pos,
115 procprog) >= 4
116 #endif
117 ) {
118 found++;
119 resultsum = 0;
120 procargs = strcpy (procargs, &input_buffer[pos]);
121 strip (procargs);
122 if ((options & STAT) && (strstr (statopts, procstat)))
123 resultsum |= STAT;
124 if ((options & ARGS) && (strstr (procargs, args) == procargs))
125 resultsum |= ARGS;
126 if ((options & PROG) && (strcmp (prog, procprog) == 0))
127 resultsum |= PROG;
128 if ((options & PPID) && (procppid == ppid))
129 resultsum |= PPID;
130 if ((options & USER) && (procuid == uid))
131 resultsum |= USER;
132 #ifdef DEBUG1
133 if (procargs == NULL)
134 printf ("%d %d %d %s %s\n", procs, procuid, procppid, procstat,
135 procprog);
136 else
137 printf ("%d %d %d %s %s %s\n", procs, procuid, procppid, procstat,
138 procprog, procargs);
139 #endif
140 if (options == resultsum)
141 procs++;
142 }
143 }
145 /* If we get anything on STDERR, at least set warning */
146 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
147 if (verbose)
148 printf ("%s", input_buffer);
149 result = max (result, STATE_WARNING);
150 }
151 if (result > STATE_OK)
152 printf ("System call sent warnings to stderr\n");
154 (void) fclose (child_stderr);
156 /* close the pipe */
157 if (spclose (child_process)) {
158 printf ("System call returned nonzero status\n");
159 return max (result, STATE_WARNING);
160 }
162 if (options == ALL)
163 procs = found;
165 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
166 printf ("Unable to read output\n");
167 return max (result, STATE_UNKNOWN);
168 }
170 if (verbose && (options & STAT))
171 printf ("%s ", statopts);
172 if (verbose && (options & PROG))
173 printf ("%s ", prog);
174 if (verbose && (options & PPID))
175 printf ("%d ", ppid);
176 if (verbose && (options & USER))
177 printf ("%d ", uid);
179 if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
180 if (procs > cmax && procs < cmin) {
181 printf (format, "CRITICAL", procs);
182 return STATE_CRITICAL;
183 }
184 }
185 else if (cmax >= 0 && procs > cmax) {
186 printf (format, "CRITICAL", procs);
187 return STATE_CRITICAL;
188 }
189 else if (cmin >= 0 && procs < cmin) {
190 printf (format, "CRITICAL", procs);
191 return STATE_CRITICAL;
192 }
194 if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
195 if (procs > wmax && procs < wmin) {
196 printf (format, "CRITICAL", procs);
197 return STATE_CRITICAL;
198 }
199 }
200 else if (wmax >= 0 && procs > wmax) {
201 printf (format, "WARNING", procs);
202 return max (result, STATE_WARNING);
203 }
204 else if (wmin >= 0 && procs < wmin) {
205 printf (format, "WARNING", procs);
206 return max (result, STATE_WARNING);
207 }
209 printf (format, "OK", procs);
210 return max (result, STATE_OK);
211 }
213 /* process command-line arguments */
214 int
215 process_arguments (int argc, char **argv)
216 {
217 int c;
219 if (argc < 2)
220 return ERROR;
222 for (c = 1; c < argc; c++)
223 if (strcmp ("-to", argv[c]) == 0)
224 strcpy (argv[c], "-t");
226 c = 0;
227 while (c += (call_getopt (argc - c, &argv[c]))) {
228 if (argc <= c)
229 break;
230 if (wmax == -1)
231 wmax = atoi (argv[c]);
232 else if (cmax == -1)
233 cmax = atoi (argv[c]);
234 else if (statopts == NULL) {
235 statopts = strscpy (statopts, argv[c]);
236 format =
237 strscat (format,
238 ssprintf (NULL, "%sSTATE = %s", (options ? ", " : ""),
239 statopts));
240 options |= STAT;
241 }
242 }
244 return validate_arguments ();
245 }
247 int
248 call_getopt (int argc, char **argv)
249 {
250 int c, i = 1;
251 char *user;
252 struct passwd *pw;
253 #ifdef HAVE_GETOPT_H
254 int option_index = 0;
255 static struct option long_options[] = {
256 {"warning", required_argument, 0, 'w'},
257 {"critical", required_argument, 0, 'c'},
258 {"timeout", required_argument, 0, 't'},
259 {"status", required_argument, 0, 's'},
260 {"ppid", required_argument, 0, 'p'},
261 {"command", required_argument, 0, 'C'},
262 {"argument-array", required_argument, 0, 'a'},
263 {"help", no_argument, 0, 'h'},
264 {"version", no_argument, 0, 'V'},
265 {"verbose", no_argument, 0, 'v'},
266 {0, 0, 0, 0}
267 };
268 #endif
270 while (1) {
271 #ifdef HAVE_GETOPT_H
272 c =
273 getopt_long (argc, argv, "+Vvht:c:w:p:s:u:C:a:", long_options,
274 &option_index);
275 #else
276 c = getopt (argc, argv, "+Vvht:c:w:p:s:u:C:a:");
277 #endif
279 if (c == EOF)
280 break;
282 i++;
283 switch (c) {
284 case 't':
285 case 'c':
286 case 'w':
287 case 'p':
288 case 's':
289 case 'a':
290 case 'u':
291 case 'C':
292 i++;
293 }
295 switch (c) {
296 case '?': /* help */
297 print_usage ();
298 exit (STATE_UNKNOWN);
299 case 'h': /* help */
300 print_help (my_basename (argv[0]));
301 exit (STATE_OK);
302 case 'V': /* version */
303 print_revision (my_basename (argv[0]), "$Revision$");
304 exit (STATE_OK);
305 case 't': /* timeout period */
306 if (!is_integer (optarg)) {
307 printf ("%s: Timeout Interval must be an integer!\n\n",
308 my_basename (argv[0]));
309 print_usage ();
310 exit (STATE_UNKNOWN);
311 }
312 timeout_interval = atoi (optarg);
313 break;
314 case 'c': /* critical threshold */
315 if (is_integer (optarg)) {
316 cmax = atoi (optarg);
317 break;
318 }
319 else if (sscanf (optarg, ":%d", &cmax) == 1) {
320 break;
321 }
322 else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2) {
323 break;
324 }
325 else if (sscanf (optarg, "%d:", &cmin) == 1) {
326 break;
327 }
328 else {
329 printf ("%s: Critical Process Count must be an integer!\n\n",
330 my_basename (argv[0]));
331 print_usage ();
332 exit (STATE_UNKNOWN);
333 }
334 case 'w': /* warning time threshold */
335 if (is_integer (optarg)) {
336 wmax = atoi (optarg);
337 break;
338 }
339 else if (sscanf (optarg, ":%d", &wmax) == 1) {
340 break;
341 }
342 else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2) {
343 break;
344 }
345 else if (sscanf (optarg, "%d:", &wmin) == 1) {
346 break;
347 }
348 else {
349 printf ("%s: Warning Process Count must be an integer!\n\n",
350 my_basename (argv[0]));
351 print_usage ();
352 exit (STATE_UNKNOWN);
353 }
354 case 'p': /* process id */
355 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
356 format =
357 strscat (format,
358 ssprintf (NULL, "%sPPID = %d", (options ? ", " : ""),
359 ppid));
360 options |= PPID;
361 break;
362 }
363 printf ("%s: Parent Process ID must be an integer!\n\n",
364 my_basename (argv[0]));
365 print_usage ();
366 exit (STATE_UNKNOWN);
367 case 's': /* status */
368 statopts = strscpy (statopts, optarg);
369 format =
370 strscat (format,
371 ssprintf (NULL, "%sSTATE = %s", (options ? ", " : ""),
372 statopts));
373 options |= STAT;
374 break;
375 case 'u': /* user or user id */
376 if (is_integer (optarg)) {
377 uid = atoi (optarg);
378 pw = getpwuid ((uid_t) uid);
379 /* check to be sure user exists */
380 if (pw == NULL) {
381 printf ("UID %d was not found\n", uid);
382 print_usage ();
383 exit (STATE_UNKNOWN);
384 }
385 }
386 else {
387 pw = getpwnam (optarg);
388 /* check to be sure user exists */
389 if (pw == NULL) {
390 printf ("User name %s was not found\n", optarg);
391 print_usage ();
392 exit (STATE_UNKNOWN);
393 }
394 /* then get uid */
395 uid = pw->pw_uid;
396 }
397 user = pw->pw_name;
398 format =
399 strscat (format,
400 ssprintf (NULL, "%sUID = %d (%s)", (options ? ", " : ""),
401 uid, user));
402 options |= USER;
403 break;
404 case 'C': /* command */
405 prog = strscpy (prog, optarg);
406 format =
407 strscat (format,
408 ssprintf (NULL, "%scommand name %s", (options ? ", " : ""),
409 prog));
410 options |= PROG;
411 break;
412 case 'a': /* args (full path name with args) */
413 args = strscpy (args, optarg);
414 format =
415 strscat (format,
416 ssprintf (NULL, "%sargs %s", (options ? ", " : ""), args));
417 options |= ARGS;
418 break;
419 case 'v': /* command */
420 verbose = TRUE;
421 break;
422 }
423 }
424 return i;
425 }
428 int
429 validate_arguments ()
430 {
432 if (wmax >= 0 && wmin == -1)
433 wmin = 0;
434 if (cmax >= 0 && cmin == -1)
435 cmin = 0;
436 if (wmax >= wmin && cmax >= cmin) { /* standard ranges */
437 if (wmax > cmax && cmax != -1) {
438 printf ("wmax (%d) cannot be greater than cmax (%d)\n", wmax, cmax);
439 return ERROR;
440 }
441 if (cmin > wmin && wmin != -1) {
442 printf ("wmin (%d) cannot be less than cmin (%d)\n", wmin, cmin);
443 return ERROR;
444 }
445 }
447 if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
448 printf ("At least one threshold must be set\n");
449 return ERROR;
450 }
452 if (options == 0) {
453 options = 1;
454 format = ssprintf (format, "%%s - %%d processes running\n");
455 }
456 else {
457 format =
458 ssprintf (format, "%%s - %%d processes running with %s\n", format);
459 }
461 return options;
462 }
465 void
466 print_help (char *cmd)
467 {
468 print_revision (cmd, "$Revision$");
469 printf
470 ("Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)\n\n"
471 "This plugin checks the number of currently running processes and\n"
472 "generates WARNING or CRITICAL states if the process count is outside\n"
473 "the specified threshold ranges. The process count can be filtered by\n"
474 "process owner, parent process PID, current state (e.g., 'Z'), or may\n"
475 "be the total number of running processes\n\n");
476 print_usage ();
477 printf
478 ("\nRequired Arguments:\n"
479 " -w, --warning=RANGE\n"
480 " generate warning state if process count is outside this range\n"
481 " -c, --critical=RANGE\n"
482 " generate critical state if process count is outside this range\n\n"
483 "Optional Filters:\n"
484 " -s, --state=STATUSFLAGS\n"
485 " Only scan for processes that have, in the output of `ps`, one or\n"
486 " more of the status flags you specify (for example R, Z, S, RS,\n"
487 " RSZDT, plus others based on the output of your 'ps' command).\n"
488 " -p, --ppid=PPID\n"
489 " Only scan for children of the parent process ID indicated.\n"
490 " -u, --user=USER\n"
491 " Only scan for proceses with user name or ID indicated.\n"
492 " -a, --argument-array=STRING\n"
493 " Only scan for ARGS that match up to the length of the given STRING\n"
494 " -C, --command=COMMAND\n"
495 " Only scan for exact matches to the named COMMAND.\n\n"
496 "RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n"
497 "specified 'max:min', a warning status will be generated if the\n"
499 "count is inside the specified range\n");}
502 void
503 print_usage (void)
504 {
505 printf
506 ("Usage:\n"
507 " check_procs -w <range> -c <range> [-s state] [-p ppid] [-u user]\n"
508 " [-a argument-array] [-C command]\n"
509 " check_procs --version\n" " check_procs --help\n");
510 }