5448842ccbade6728dda7ce201889ada755bfb8a
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) != TRUE)
119 usage4 (_("Could not parse arguments"));
121 /* Set signal handling and alarm timeout */
122 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
123 usage4 (_("Cannot catch SIGALRM"));
124 }
125 alarm (timeout_interval);
127 if (verbose >= 2)
128 printf (_("CMD: %s\n"), PS_COMMAND);
130 child_process = spopen (PS_COMMAND);
131 if (child_process == NULL) {
132 printf (_("Could not open pipe: %s\n"), PS_COMMAND);
133 return STATE_UNKNOWN;
134 }
136 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
137 if (child_stderr == NULL)
138 printf (_("Could not open stderr for %s\n"), PS_COMMAND);
140 /* flush first line */
141 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
142 while ( input_buffer[strlen(input_buffer)-1] != '\n' )
143 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
145 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
146 asprintf (&input_line, "%s", input_buffer);
147 while ( input_buffer[strlen(input_buffer)-1] != '\n' ) {
148 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
149 asprintf (&input_line, "%s%s", input_line, input_buffer);
150 }
152 if (verbose >= 3)
153 printf ("%s", input_line);
155 strcpy (procprog, "");
156 asprintf (&procargs, "%s", "");
158 cols = sscanf (input_line, PS_FORMAT, PS_VARLIST);
160 /* Zombie processes do not give a procprog command */
161 if ( cols == (expected_cols - 1) && strstr(procstat, zombie) ) {
162 cols = expected_cols;
163 }
164 if ( cols >= expected_cols ) {
165 resultsum = 0;
166 asprintf (&procargs, "%s", input_line + pos);
167 strip (procargs);
169 /* Some ps return full pathname for command. This removes path */
170 temp_string = strtok ((char *)procprog, "/");
171 while (temp_string) {
172 strcpy(procprog, temp_string);
173 temp_string = strtok (NULL, "/");
174 }
176 if (verbose >= 3)
177 printf ("%d %d %d %d %d %.2f %s %s %s\n",
178 procs, procuid, procvsz, procrss,
179 procppid, procpcpu, procstat, procprog, procargs);
181 /* Ignore self */
182 if (strcmp (procprog, progname) == 0) {
183 continue;
184 }
186 if ((options & STAT) && (strstr (statopts, procstat)))
187 resultsum |= STAT;
188 if ((options & ARGS) && procargs && (strstr (procargs, args) != NULL))
189 resultsum |= ARGS;
190 if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0))
191 resultsum |= PROG;
192 if ((options & PPID) && (procppid == ppid))
193 resultsum |= PPID;
194 if ((options & USER) && (procuid == uid))
195 resultsum |= USER;
196 if ((options & VSZ) && (procvsz >= vsz))
197 resultsum |= VSZ;
198 if ((options & RSS) && (procrss >= rss))
199 resultsum |= RSS;
200 if ((options & PCPU) && (procpcpu >= pcpu))
201 resultsum |= PCPU;
203 found++;
205 /* Next line if filters not matched */
206 if (!(options == resultsum || options == ALL))
207 continue;
209 procs++;
211 if (metric == METRIC_VSZ)
212 i = check_thresholds (procvsz);
213 else if (metric == METRIC_RSS)
214 i = check_thresholds (procrss);
215 /* TODO? float thresholds for --metric=CPU */
216 else if (metric == METRIC_CPU)
217 i = check_thresholds ((int)procpcpu);
219 if (metric != METRIC_PROCS) {
220 if (i == STATE_WARNING) {
221 warn++;
222 asprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog);
223 result = max_state (result, i);
224 }
225 if (i == STATE_CRITICAL) {
226 crit++;
227 asprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog);
228 result = max_state (result, i);
229 }
230 }
231 }
232 /* This should not happen */
233 else if (verbose) {
234 printf(_("Not parseable: %s"), input_buffer);
235 }
236 }
238 /* If we get anything on STDERR, at least set warning */
239 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
240 if (verbose)
241 printf (_("STDERR: %s"), input_buffer);
242 result = max_state (result, STATE_WARNING);
243 printf (_("System call sent warnings to stderr\n"));
244 }
246 (void) fclose (child_stderr);
248 /* close the pipe */
249 if (spclose (child_process)) {
250 printf (_("System call returned nonzero status\n"));
251 result = max_state (result, STATE_WARNING);
252 }
254 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
255 printf (_("Unable to read output\n"));
256 return result;
257 }
259 if ( result == STATE_UNKNOWN )
260 result = STATE_OK;
262 /* Needed if procs found, but none match filter */
263 if ( metric == METRIC_PROCS ) {
264 result = max_state (result, check_thresholds (procs) );
265 }
267 if ( result == STATE_OK ) {
268 printf ("%s %s: ", metric_name, _("OK"));
269 } else if (result == STATE_WARNING) {
270 printf ("%s %s: ", metric_name, _("WARNING"));
271 if ( metric != METRIC_PROCS ) {
272 printf (_("%d warn out of "), warn);
273 }
274 } else if (result == STATE_CRITICAL) {
275 printf ("%s %s: ", metric_name, _("CRITICAL"));
276 if (metric != METRIC_PROCS) {
277 printf (_("%d crit, %d warn out of "), crit, warn);
278 }
279 }
280 printf ("%d %s", procs, procs == 1 ? _("process") : _("processes"));
282 if (strcmp(fmt,"") != 0) {
283 printf (_(" with %s"), fmt);
284 }
286 if ( verbose >= 1 && strcmp(fails,"") )
287 printf (" [%s]", fails);
289 printf ("\n");
290 return result;
291 }
295 /* process command-line arguments */
296 int
297 process_arguments (int argc, char **argv)
298 {
299 int c = 1;
300 char *user;
301 struct passwd *pw;
302 int option = 0;
303 static struct option longopts[] = {
304 {"warning", required_argument, 0, 'w'},
305 {"critical", required_argument, 0, 'c'},
306 {"metric", required_argument, 0, 'm'},
307 {"timeout", required_argument, 0, 't'},
308 {"status", required_argument, 0, 's'},
309 {"ppid", required_argument, 0, 'p'},
310 {"command", required_argument, 0, 'C'},
311 {"vsz", required_argument, 0, 'z'},
312 {"rss", required_argument, 0, 'r'},
313 {"pcpu", required_argument, 0, 'P'},
314 {"argument-array", required_argument, 0, 'a'},
315 {"help", no_argument, 0, 'h'},
316 {"version", no_argument, 0, 'V'},
317 {"verbose", no_argument, 0, 'v'},
318 {0, 0, 0, 0}
319 };
321 for (c = 1; c < argc; c++)
322 if (strcmp ("-to", argv[c]) == 0)
323 strcpy (argv[c], "-t");
325 while (1) {
326 c = getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:z:r:m:P:",
327 longopts, &option);
329 if (c == -1 || c == EOF)
330 break;
332 switch (c) {
333 case '?': /* help */
334 printf (_("%s: Unknown argument: %s\n\n"), progname, optarg);
335 print_usage ();
336 exit (STATE_UNKNOWN);
337 case 'h': /* help */
338 print_help ();
339 exit (STATE_OK);
340 case 'V': /* version */
341 print_revision (progname, revision);
342 exit (STATE_OK);
343 case 't': /* timeout period */
344 if (!is_integer (optarg))
345 usage2 (_("Timeout interval must be a positive integer"), optarg);
346 else
347 timeout_interval = atoi (optarg);
348 break;
349 case 'c': /* critical threshold */
350 if (is_integer (optarg))
351 cmax = atoi (optarg);
352 else if (sscanf (optarg, ":%d", &cmax) == 1)
353 break;
354 else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2)
355 break;
356 else if (sscanf (optarg, "%d:", &cmin) == 1)
357 break;
358 else
359 usage (_("Critical Process Count must be an integer!\n\n"));
360 break;
361 case 'w': /* warning threshold */
362 if (is_integer (optarg))
363 wmax = atoi (optarg);
364 else if (sscanf (optarg, ":%d", &wmax) == 1)
365 break;
366 else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2)
367 break;
368 else if (sscanf (optarg, "%d:", &wmin) == 1)
369 break;
370 else
371 usage (_("Warning Process Count must be an integer!\n\n"));
372 break;
373 case 'p': /* process id */
374 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
375 asprintf (&fmt, "%s%sPPID = %d", (fmt ? fmt : "") , (options ? ", " : ""), ppid);
376 options |= PPID;
377 break;
378 }
379 usage2 (_("%s: Parent Process ID must be an integer!\n\n"), progname);
380 case 's': /* status */
381 if (statopts)
382 break;
383 else
384 statopts = optarg;
385 asprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts);
386 options |= STAT;
387 break;
388 case 'u': /* user or user id */
389 if (is_integer (optarg)) {
390 uid = atoi (optarg);
391 pw = getpwuid ((uid_t) uid);
392 /* check to be sure user exists */
393 if (pw == NULL)
394 usage2 (_("UID %s was not found\n"), optarg);
395 }
396 else {
397 pw = getpwnam (optarg);
398 /* check to be sure user exists */
399 if (pw == NULL)
400 usage2 (_("User name %s was not found\n"), optarg);
401 /* then get uid */
402 uid = pw->pw_uid;
403 }
404 user = pw->pw_name;
405 asprintf (&fmt, _("%s%sUID = %d (%s)"), (fmt ? fmt : ""), (options ? ", " : ""),
406 uid, user);
407 options |= USER;
408 break;
409 case 'C': /* command */
410 if (prog)
411 break;
412 else
413 prog = optarg;
414 asprintf (&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""),
415 prog);
416 options |= PROG;
417 break;
418 case 'a': /* args (full path name with args) */
419 if (args)
420 break;
421 else
422 args = optarg;
423 asprintf (&fmt, _("%s%sargs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), args);
424 options |= ARGS;
425 break;
426 case 'r': /* RSS */
427 if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) {
428 asprintf (&fmt, _("%s%sRSS >= %d"), (fmt ? fmt : ""), (options ? ", " : ""), rss);
429 options |= RSS;
430 break;
431 }
432 usage2 (_("%s: RSS must be an integer!\n\n"), progname);
433 case 'z': /* VSZ */
434 if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) {
435 asprintf (&fmt, _("%s%sVSZ >= %d"), (fmt ? fmt : ""), (options ? ", " : ""), vsz);
436 options |= VSZ;
437 break;
438 }
439 usage2 (_("%s: VSZ must be an integer!\n\n"), progname);
440 case 'P': /* PCPU */
441 /* TODO: -P 1.5.5 is accepted */
442 if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) {
443 asprintf (&fmt, _("%s%sPCPU >= %.2f"), (fmt ? fmt : ""), (options ? ", " : ""), pcpu);
444 options |= PCPU;
445 break;
446 }
447 usage2 (_("%s: PCPU must be a float!\n\n"), progname);
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 }
492 int
493 validate_arguments ()
494 {
496 if (wmax >= 0 && wmin == -1)
497 wmin = 0;
498 if (cmax >= 0 && cmin == -1)
499 cmin = 0;
500 if (wmax >= wmin && cmax >= cmin) { /* standard ranges */
501 if (wmax > cmax && cmax != -1) {
502 printf (_("wmax (%d) cannot be greater than cmax (%d)\n"), wmax, cmax);
503 return ERROR;
504 }
505 if (cmin > wmin && wmin != -1) {
506 printf (_("wmin (%d) cannot be less than cmin (%d)\n"), wmin, cmin);
507 return ERROR;
508 }
509 }
511 /* if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) { */
512 /* printf ("At least one threshold must be set\n"); */
513 /* return ERROR; */
514 /* } */
516 if (options == 0)
517 options = ALL;
519 if (statopts==NULL)
520 statopts = strdup("");
522 if (prog==NULL)
523 prog = strdup("");
525 if (args==NULL)
526 args = strdup("");
528 if (fmt==NULL)
529 fmt = strdup("");
531 if (fails==NULL)
532 fails = strdup("");
534 return options;
535 }
539 /* Check thresholds against value */
540 int
541 check_thresholds (int value)
542 {
543 if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
544 return OK;
545 }
546 else if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
547 if (value > cmax && value < cmin)
548 return STATE_CRITICAL;
549 }
550 else if (cmax >= 0 && value > cmax) {
551 return STATE_CRITICAL;
552 }
553 else if (cmin >= 0 && value < cmin) {
554 return STATE_CRITICAL;
555 }
557 if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
558 if (value > wmax && value < wmin) {
559 return STATE_WARNING;
560 }
561 }
562 else if (wmax >= 0 && value > wmax) {
563 return STATE_WARNING;
564 }
565 else if (wmin >= 0 && value < wmin) {
566 return STATE_WARNING;
567 }
568 return STATE_OK;
569 }
573 void
574 print_help (void)
575 {
576 print_revision (progname, revision);
578 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>");
579 printf (COPYRIGHT, copyright, email);
581 printf(_("\
582 Checks all processes and generates WARNING or CRITICAL states if the specified\n\
583 metric is outside the required threshold ranges. The metric defaults to number\n\
584 of processes. Search filters can be applied to limit the processes to check.\n\n"));
586 print_usage ();
588 printf(_("\n\
589 Required Arguments:\n\
590 -w, --warning=RANGE\n\
591 Generate warning state if metric is outside this range\n\
592 -c, --critical=RANGE\n\
593 Generate critical state if metric is outside this range\n"));
595 printf(_("\n\
596 Optional Arguments:\n\
597 -m, --metric=TYPE\n\
598 Check thresholds against metric. Valid types:\n\
599 PROCS - number of processes (default)\n\
600 VSZ - virtual memory size\n\
601 RSS - resident set memory size\n\
602 CPU - percentage cpu\n"));
604 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
606 printf(_("\
607 -v, --verbose\n\
608 Extra information. Up to 3 verbosity levels\n"));
610 printf(_("\n\
611 Optional Filters:\n\
612 -s, --state=STATUSFLAGS\n\
613 Only scan for processes that have, in the output of `ps`, one or\n\
614 more of the status flags you specify (for example R, Z, S, RS,\n\
615 RSZDT, plus others based on the output of your 'ps' command).\n\
616 -p, --ppid=PPID\n\
617 Only scan for children of the parent process ID indicated.\n\
618 -z, --vsz=VSZ\n\
619 Only scan for processes with vsz higher than indicated.\n\
620 -r, --rss=RSS\n\
621 Only scan for processes with rss higher than indicated.\n"));
623 printf(_("\
624 -P, --pcpu=PCPU\n\
625 Only scan for processes with pcpu higher than indicated.\n\
626 -u, --user=USER\n\
627 Only scan for processes with user name or ID indicated.\n\
628 -a, --argument-array=STRING\n\
629 Only scan for processes with args that contain STRING.\n\
630 -C, --command=COMMAND\n\
631 Only scan for exact matches of COMMAND (without path).\n"));
633 printf(_("\n\
634 RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\
635 specified 'max:min', a warning status will be generated if the\n\
636 count is inside the specified range\n\n"));
638 printf(_("\
639 This plugin checks the number of currently running processes and\n\
640 generates WARNING or CRITICAL states if the process count is outside\n\
641 the specified threshold ranges. The process count can be filtered by\n\
642 process owner, parent process PID, current state (e.g., 'Z'), or may\n\
643 be the total number of running processes\n\n"));
645 printf(_("\
646 Examples:\n\
647 check_procs -w 2:2 -c 2:1024 -C portsentry\n\
648 Warning if not two processes with command name portsentry. Critical\n\
649 if < 2 or > 1024 processes\n\n\
650 check_procs -w 10 -a '/usr/local/bin/perl' -u root\n\
651 Warning alert if > 10 processes with command arguments containing \n\
652 '/usr/local/bin/perl' and owned by root\n\n\
653 check_procs -w 50000 -c 100000 --metric=VSZ\n\
654 Alert if vsz of any processes over 50K or 100K\n\
655 check_procs -w 10 -c 20 --metric=CPU\n\
656 Alert if cpu of any processes over 10% or 20%\n\n"));
658 printf (_(UT_SUPPORT));
659 }
663 void
664 print_usage (void)
665 {
666 printf ("\
667 Usage: %s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n\
668 [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n\
669 [-C command] [-t timeout] [-v]\n", progname);
670 printf (_(UT_HLP_VRS), progname, progname);
671 }