Code

changed Error: by CRITICAL -
[nagiosplug.git] / plugins / check_procs.c
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$
18  
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         }
246         
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);
282         
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;
296 /* process command-line arguments */
297 int
298 process_arguments (int argc, char **argv)
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 ();
493 int
494 validate_arguments ()
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;
540 /* Check thresholds against value */
541 int
542 check_thresholds (int value)
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;
574 void
575 print_help (void)
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));
664 void
665 print_usage (void)
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);