b7f32c7c7f7aab5a17ac8fca88d249164e10babe
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 $Id$
19 ******************************************************************************/
21 const char *progname = "check_procs";
22 const char *revision = "$Revision$";
23 const char *copyright = "2000-2003";
24 const char *email = "nagiosplug-devel@lists.sourceforge.net";
26 #include "common.h"
27 #include "popen.h"
28 #include "utils.h"
29 #include <pwd.h>
31 int process_arguments (int, char **);
32 int validate_arguments (void);
33 int check_thresholds (int);
34 void print_help (void);
35 void print_usage (void);
37 int wmax = -1;
38 int cmax = -1;
39 int wmin = -1;
40 int cmin = -1;
42 int options = 0; /* bitmask of filter criteria to test against */
43 #define ALL 1
44 #define STAT 2
45 #define PPID 4
46 #define USER 8
47 #define PROG 16
48 #define ARGS 32
49 #define VSZ 64
50 #define RSS 128
51 #define PCPU 256
53 /* Different metrics */
54 char *metric_name;
55 enum metric {
56 METRIC_PROCS,
57 METRIC_VSZ,
58 METRIC_RSS,
59 METRIC_CPU
60 };
61 enum metric metric = METRIC_PROCS;
63 int verbose = 0;
64 int uid;
65 int ppid;
66 int vsz;
67 int rss;
68 float pcpu;
69 char *statopts;
70 char *prog;
71 char *args;
72 char *fmt;
73 char *fails;
74 char tmp[MAX_INPUT_BUFFER];
78 int
79 main (int argc, char **argv)
80 {
81 char *input_buffer;
82 char *input_line;
83 char *procprog;
85 int procuid = 0;
86 int procppid = 0;
87 int procvsz = 0;
88 int procrss = 0;
89 float procpcpu = 0;
90 char procstat[8];
91 char *procargs;
92 char *temp_string;
94 const char *zombie = "Z";
96 int resultsum = 0; /* bitmask of the filter criteria met by a process */
97 int found = 0; /* counter for number of lines returned in `ps` output */
98 int procs = 0; /* counter for number of processes meeting filter criteria */
99 int pos; /* number of spaces before 'args' in `ps` output */
100 int cols; /* number of columns in ps output */
101 int expected_cols = PS_COLS - 1;
102 int warn = 0; /* number of processes in warn state */
103 int crit = 0; /* number of processes in crit state */
104 int i = 0;
106 int result = STATE_UNKNOWN;
108 setlocale (LC_ALL, "");
109 bindtextdomain (PACKAGE, LOCALEDIR);
110 textdomain (PACKAGE);
112 input_buffer = malloc (MAX_INPUT_BUFFER);
113 procprog = malloc (MAX_INPUT_BUFFER);
115 asprintf (&metric_name, "PROCS");
116 metric = METRIC_PROCS;
118 if (process_arguments (argc, argv) != OK)
119 usage (_("check_procs: could not parse arguments\n"));
121 /* Set signal handling and alarm timeout */
122 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
123 printf (_("Cannot catch SIGALRM"));
124 return STATE_UNKNOWN;
125 }
126 alarm (timeout_interval);
128 if (verbose >= 2)
129 printf (_("CMD: %s\n"), PS_COMMAND);
131 child_process = spopen (PS_COMMAND);
132 if (child_process == NULL) {
133 printf (_("Could not open pipe: %s\n"), PS_COMMAND);
134 return STATE_UNKNOWN;
135 }
137 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
138 if (child_stderr == NULL)
139 printf (_("Could not open stderr for %s\n"), PS_COMMAND);
141 /* flush first line */
142 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
143 while ( input_buffer[strlen(input_buffer)-1] != '\n' )
144 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
146 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
147 asprintf (&input_line, "%s", input_buffer);
148 while ( input_buffer[strlen(input_buffer)-1] != '\n' ) {
149 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
150 asprintf (&input_line, "%s%s", input_line, input_buffer);
151 }
153 if (verbose >= 3)
154 printf ("%s", input_line);
156 strcpy (procprog, "");
157 asprintf (&procargs, "%s", "");
159 cols = sscanf (input_line, PS_FORMAT, PS_VARLIST);
161 /* Zombie processes do not give a procprog command */
162 if ( cols == (expected_cols - 1) && strstr(procstat, zombie) ) {
163 cols = expected_cols;
164 }
165 if ( cols >= expected_cols ) {
166 resultsum = 0;
167 asprintf (&procargs, "%s", input_line + pos);
168 strip (procargs);
170 /* Some ps return full pathname for command. This removes path */
171 temp_string = strtok ((char *)procprog, "/");
172 while (temp_string) {
173 strcpy(procprog, temp_string);
174 temp_string = strtok (NULL, "/");
175 }
177 if (verbose >= 3)
178 printf ("%d %d %d %d %d %.2f %s %s %s\n",
179 procs, procuid, procvsz, procrss,
180 procppid, procpcpu, procstat, procprog, procargs);
182 /* Ignore self */
183 if (strcmp (procprog, progname) == 0) {
184 continue;
185 }
187 if ((options & STAT) && (strstr (statopts, procstat)))
188 resultsum |= STAT;
189 if ((options & ARGS) && procargs && (strstr (procargs, args) != NULL))
190 resultsum |= ARGS;
191 if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0))
192 resultsum |= PROG;
193 if ((options & PPID) && (procppid == ppid))
194 resultsum |= PPID;
195 if ((options & USER) && (procuid == uid))
196 resultsum |= USER;
197 if ((options & VSZ) && (procvsz >= vsz))
198 resultsum |= VSZ;
199 if ((options & RSS) && (procrss >= rss))
200 resultsum |= RSS;
201 if ((options & PCPU) && (procpcpu >= pcpu))
202 resultsum |= PCPU;
204 found++;
206 /* Next line if filters not matched */
207 if (!(options == resultsum || options == ALL))
208 continue;
210 procs++;
212 if (metric == METRIC_VSZ)
213 i = check_thresholds (procvsz);
214 else if (metric == METRIC_RSS)
215 i = check_thresholds (procrss);
216 /* TODO? float thresholds for --metric=CPU */
217 else if (metric == METRIC_CPU)
218 i = check_thresholds ((int)procpcpu);
220 if (metric != METRIC_PROCS) {
221 if (i == STATE_WARNING) {
222 warn++;
223 asprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog);
224 result = max_state (result, i);
225 }
226 if (i == STATE_CRITICAL) {
227 crit++;
228 asprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog);
229 result = max_state (result, i);
230 }
231 }
232 }
233 /* This should not happen */
234 else if (verbose) {
235 printf(_("Not parseable: %s"), input_buffer);
236 }
237 }
239 /* If we get anything on STDERR, at least set warning */
240 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
241 if (verbose)
242 printf (_("STDERR: %s"), input_buffer);
243 result = max_state (result, STATE_WARNING);
244 printf (_("System call sent warnings to stderr\n"));
245 }
247 (void) fclose (child_stderr);
249 /* close the pipe */
250 if (spclose (child_process)) {
251 printf (_("System call returned nonzero status\n"));
252 result = max_state (result, STATE_WARNING);
253 }
255 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
256 printf (_("Unable to read output\n"));
257 return result;
258 }
260 if ( result == STATE_UNKNOWN )
261 result = STATE_OK;
263 /* Needed if procs found, but none match filter */
264 if ( metric == METRIC_PROCS ) {
265 result = max_state (result, check_thresholds (procs) );
266 }
268 if ( result == STATE_OK ) {
269 printf ("%s %s: ", metric_name, _("OK"));
270 } else if (result == STATE_WARNING) {
271 printf ("%s %s: ", metric_name, _("WARNING"));
272 if ( metric != METRIC_PROCS ) {
273 printf (_("%d warn out of "), warn);
274 }
275 } else if (result == STATE_CRITICAL) {
276 printf ("%s %s: ", metric_name, _("CRITICAL"));
277 if (metric != METRIC_PROCS) {
278 printf (_("%d crit, %d warn out of "), crit, warn);
279 }
280 }
281 printf (ngettext ("%d process", "%d processes", (unsigned long) procs), procs);
283 if (strcmp(fmt,"") != 0) {
284 printf (_(" with %s"), fmt);
285 }
287 if ( verbose >= 1 && strcmp(fails,"") )
288 printf (" [%s]", fails);
290 printf ("\n");
291 return result;
292 }
296 /* process command-line arguments */
297 int
298 process_arguments (int argc, char **argv)
299 {
300 int c = 1;
301 char *user;
302 struct passwd *pw;
303 int option = 0;
304 static struct option longopts[] = {
305 {"warning", required_argument, 0, 'w'},
306 {"critical", required_argument, 0, 'c'},
307 {"metric", required_argument, 0, 'm'},
308 {"timeout", required_argument, 0, 't'},
309 {"status", required_argument, 0, 's'},
310 {"ppid", required_argument, 0, 'p'},
311 {"command", required_argument, 0, 'C'},
312 {"vsz", required_argument, 0, 'z'},
313 {"rss", required_argument, 0, 'r'},
314 {"pcpu", required_argument, 0, 'P'},
315 {"argument-array", required_argument, 0, 'a'},
316 {"help", no_argument, 0, 'h'},
317 {"version", no_argument, 0, 'V'},
318 {"verbose", no_argument, 0, 'v'},
319 {0, 0, 0, 0}
320 };
322 for (c = 1; c < argc; c++)
323 if (strcmp ("-to", argv[c]) == 0)
324 strcpy (argv[c], "-t");
326 while (1) {
327 c = getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:z:r:m:P:",
328 longopts, &option);
330 if (c == -1 || c == EOF)
331 break;
333 switch (c) {
334 case '?': /* help */
335 printf (_("%s: Unknown argument: %s\n\n"), progname, optarg);
336 print_usage ();
337 exit (STATE_UNKNOWN);
338 case 'h': /* help */
339 print_help ();
340 exit (STATE_OK);
341 case 'V': /* version */
342 print_revision (progname, revision);
343 exit (STATE_OK);
344 case 't': /* timeout period */
345 if (!is_integer (optarg))
346 usage2 (_("Timeout interval must be a positive integer"), optarg);
347 else
348 timeout_interval = atoi (optarg);
349 break;
350 case 'c': /* critical threshold */
351 if (is_integer (optarg))
352 cmax = atoi (optarg);
353 else if (sscanf (optarg, ":%d", &cmax) == 1)
354 break;
355 else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2)
356 break;
357 else if (sscanf (optarg, "%d:", &cmin) == 1)
358 break;
359 else
360 usage (_("Critical Process Count must be an integer!\n\n"));
361 break;
362 case 'w': /* warning threshold */
363 if (is_integer (optarg))
364 wmax = atoi (optarg);
365 else if (sscanf (optarg, ":%d", &wmax) == 1)
366 break;
367 else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2)
368 break;
369 else if (sscanf (optarg, "%d:", &wmin) == 1)
370 break;
371 else
372 usage (_("Warning Process Count must be an integer!\n\n"));
373 break;
374 case 'p': /* process id */
375 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
376 asprintf (&fmt, "%s%sPPID = %d", (fmt ? fmt : "") , (options ? ", " : ""), ppid);
377 options |= PPID;
378 break;
379 }
380 usage2 (_("%s: Parent Process ID must be an integer!\n\n"), progname);
381 case 's': /* status */
382 if (statopts)
383 break;
384 else
385 statopts = optarg;
386 asprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts);
387 options |= STAT;
388 break;
389 case 'u': /* user or user id */
390 if (is_integer (optarg)) {
391 uid = atoi (optarg);
392 pw = getpwuid ((uid_t) uid);
393 /* check to be sure user exists */
394 if (pw == NULL)
395 usage2 (_("UID %s was not found\n"), optarg);
396 }
397 else {
398 pw = getpwnam (optarg);
399 /* check to be sure user exists */
400 if (pw == NULL)
401 usage2 (_("User name %s was not found\n"), optarg);
402 /* then get uid */
403 uid = pw->pw_uid;
404 }
405 user = pw->pw_name;
406 asprintf (&fmt, _("%s%sUID = %d (%s)"), (fmt ? fmt : ""), (options ? ", " : ""),
407 uid, user);
408 options |= USER;
409 break;
410 case 'C': /* command */
411 if (prog)
412 break;
413 else
414 prog = optarg;
415 asprintf (&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""),
416 prog);
417 options |= PROG;
418 break;
419 case 'a': /* args (full path name with args) */
420 if (args)
421 break;
422 else
423 args = optarg;
424 asprintf (&fmt, _("%s%sargs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), args);
425 options |= ARGS;
426 break;
427 case 'r': /* RSS */
428 if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) {
429 asprintf (&fmt, _("%s%sRSS >= %d"), (fmt ? fmt : ""), (options ? ", " : ""), rss);
430 options |= RSS;
431 break;
432 }
433 usage2 (_("%s: RSS must be an integer!\n\n"), progname);
434 case 'z': /* VSZ */
435 if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) {
436 asprintf (&fmt, _("%s%sVSZ >= %d"), (fmt ? fmt : ""), (options ? ", " : ""), vsz);
437 options |= VSZ;
438 break;
439 }
440 usage2 (_("%s: VSZ must be an integer!\n\n"), progname);
441 case 'P': /* PCPU */
442 /* TODO: -P 1.5.5 is accepted */
443 if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) {
444 asprintf (&fmt, _("%s%sPCPU >= %.2f"), (fmt ? fmt : ""), (options ? ", " : ""), pcpu);
445 options |= PCPU;
446 break;
447 }
448 usage2 (_("%s: PCPU must be a float!\n\n"), progname);
449 case 'm':
450 asprintf (&metric_name, "%s", optarg);
451 if ( strcmp(optarg, "PROCS") == 0) {
452 metric = METRIC_PROCS;
453 break;
454 }
455 else if ( strcmp(optarg, "VSZ") == 0) {
456 metric = METRIC_VSZ;
457 break;
458 }
459 else if ( strcmp(optarg, "RSS") == 0 ) {
460 metric = METRIC_RSS;
461 break;
462 }
463 else if ( strcmp(optarg, "CPU") == 0 ) {
464 metric = METRIC_CPU;
465 break;
466 }
467 printf (_("%s: metric must be one of PROCS, VSZ, RSS, CPU!\n\n"),
468 progname);
469 print_usage ();
470 exit (STATE_UNKNOWN);
471 case 'v': /* command */
472 verbose++;
473 break;
474 }
475 }
477 c = optind;
478 if (wmax == -1 && argv[c])
479 wmax = atoi (argv[c++]);
480 if (cmax == -1 && argv[c])
481 cmax = atoi (argv[c++]);
482 if (statopts == NULL && argv[c]) {
483 asprintf (&statopts, "%s", argv[c++]);
484 asprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts);
485 options |= STAT;
486 }
488 return validate_arguments ();
489 }
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 }
540 /* Check thresholds against value */
541 int
542 check_thresholds (int value)
543 {
544 if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
545 return OK;
546 }
547 else if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
548 if (value > cmax && value < cmin)
549 return STATE_CRITICAL;
550 }
551 else if (cmax >= 0 && value > cmax) {
552 return STATE_CRITICAL;
553 }
554 else if (cmin >= 0 && value < cmin) {
555 return STATE_CRITICAL;
556 }
558 if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
559 if (value > wmax && value < wmin) {
560 return STATE_WARNING;
561 }
562 }
563 else if (wmax >= 0 && value > wmax) {
564 return STATE_WARNING;
565 }
566 else if (wmin >= 0 && value < wmin) {
567 return STATE_WARNING;
568 }
569 return STATE_OK;
570 }
574 void
575 print_help (void)
576 {
577 print_revision (progname, revision);
579 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>");
580 printf (COPYRIGHT, copyright, email);
582 printf(_("\
583 Checks all processes and generates WARNING or CRITICAL states if the specified\n\
584 metric is outside the required threshold ranges. The metric defaults to number\n\
585 of processes. Search filters can be applied to limit the processes to check.\n\n"));
587 print_usage ();
589 printf(_("\n\
590 Required Arguments:\n\
591 -w, --warning=RANGE\n\
592 Generate warning state if metric is outside this range\n\
593 -c, --critical=RANGE\n\
594 Generate critical state if metric is outside this range\n"));
596 printf(_("\n\
597 Optional Arguments:\n\
598 -m, --metric=TYPE\n\
599 Check thresholds against metric. Valid types:\n\
600 PROCS - number of processes (default)\n\
601 VSZ - virtual memory size\n\
602 RSS - resident set memory size\n\
603 CPU - percentage cpu\n"));
605 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
607 printf(_("\
608 -v, --verbose\n\
609 Extra information. Up to 3 verbosity levels\n"));
611 printf(_("\n\
612 Optional Filters:\n\
613 -s, --state=STATUSFLAGS\n\
614 Only scan for processes that have, in the output of `ps`, one or\n\
615 more of the status flags you specify (for example R, Z, S, RS,\n\
616 RSZDT, plus others based on the output of your 'ps' command).\n\
617 -p, --ppid=PPID\n\
618 Only scan for children of the parent process ID indicated.\n\
619 -z, --vsz=VSZ\n\
620 Only scan for processes with vsz higher than indicated.\n\
621 -r, --rss=RSS\n\
622 Only scan for processes with rss higher than indicated.\n"));
624 printf(_("\
625 -P, --pcpu=PCPU\n\
626 Only scan for processes with pcpu higher than indicated.\n\
627 -u, --user=USER\n\
628 Only scan for processes with user name or ID indicated.\n\
629 -a, --argument-array=STRING\n\
630 Only scan for processes with args that contain STRING.\n\
631 -C, --command=COMMAND\n\
632 Only scan for exact matches of COMMAND (without path).\n"));
634 printf(_("\n\
635 RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\
636 specified 'max:min', a warning status will be generated if the\n\
637 count is inside the specified range\n\n"));
639 printf(_("\
640 This plugin checks the number of currently running processes and\n\
641 generates WARNING or CRITICAL states if the process count is outside\n\
642 the specified threshold ranges. The process count can be filtered by\n\
643 process owner, parent process PID, current state (e.g., 'Z'), or may\n\
644 be the total number of running processes\n\n"));
646 printf(_("\
647 Examples:\n\
648 check_procs -w 2:2 -c 2:1024 -C portsentry\n\
649 Warning if not two processes with command name portsentry. Critical\n\
650 if < 2 or > 1024 processes\n\n\
651 check_procs -w 10 -a '/usr/local/bin/perl' -u root\n\
652 Warning alert if > 10 processes with command arguments containing \n\
653 '/usr/local/bin/perl' and owned by root\n\n\
654 check_procs -w 50000 -c 100000 --metric=VSZ\n\
655 Alert if vsz of any processes over 50K or 100K\n\
656 check_procs -w 10 -c 20 --metric=CPU\n\
657 Alert if cpu of any processes over 10% or 20%\n\n"));
659 printf (_(UT_SUPPORT));
660 }
664 void
665 print_usage (void)
666 {
667 printf ("\
668 Usage: %s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n\
669 [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n\
670 [-C command] [-t timeout] [-v]\n", progname);
671 printf (_(UT_HLP_VRS), progname, progname);
672 }