1 /******************************************************************************
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 ******************************************************************************/
19 const char *progname = "check_procs";
20 const char *revision = "$Revision$";
21 const char *copyright = "2000-2003";
22 const char *email = "nagiosplug-devel@lists.sourceforge.net";
24 #include "common.h"
25 #include "popen.h"
26 #include "utils.h"
27 #include <pwd.h>
29 int process_arguments (int, char **);
30 int validate_arguments (void);
31 int check_thresholds (int);
32 void print_help (void);
33 void print_usage (void);
35 int wmax = -1;
36 int cmax = -1;
37 int wmin = -1;
38 int cmin = -1;
40 int options = 0; /* bitmask of filter criteria to test against */
41 #define ALL 1
42 #define STAT 2
43 #define PPID 4
44 #define USER 8
45 #define PROG 16
46 #define ARGS 32
47 #define VSZ 64
48 #define RSS 128
49 #define PCPU 256
51 /* Different metrics */
52 char *metric_name;
53 enum metric {
54 METRIC_PROCS,
55 METRIC_VSZ,
56 METRIC_RSS,
57 METRIC_CPU
58 };
59 enum metric metric = METRIC_PROCS;
61 int verbose = 0;
62 int uid;
63 int ppid;
64 int vsz;
65 int rss;
66 float pcpu;
67 char *statopts;
68 char *prog;
69 char *args;
70 char *fmt;
71 char *fails;
72 char tmp[MAX_INPUT_BUFFER];
78 \f
79 int
80 main (int argc, char **argv)
81 {
82 char input_buffer[MAX_INPUT_BUFFER];
84 int procuid = 0;
85 int procppid = 0;
86 int procvsz = 0;
87 int procrss = 0;
88 float procpcpu = 0;
89 char procstat[8];
90 char procprog[MAX_INPUT_BUFFER];
91 char *procargs;
93 const char *zombie = "Z";
95 int resultsum = 0; /* bitmask of the filter criteria met by a process */
96 int found = 0; /* counter for number of lines returned in `ps` output */
97 int procs = 0; /* counter for number of processes meeting filter criteria */
98 int pos; /* number of spaces before 'args' in `ps` output */
99 int cols; /* number of columns in ps output */
100 int warn = 0; /* number of processes in warn state */
101 int crit = 0; /* number of processes in crit state */
102 int i = 0;
104 int result = STATE_UNKNOWN;
106 setlocale (LC_ALL, "");
107 bindtextdomain (PACKAGE, LOCALEDIR);
108 textdomain (PACKAGE);
110 asprintf (&metric_name, "PROCS");
111 metric = METRIC_PROCS;
113 if (process_arguments (argc, argv) == ERROR)
114 usage (_("Unable to parse command line\n"));
116 if (verbose >= 2)
117 printf (_("CMD: %s\n"), PS_COMMAND);
119 child_process = spopen (PS_COMMAND);
120 if (child_process == NULL) {
121 printf (_("Could not open pipe: %s\n"), PS_COMMAND);
122 return STATE_UNKNOWN;
123 }
125 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
126 if (child_stderr == NULL)
127 printf (_("Could not open stderr for %s\n"), PS_COMMAND);
129 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
131 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
132 strcpy (procprog, "");
133 asprintf (&procargs, "%s", "");
135 cols = sscanf (input_buffer, PS_FORMAT, PS_VARLIST);
137 /* Zombie processes do not give a procprog command */
138 if ( cols == 6 && strstr(procstat, zombie) ) {
139 cols = 7;
140 /* Set some value for procargs for the strip command further below
141 Seen to be a problem on some Solaris 7 and 8 systems */
142 input_buffer[pos] = '\n';
143 input_buffer[pos+1] = 0x0;
144 }
145 if ( cols >= 7 ) {
146 resultsum = 0;
147 asprintf (&procargs, "%s", input_buffer + pos);
148 strip (procargs);
150 if ((options & STAT) && (strstr (statopts, procstat)))
151 resultsum |= STAT;
152 if ((options & ARGS) && procargs && (strstr (procargs, args) != NULL))
153 resultsum |= ARGS;
154 if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0))
155 resultsum |= PROG;
156 if ((options & PPID) && (procppid == ppid))
157 resultsum |= PPID;
158 if ((options & USER) && (procuid == uid))
159 resultsum |= USER;
160 if ((options & VSZ) && (procvsz >= vsz))
161 resultsum |= VSZ;
162 if ((options & RSS) && (procrss >= rss))
163 resultsum |= RSS;
164 if ((options & PCPU) && (procpcpu >= pcpu))
165 resultsum |= PCPU;
167 if (verbose >= 3)
168 printf ("%d %d %d %d %d %.2f %s %s %s\n",
169 procs, procuid, procvsz, procrss,
170 procppid, procpcpu, procstat, procprog, procargs);
172 /* Ignore self */
173 if (strcmp (procprog, progname) == 0)
174 continue;
176 found++;
178 /* Next line if filters not matched */
179 if (!(options == resultsum || options == ALL))
180 continue;
182 procs++;
184 if (metric == METRIC_VSZ)
185 i = check_thresholds (procvsz);
186 else if (metric == METRIC_RSS)
187 i = check_thresholds (procrss);
188 /* TODO? float thresholds for --metric=CPU */
189 else if (metric == METRIC_CPU)
190 i = check_thresholds ((int)procpcpu);
192 if (metric != METRIC_PROCS) {
193 if (i == STATE_WARNING) {
194 warn++;
195 asprintf (&fails, "%s%s%s", fails, (fails == "" ? "" : ", "), procprog);
196 }
197 if (i == STATE_CRITICAL) {
198 crit++;
199 asprintf (&fails, "%s%s%s", fails, (fails == "" ? "" : ", "), procprog);
200 }
201 result = max_state (result, i);
202 }
203 }
204 /* This should not happen */
205 else if (verbose) {
206 printf(_("Not parseable: %s"), input_buffer);
207 }
208 }
210 /* If we get anything on STDERR, at least set warning */
211 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
212 if (verbose)
213 printf (_("STDERR: %s"), input_buffer);
214 result = max_state (result, STATE_WARNING);
215 printf (_("System call sent warnings to stderr\n"));
216 }
218 (void) fclose (child_stderr);
220 /* close the pipe */
221 if (spclose (child_process)) {
222 printf (_("System call returned nonzero status\n"));
223 result = max_state (result, STATE_WARNING);
224 }
226 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
227 printf (_("Unable to read output\n"));
228 return result;
229 }
231 if ( result == STATE_UNKNOWN )
232 result = STATE_OK;
234 /* Needed if procs found, but none match filter */
235 if ( metric == METRIC_PROCS ) {
236 result = max_state (result, check_thresholds (procs) );
237 }
239 if ( result == STATE_OK ) {
240 printf (_("%s OK: %d process%s"),
241 metric_name, procs, ( procs != 1 ? "es" : "") );
242 } else if (result == STATE_WARNING) {
243 if ( metric == METRIC_PROCS ) {
244 printf (_("PROCS WARNING: %d process%s"), procs,
245 ( procs != 1 ? "es" : ""));
246 } else {
247 printf (_("%s WARNING: %d warn out of %d process%s"),
248 metric_name, warn, procs,
249 ( procs != 1 ? "es" : ""));
250 }
251 } else if (result == STATE_CRITICAL) {
252 if (metric == METRIC_PROCS) {
253 printf (_("PROCS CRITICAL: %d process%s"), procs,
254 ( procs != 1 ? "es" : ""));
255 } else {
256 printf (_("%s CRITICAL: %d crit, %d warn out of %d process%s"),
257 metric_name, crit, warn, procs,
258 ( procs != 1 ? "es" : ""));
259 }
260 }
262 if (strcmp(fmt,"") != 0) {
263 printf (_(" with %s"), fmt);
264 }
266 if ( verbose >= 1 && fails != "" )
267 printf (" [%s]", fails);
269 printf ("\n");
270 return result;
271 }
277 \f
278 /* process command-line arguments */
279 int
280 process_arguments (int argc, char **argv)
281 {
282 int c = 1;
283 char *user;
284 struct passwd *pw;
285 int option = 0;
286 static struct option longopts[] = {
287 {"warning", required_argument, 0, 'w'},
288 {"critical", required_argument, 0, 'c'},
289 {"metric", required_argument, 0, 'm'},
290 {"timeout", required_argument, 0, 't'},
291 {"status", required_argument, 0, 's'},
292 {"ppid", required_argument, 0, 'p'},
293 {"command", required_argument, 0, 'C'},
294 {"vsz", required_argument, 0, 'z'},
295 {"rss", required_argument, 0, 'r'},
296 {"pcpu", required_argument, 0, 'P'},
297 {"argument-array", required_argument, 0, 'a'},
298 {"help", no_argument, 0, 'h'},
299 {"version", no_argument, 0, 'V'},
300 {"verbose", no_argument, 0, 'v'},
301 {0, 0, 0, 0}
302 };
304 for (c = 1; c < argc; c++)
305 if (strcmp ("-to", argv[c]) == 0)
306 strcpy (argv[c], "-t");
308 while (1) {
309 c = getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:z:r:m:P:",
310 longopts, &option);
312 if (c == -1 || c == EOF)
313 break;
315 switch (c) {
316 case '?': /* help */
317 print_usage ();
318 exit (STATE_UNKNOWN);
319 case 'h': /* help */
320 print_help ();
321 exit (STATE_OK);
322 case 'V': /* version */
323 print_revision (progname, revision);
324 exit (STATE_OK);
325 case 't': /* timeout period */
326 if (!is_integer (optarg))
327 usage (_("Timeout Interval must be an integer!\n\n"));
328 else
329 timeout_interval = atoi (optarg);
330 break;
331 case 'c': /* critical threshold */
332 if (is_integer (optarg))
333 cmax = atoi (optarg);
334 else if (sscanf (optarg, ":%d", &cmax) == 1)
335 break;
336 else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2)
337 break;
338 else if (sscanf (optarg, "%d:", &cmin) == 1)
339 break;
340 else
341 usage (_("Critical Process Count must be an integer!\n\n"));
342 break;
343 case 'w': /* warning time threshold */
344 if (is_integer (optarg))
345 wmax = atoi (optarg);
346 else if (sscanf (optarg, ":%d", &wmax) == 1)
347 break;
348 else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2)
349 break;
350 else if (sscanf (optarg, "%d:", &wmin) == 1)
351 break;
352 else
353 usage (_("%s: Warning Process Count must be an integer!\n\n"));
354 break;
355 case 'p': /* process id */
356 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
357 asprintf (&fmt, "%s%sPPID = %d", (fmt ? fmt : "") , (options ? ", " : ""), ppid);
358 options |= PPID;
359 break;
360 }
361 printf (_("%s: Parent Process ID must be an integer!\n\n"),
362 progname);
363 print_usage ();
364 exit (STATE_UNKNOWN);
365 case 's': /* status */
366 if (statopts)
367 break;
368 else
369 statopts = optarg;
370 asprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts);
371 options |= STAT;
372 break;
373 case 'u': /* user or user id */
374 if (is_integer (optarg)) {
375 uid = atoi (optarg);
376 pw = getpwuid ((uid_t) uid);
377 /* check to be sure user exists */
378 if (pw == NULL) {
379 printf (_("UID %d was not found\n"), uid);
380 print_usage ();
381 exit (STATE_UNKNOWN);
382 }
383 }
384 else {
385 pw = getpwnam (optarg);
386 /* check to be sure user exists */
387 if (pw == NULL) {
388 printf (_("User name %s was not found\n"), optarg);
389 print_usage ();
390 exit (STATE_UNKNOWN);
391 }
392 /* then get uid */
393 uid = pw->pw_uid;
394 }
395 user = pw->pw_name;
396 asprintf (&fmt, _("%s%sUID = %d (%s)"), (fmt ? fmt : ""), (options ? ", " : ""),
397 uid, user);
398 options |= USER;
399 break;
400 case 'C': /* command */
401 if (prog)
402 break;
403 else
404 prog = optarg;
405 asprintf (&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""),
406 prog);
407 options |= PROG;
408 break;
409 case 'a': /* args (full path name with args) */
410 if (args)
411 break;
412 else
413 args = optarg;
414 asprintf (&fmt, _("%s%sargs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), args);
415 options |= ARGS;
416 break;
417 case 'r': /* RSS */
418 if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) {
419 asprintf (&fmt, _("%s%sRSS >= %d"), (fmt ? fmt : ""), (options ? ", " : ""), rss);
420 options |= RSS;
421 break;
422 }
423 printf (_("%s: RSS must be an integer!\n\n"),
424 progname);
425 print_usage ();
426 exit (STATE_UNKNOWN);
427 case 'z': /* VSZ */
428 if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) {
429 asprintf (&fmt, _("%s%sVSZ >= %d"), (fmt ? fmt : ""), (options ? ", " : ""), vsz);
430 options |= VSZ;
431 break;
432 }
433 printf (_("%s: VSZ must be an integer!\n\n"),
434 progname);
435 print_usage ();
436 exit (STATE_UNKNOWN);
437 case 'P': /* PCPU */
438 /* TODO: -P 1.5.5 is accepted */
439 if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) {
440 asprintf (&fmt, _("%s%sPCPU >= %.2f"), (fmt ? fmt : ""), (options ? ", " : ""), pcpu);
441 options |= PCPU;
442 break;
443 }
444 printf (_("%s: PCPU must be a float!\n\n"),
445 progname);
446 print_usage ();
447 exit (STATE_UNKNOWN);
448 case 'm':
449 asprintf (&metric_name, "%s", optarg);
450 if ( strcmp(optarg, "PROCS") == 0) {
451 metric = METRIC_PROCS;
452 break;
453 }
454 else if ( strcmp(optarg, "VSZ") == 0) {
455 metric = METRIC_VSZ;
456 break;
457 }
458 else if ( strcmp(optarg, "RSS") == 0 ) {
459 metric = METRIC_RSS;
460 break;
461 }
462 else if ( strcmp(optarg, "CPU") == 0 ) {
463 metric = METRIC_CPU;
464 break;
465 }
466 printf (_("%s: metric must be one of PROCS, VSZ, RSS, CPU!\n\n"),
467 progname);
468 print_usage ();
469 exit (STATE_UNKNOWN);
470 case 'v': /* command */
471 verbose++;
472 break;
473 }
474 }
476 c = optind;
477 if (wmax == -1 && argv[c])
478 wmax = atoi (argv[c++]);
479 if (cmax == -1 && argv[c])
480 cmax = atoi (argv[c++]);
481 if (statopts == NULL && argv[c]) {
482 asprintf (&statopts, "%s", argv[c++]);
483 asprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts);
484 options |= STAT;
485 }
487 return validate_arguments ();
488 }
493 int
494 validate_arguments ()
495 {
497 if (wmax >= 0 && wmin == -1)
498 wmin = 0;
499 if (cmax >= 0 && cmin == -1)
500 cmin = 0;
501 if (wmax >= wmin && cmax >= cmin) { /* standard ranges */
502 if (wmax > cmax && cmax != -1) {
503 printf (_("wmax (%d) cannot be greater than cmax (%d)\n"), wmax, cmax);
504 return ERROR;
505 }
506 if (cmin > wmin && wmin != -1) {
507 printf (_("wmin (%d) cannot be less than cmin (%d)\n"), wmin, cmin);
508 return ERROR;
509 }
510 }
512 /* if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) { */
513 /* printf ("At least one threshold must be set\n"); */
514 /* return ERROR; */
515 /* } */
517 if (options == 0)
518 options = ALL;
520 if (statopts==NULL)
521 statopts = strdup("");
523 if (prog==NULL)
524 prog = strdup("");
526 if (args==NULL)
527 args = strdup("");
529 if (fmt==NULL)
530 fmt = strdup("");
532 if (fails==NULL)
533 fails = strdup("");
535 return options;
536 }
542 \f
543 /* Check thresholds against value */
544 int
545 check_thresholds (int value)
546 {
547 if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
548 return OK;
549 }
550 else if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
551 if (value > cmax && value < cmin)
552 return STATE_CRITICAL;
553 }
554 else if (cmax >= 0 && value > cmax) {
555 return STATE_CRITICAL;
556 }
557 else if (cmin >= 0 && value < cmin) {
558 return STATE_CRITICAL;
559 }
561 if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
562 if (value > wmax && value < wmin) {
563 return STATE_WARNING;
564 }
565 }
566 else if (wmax >= 0 && value > wmax) {
567 return STATE_WARNING;
568 }
569 else if (wmin >= 0 && value < wmin) {
570 return STATE_WARNING;
571 }
572 return STATE_OK;
573 }
579 \f
580 void
581 print_help (void)
582 {
583 print_revision (progname, revision);
585 printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>"));
586 printf (_(COPYRIGHT), copyright, email);
588 printf(_("\
589 Checks all processes and generates WARNING or CRITICAL states if the specified\n\
590 metric is outside the required threshold ranges. The metric defaults to number\n\
591 of processes. Search filters can be applied to limit the processes to check.\n\n"));
593 print_usage ();
595 printf(_("\n\
596 Required Arguments:\n\
597 -w, --warning=RANGE\n\
598 Generate warning state if metric is outside this range\n\
599 -c, --critical=RANGE\n\
600 Generate critical state if metric is outside this range\n"));
602 printf(_("\n\
603 Optional Arguments:\n\
604 -m, --metric=TYPE\n\
605 Check thresholds against metric. Valid types:\n\
606 PROCS - number of processes (default)\n\
607 VSZ - virtual memory size\n\
608 RSS - resident set memory size\n\
609 CPU - percentage cpu\n\
610 -v, --verbose\n\
611 Extra information. Up to 3 verbosity levels\n"));
613 printf(_("\n\
614 Optional Filters:\n\
615 -s, --state=STATUSFLAGS\n\
616 Only scan for processes that have, in the output of `ps`, one or\n\
617 more of the status flags you specify (for example R, Z, S, RS,\n\
618 RSZDT, plus others based on the output of your 'ps' command).\n\
619 -p, --ppid=PPID\n\
620 Only scan for children of the parent process ID indicated.\n\
621 -z, --vsz=VSZ\n\
622 Only scan for processes with vsz higher than indicated.\n\
623 -r, --rss=RSS\n\
624 Only scan for processes with rss higher than indicated.\n"));
626 printf(_("\
627 -P, --pcpu=PCPU\n\
628 Only scan for processes with pcpu higher than indicated.\n\
629 -u, --user=USER\n\
630 Only scan for processes with user name or ID indicated.\n\
631 -a, --argument-array=STRING\n\
632 Only scan for processes with args that contain STRING.\n\
633 -C, --command=COMMAND\n\
634 Only scan for exact matches to the named COMMAND.\n"));
636 printf(_("\n\
637 RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\
638 specified 'max:min', a warning status will be generated if the\n\
639 count is inside the specified range\n\n"));
641 printf(_("\
642 This plugin checks the number of currently running processes and\n\
643 generates WARNING or CRITICAL states if the process count is outside\n\
644 the specified threshold ranges. The process count can be filtered by\n\
645 process owner, parent process PID, current state (e.g., 'Z'), or may\n\
646 be the total number of running processes\n\n"));
648 printf(_("\
649 Examples:\n\
650 check_procs -w 2:2 -c 2:1024 -C portsentry\n\
651 Warning if not two processes with command name portsentry. Critical\n\
652 if < 2 or > 1024 processes\n\n\
653 check_procs -w 10 -a '/usr/local/bin/perl' -u root\n\
654 Warning alert if > 10 processes with command arguments containing \n\
655 '/usr/local/bin/perl' and owned by root\n\n\
656 check_procs -w 50000 -c 100000 --metric=VSZ\n\
657 Alert if vsz of any processes over 50K or 100K\n\n"));
659 printf (_(UT_SUPPORT));
660 }
662 void
663 print_usage (void)
664 {
665 printf ("\
666 Usage: %s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n\
667 [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n\
668 [-C command] [-v]\n", progname);
669 printf (_(UT_HLP_VRS), progname, progname);
670 }