Code

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