summaryrefslogtreecommitdiffstats
path: root/plugins/check_procs.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_procs.c')
-rw-r--r--plugins/check_procs.c362
1 files changed, 260 insertions, 102 deletions
diff --git a/plugins/check_procs.c b/plugins/check_procs.c
index 29f86bed..2cf550cc 100644
--- a/plugins/check_procs.c
+++ b/plugins/check_procs.c
@@ -39,11 +39,64 @@ const char *progname = "check_procs";
39#define COPYRIGHT "1999-2002" 39#define COPYRIGHT "1999-2002"
40#define AUTHOR "Ethan Galstad" 40#define AUTHOR "Ethan Galstad"
41#define EMAIL "nagios@nagios.org" 41#define EMAIL "nagios@nagios.org"
42#define SUMMARY "Check the number of currently running processes and generates WARNING or\n\ 42#define SUMMARY "\
43CRITICAL states if the process count is outside the specified threshold\n\ 43Checks all processes and generates WARNING or CRITICAL states if the specified\n\
44ranges. The process count can be filtered by process owner, parent process\n\ 44metric is outside the required threshold ranges. The metric defaults to number\n\
45PID, current state (e.g., 'Z'), or may be the total number of running\n\ 45of processes. Search filters can be applied to limit the processes to check.\n"
46processes\n" 46
47#define OPTIONS "\
48-w <range> -c <range> [-m metric]\n\
49 [-s state] [-p ppid] [-u user] [-r rss] [-z vsz] [-P %cpu]\n\
50 [-a argument-array] [-C command] [-v]"
51
52#define LONGOPTIONS "\
53Required Arguments:\n\
54 -w, --warning=RANGE\n\
55 Generate warning state if metric is outside this range\n\
56 -c, --critical=RANGE\n\
57 Generate critical state if metric is outside this range\n\
58Optional Arguments:\n\
59 -m, --metric=TYPE\n\
60 Check thresholds against metric. Valid types:\n\
61 PROCS - number of processes (default)\n\
62 VSZ - virtual memory size\n\
63 RSS - resident set memory size\n\
64 CPU - percentage cpu\n\
65 -v, --verbose\n\
66 Extra information. Up to 3 verbosity levels\n\
67Optional Filters:\n\
68 -s, --state=STATUSFLAGS\n\
69 Only scan for processes that have, in the output of `ps`, one or\n\
70 more of the status flags you specify (for example R, Z, S, RS,\n\
71 RSZDT, plus others based on the output of your 'ps' command).\n\
72 -p, --ppid=PPID\n\
73 Only scan for children of the parent process ID indicated.\n\
74 -z, --vsz=VSZ\n\
75 Only scan for processes with vsz higher than indicated.\n\
76 -r, --rss=RSS\n\
77 Only scan for processes with rss higher than indicated.\n\
78 -P, --pcpu=PCPU\n\
79 Only scan for processes with pcpu higher than indicated.\n\
80 -u, --user=USER\n\
81 Only scan for processes with user name or ID indicated.\n\
82 -a, --argument-array=STRING\n\
83 Only scan for ARGS that match up to the length of the given STRING.\n\
84 -C, --command=COMMAND\n\
85 Only scan for exact matches to the named COMMAND.\n\
86\n\
87RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\
88specified 'max:min', a warning status will be generated if the\n\
89count is inside the specified range\n"
90
91#define EXAMPLES "\
92 check_procs -w 2:2 -c 2:1024 -C portsentry\n\
93 Warning if not two processes with command name portsentry. Critical\n\
94 if < 2 or > 1024 processes\n\
95 check_procs -w 10 -a '/usr/local/bin/perl' -u root\n\
96 Warning alert if > 10 processes with command arguments containing \n\
97 '/usr/local/bin/perl' and owned by root\n\
98 check_procs -w 50000 -c 100000 --metric=VSZ\n\
99 Alert if vsz of any processes over 50K or 100K\n"
47 100
48#include "config.h" 101#include "config.h"
49#include <pwd.h> 102#include <pwd.h>
@@ -55,6 +108,7 @@ int process_arguments (int, char **);
55int validate_arguments (void); 108int validate_arguments (void);
56void print_usage (void); 109void print_usage (void);
57void print_help (void); 110void print_help (void);
111int check_thresholds (int);
58 112
59int wmax = -1; 113int wmax = -1;
60int cmax = -1; 114int cmax = -1;
@@ -68,16 +122,30 @@ int options = 0; /* bitmask of filter criteria to test against */
68#define USER 8 122#define USER 8
69#define PROG 16 123#define PROG 16
70#define ARGS 32 124#define ARGS 32
71 125#define VSZ 64
72int verbose = FALSE; 126#define RSS 128
127#define PCPU 256
128
129/* Different metrics */
130int metric = 0;
131#define METRIC_PROCS 0
132#define METRIC_VSZ 1
133#define METRIC_RSS 2
134#define METRIC_CPU 3
135char *metric_name = "";
136
137int verbose = 0;
73int uid; 138int uid;
74int ppid; 139int ppid;
140int vsz;
141int rss;
142float pcpu;
75char *statopts = ""; 143char *statopts = "";
76char *prog = ""; 144char *prog = "";
77char *args = ""; 145char *args = "";
78char *fmt = ""; 146char *fmt = "";
147char *fails = "";
79char tmp[MAX_INPUT_BUFFER]; 148char tmp[MAX_INPUT_BUFFER];
80const char *zombie = "Z";
81 149
82int 150int
83main (int argc, char **argv) 151main (int argc, char **argv)
@@ -86,24 +154,35 @@ main (int argc, char **argv)
86 154
87 int procuid = 0; 155 int procuid = 0;
88 int procppid = 0; 156 int procppid = 0;
157 int procvsz = 0;
158 int procrss = 0;
159 float procpcpu = 0;
89 char procstat[8]; 160 char procstat[8];
90 char procprog[MAX_INPUT_BUFFER]; 161 char procprog[MAX_INPUT_BUFFER];
91 char *procargs; 162 char *procargs;
92 163
164 const char *zombie = "Z";
165
93 int resultsum = 0; /* bitmask of the filter criteria met by a process */ 166 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 */ 167 int found = 0; /* counter for number of lines returned in `ps` output */
95 int procs = 0; /* counter for number of processes meeting filter criteria */ 168 int procs = 0; /* counter for number of processes meeting filter criteria */
96 int pos; /* number of spaces before 'args' in `ps` output */ 169 int pos; /* number of spaces before 'args' in `ps` output */
97 int cols; /* number of columns in ps output */ 170 int cols; /* number of columns in ps output */
171 int warn = 0; /* number of processes in warn state */
172 int crit = 0; /* number of processes in crit state */
173 int i;
98 174
99 int result = STATE_UNKNOWN; 175 int result = STATE_UNKNOWN;
100 176
177 asprintf (&metric_name, "PROCS");
178 metric = METRIC_PROCS;
179
101 if (process_arguments (argc, argv) == ERROR) 180 if (process_arguments (argc, argv) == ERROR)
102 usage ("Unable to parse command line\n"); 181 usage ("Unable to parse command line\n");
103 182
104 /* run the command */ 183 if (verbose >= 2)
105 if (verbose) 184 printf ("CMD: %s\n", PS_COMMAND);
106 printf ("%s\n", PS_COMMAND); 185
107 child_process = spopen (PS_COMMAND); 186 child_process = spopen (PS_COMMAND);
108 if (child_process == NULL) { 187 if (child_process == NULL) {
109 printf ("Could not open pipe: %s\n", PS_COMMAND); 188 printf ("Could not open pipe: %s\n", PS_COMMAND);
@@ -117,18 +196,22 @@ main (int argc, char **argv)
117 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process); 196 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
118 197
119 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { 198 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
199 strcpy(procprog,"");
200 asprintf(&procargs,"");
201
120 cols = sscanf (input_buffer, PS_FORMAT, PS_VARLIST); 202 cols = sscanf (input_buffer, PS_FORMAT, PS_VARLIST);
121 203
122 /* Zombie processes do not give a procprog command */ 204 /* Zombie processes do not give a procprog command */
205 /* - would they give other columns? */
123 if ( cols == 3 && strstr(procstat, zombie) ) { 206 if ( cols == 3 && strstr(procstat, zombie) ) {
124 strcpy(procprog, "");
125 cols = 4; 207 cols = 4;
126 } 208 }
127 if ( cols >= 4 ) { 209 if ( cols >= 7 ) {
128 found++; 210 found++;
129 resultsum = 0; 211 resultsum = 0;
130 asprintf (&procargs, "%s", input_buffer + pos); 212 asprintf (&procargs, "%s", input_buffer + pos);
131 strip (procargs); 213 strip (procargs);
214
132 if ((options & STAT) && (strstr (statopts, procstat))) 215 if ((options & STAT) && (strstr (statopts, procstat)))
133 resultsum |= STAT; 216 resultsum |= STAT;
134 if ((options & ARGS) && procargs && (strstr (procargs, args) == procargs)) 217 if ((options & ARGS) && procargs && (strstr (procargs, args) == procargs))
@@ -139,16 +222,43 @@ main (int argc, char **argv)
139 resultsum |= PPID; 222 resultsum |= PPID;
140 if ((options & USER) && (procuid == uid)) 223 if ((options & USER) && (procuid == uid))
141 resultsum |= USER; 224 resultsum |= USER;
142#ifdef DEBUG1 225 if ((options & VSZ) && (procvsz >= vsz))
143 if (procargs == NULL) 226 resultsum |= VSZ;
144 printf ("%d %d %d %s %s\n", procs, procuid, procppid, procstat, 227 if ((options & RSS) && (procrss >= rss))
145 procprog); 228 resultsum |= RSS;
146 else 229 if ((options & PCPU) && (procpcpu >= pcpu))
147 printf ("%d %d %d %s %s %s\n", procs, procuid, procppid, procstat, 230 resultsum |= PCPU;
148 procprog, procargs); 231
149#endif 232 if (verbose >= 3)
150 if (options == resultsum) 233 printf ("%d %d %d %d %d %.2f %s %s %s\n",
151 procs++; 234 procs, procuid, procvsz, procrss,
235 procppid, procpcpu, procstat, procprog, procargs);
236
237 /* Next line if filters not matched */
238 if (!(options == resultsum || options == ALL))
239 continue;
240
241 procs++;
242
243 if (metric == METRIC_VSZ)
244 i = check_thresholds (procvsz);
245 else if (metric == METRIC_RSS)
246 i = check_thresholds (procrss);
247 /* TODO? float thresholds for --metric=CPU */
248 else if (metric == METRIC_CPU)
249 i = check_thresholds ((int)procpcpu);
250
251 if (metric != METRIC_PROCS) {
252 if (i == STATE_WARNING) {
253 warn++;
254 asprintf (&fails, "%s%s%s", fails, (fails == "" ? "" : ", "), procprog);
255 }
256 if (i == STATE_CRITICAL) {
257 crit++;
258 asprintf (&fails, "%s%s%s", fails, (fails == "" ? "" : ", "), procprog);
259 }
260 result = max_state (result, i);
261 }
152 } 262 }
153 /* This should not happen */ 263 /* This should not happen */
154 else if (verbose) { 264 else if (verbose) {
@@ -172,65 +282,83 @@ main (int argc, char **argv)
172 result = max_state (result, STATE_WARNING); 282 result = max_state (result, STATE_WARNING);
173 } 283 }
174 284
175 if (options == ALL)
176 procs = found;
177
178 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */ 285 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
179 printf ("Unable to read output\n"); 286 printf ("Unable to read output\n");
180
181 return result; 287 return result;
182 } 288 }
183 289
184 if (verbose && (options & STAT)) 290 if ( result == STATE_UNKNOWN )
185 printf ("%s ", statopts); 291 result = STATE_OK;
186 if (verbose && (options & PROG)) 292
187 printf ("%s ", prog); 293 /* Needed if procs found, but none match filter */
188 if (verbose && (options & PPID)) 294 if ( metric == METRIC_PROCS ) {
189 printf ("%d ", ppid); 295 result = max_state (result, check_thresholds (procs) );
190 if (verbose && (options & USER)) 296 }
191 printf ("%d ", uid);
192 297
298 if ( result == STATE_OK ) {
299 printf ("%s OK: %d process%s",
300 metric_name, procs, ( procs != 1 ? "es" : "") );
301 } else if (result == STATE_WARNING) {
302 if ( metric == METRIC_PROCS ) {
303 printf ("PROCS WARNING: %d process%s", procs,
304 ( procs != 1 ? "es" : ""));
305 } else {
306 printf ("%s WARNING: %d warn out of %d process%s",
307 metric_name, warn, procs,
308 ( procs != 1 ? "es" : ""));
309 }
310 } else if (result == STATE_CRITICAL) {
311 if (metric == METRIC_PROCS) {
312 printf ("PROCS CRITICAL: %d process%s", procs,
313 ( procs != 1 ? "es" : ""));
314 } else {
315 printf ("%s CRITICAL: %d crit, %d warn out of %d process%s",
316 metric_name, crit, warn, procs,
317 ( procs != 1 ? "es" : ""));
318 }
319 }
320
321 if (fmt != "") {
322 printf (" with %s", fmt);
323 }
324
325 if ( verbose >= 1 && fails != "" )
326 printf (" [%s]", fails);
327
328 printf ("\n");
329 return result;
330}
331
332/* Check thresholds against value */
333int
334check_thresholds (int value)
335{
193 if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) { 336 if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
194 if (result == STATE_UNKNOWN) 337 return OK;
195 result = STATE_OK;
196 printf (fmt, "OK", procs);
197 return result;
198 } 338 }
199 else if (cmax >= 0 && cmin >= 0 && cmax < cmin) { 339 else if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
200 if (procs > cmax && procs < cmin) { 340 if (value > cmax && value < cmin)
201 printf (fmt, "CRITICAL", procs);
202 return STATE_CRITICAL; 341 return STATE_CRITICAL;
203 }
204 } 342 }
205 else if (cmax >= 0 && procs > cmax) { 343 else if (cmax >= 0 && value > cmax) {
206 printf (fmt, "CRITICAL", procs);
207 return STATE_CRITICAL; 344 return STATE_CRITICAL;
208 } 345 }
209 else if (cmin >= 0 && procs < cmin) { 346 else if (cmin >= 0 && value < cmin) {
210 printf (fmt, "CRITICAL", procs);
211 return STATE_CRITICAL; 347 return STATE_CRITICAL;
212 } 348 }
213 349
214 if (wmax >= 0 && wmin >= 0 && wmax < wmin) { 350 if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
215 if (procs > wmax && procs < wmin) { 351 if (value > wmax && value < wmin) {
216 printf (fmt, "CRITICAL", procs); 352 return STATE_WARNING;
217 return STATE_CRITICAL;
218 } 353 }
219 } 354 }
220 else if (wmax >= 0 && procs > wmax) { 355 else if (wmax >= 0 && value > wmax) {
221 printf (fmt, "WARNING", procs); 356 return STATE_WARNING;
222 return max_state (result, STATE_WARNING);
223 } 357 }
224 else if (wmin >= 0 && procs < wmin) { 358 else if (wmin >= 0 && value < wmin) {
225 printf (fmt, "WARNING", procs); 359 return STATE_WARNING;
226 return max_state (result, STATE_WARNING);
227 }
228
229 printf (fmt, "OK", procs);
230 if ( result == STATE_UNKNOWN ) {
231 result = STATE_OK;
232 } 360 }
233 return result; 361 return STATE_OK;
234} 362}
235 363
236/* process command-line arguments */ 364/* process command-line arguments */
@@ -244,10 +372,14 @@ process_arguments (int argc, char **argv)
244 static struct option long_options[] = { 372 static struct option long_options[] = {
245 {"warning", required_argument, 0, 'w'}, 373 {"warning", required_argument, 0, 'w'},
246 {"critical", required_argument, 0, 'c'}, 374 {"critical", required_argument, 0, 'c'},
375 {"metric", required_argument, 0, 'm'},
247 {"timeout", required_argument, 0, 't'}, 376 {"timeout", required_argument, 0, 't'},
248 {"status", required_argument, 0, 's'}, 377 {"status", required_argument, 0, 's'},
249 {"ppid", required_argument, 0, 'p'}, 378 {"ppid", required_argument, 0, 'p'},
250 {"command", required_argument, 0, 'C'}, 379 {"command", required_argument, 0, 'C'},
380 {"vsz", required_argument, 0, 'z'},
381 {"rss", required_argument, 0, 'r'},
382 {"pcpu", required_argument, 0, 'P'},
251 {"argument-array", required_argument, 0, 'a'}, 383 {"argument-array", required_argument, 0, 'a'},
252 {"help", no_argument, 0, 'h'}, 384 {"help", no_argument, 0, 'h'},
253 {"version", no_argument, 0, 'V'}, 385 {"version", no_argument, 0, 'V'},
@@ -260,7 +392,8 @@ process_arguments (int argc, char **argv)
260 strcpy (argv[c], "-t"); 392 strcpy (argv[c], "-t");
261 393
262 while (1) { 394 while (1) {
263 c = getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:", long_options, &option_index); 395 c = getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:z:r:m:P:",
396 long_options, &option_index);
264 397
265 if (c == -1 || c == EOF) 398 if (c == -1 || c == EOF)
266 break; 399 break;
@@ -368,17 +501,70 @@ process_arguments (int argc, char **argv)
368 break; 501 break;
369 case 'C': /* command */ 502 case 'C': /* command */
370 asprintf (&prog, "%s", optarg); 503 asprintf (&prog, "%s", optarg);
371 asprintf (&fmt, "%s%scommand name %s", fmt, (options ? ", " : ""), 504 asprintf (&fmt, "%s%scommand name '%s'", fmt, (options ? ", " : ""),
372 prog); 505 prog);
373 options |= PROG; 506 options |= PROG;
374 break; 507 break;
375 case 'a': /* args (full path name with args) */ 508 case 'a': /* args (full path name with args) */
376 asprintf (&args, "%s", optarg); 509 asprintf (&args, "%s", optarg);
377 asprintf (&fmt, "%s%sargs %s", fmt, (options ? ", " : ""), args); 510 asprintf (&fmt, "%s%sargs '%s'", fmt, (options ? ", " : ""), args);
378 options |= ARGS; 511 options |= ARGS;
379 break; 512 break;
513 case 'r': /* RSS */
514 if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) {
515 asprintf (&fmt, "%s%sRSS >= %d", fmt, (options ? ", " : ""), rss);
516 options |= RSS;
517 break;
518 }
519 printf ("%s: RSS must be an integer!\n\n",
520 progname);
521 print_usage ();
522 exit (STATE_UNKNOWN);
523 case 'z': /* VSZ */
524 if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) {
525 asprintf (&fmt, "%s%sVSZ >= %d", fmt, (options ? ", " : ""), vsz);
526 options |= VSZ;
527 break;
528 }
529 printf ("%s: VSZ must be an integer!\n\n",
530 progname);
531 print_usage ();
532 exit (STATE_UNKNOWN);
533 case 'P': /* PCPU */
534 /* TODO: -P 1.5.5 is accepted */
535 if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) {
536 asprintf (&fmt, "%s%sPCPU >= %.2f", fmt, (options ? ", " : ""), pcpu);
537 options |= PCPU;
538 break;
539 }
540 printf ("%s: PCPU must be a float!\n\n",
541 progname);
542 print_usage ();
543 exit (STATE_UNKNOWN);
544 case 'm':
545 asprintf (&metric_name, "%s", optarg);
546 if ( strcmp(optarg, "PROCS") == 0) {
547 metric = METRIC_PROCS;
548 break;
549 }
550 else if ( strcmp(optarg, "VSZ") == 0) {
551 metric = METRIC_VSZ;
552 break;
553 }
554 else if ( strcmp(optarg, "RSS") == 0 ) {
555 metric = METRIC_RSS;
556 break;
557 }
558 else if ( strcmp(optarg, "CPU") == 0 ) {
559 metric = METRIC_CPU;
560 break;
561 }
562 printf ("%s: metric must be one of PROCS, VSZ, RSS, CPU!\n\n",
563 progname);
564 print_usage ();
565 exit (STATE_UNKNOWN);
380 case 'v': /* command */ 566 case 'v': /* command */
381 verbose = TRUE; 567 verbose++;
382 break; 568 break;
383 } 569 }
384 } 570 }
@@ -402,7 +588,7 @@ int
402validate_arguments () 588validate_arguments ()
403{ 589{
404 590
405if (wmax >= 0 && wmin == -1) 591 if (wmax >= 0 && wmin == -1)
406 wmin = 0; 592 wmin = 0;
407 if (cmax >= 0 && cmin == -1) 593 if (cmax >= 0 && cmin == -1)
408 cmin = 0; 594 cmin = 0;
@@ -422,13 +608,8 @@ if (wmax >= 0 && wmin == -1)
422/* return ERROR; */ 608/* return ERROR; */
423/* } */ 609/* } */
424 610
425 if (options == 0) { 611 if (options == 0)
426 options = 1; 612 options = ALL;
427 asprintf (&fmt, "%%s - %%d processes running\n");
428 }
429 else {
430 asprintf (&fmt, "%%s - %%d processes running with %s\n", fmt);
431 }
432 613
433 return options; 614 return options;
434} 615}
@@ -442,37 +623,14 @@ print_help (void)
442 ("Copyright (c) %s %s <%s>\n\n%s\n", 623 ("Copyright (c) %s %s <%s>\n\n%s\n",
443 COPYRIGHT, AUTHOR, EMAIL, SUMMARY); 624 COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
444 print_usage (); 625 print_usage ();
445 printf 626 printf ("\nOptions:\n" LONGOPTIONS "\nExamples:\n" EXAMPLES "\n");
446 ("\nRequired Arguments:\n" 627}
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"
466
467 "count is inside the specified range\n");}
468
469 628
470void 629void
471print_usage (void) 630print_usage (void)
472{ 631{
473 printf 632 printf ("Usage:\n" " %s %s\n"
474 ("Usage:\n" 633 " %s (-h | --help) for detailed help\n"
475 " check_procs -w <range> -c <range> [-s state] [-p ppid] [-u user]\n" 634 " %s (-V | --version) for version information\n",
476 " [-a argument-array] [-C command]\n" 635 progname, OPTIONS, progname, progname);
477 " check_procs --version\n" " check_procs --help\n");
478} 636}