Code

Use max_state
[nagiosplug.git] / plugins / check_procs.c
1 /******************************************************************************
2 *
3 * CHECK_PROCS.C
4 *
5 * Program: Process plugin for Nagios
6 * License: GPL
7 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
8 *
9 * $Id$
10 *
11 * Description:
12 *
13 * This plugin checks the number of currently running processes and
14 * generates WARNING or CRITICAL states if the process count is outside
15 * the specified threshold ranges. The process count can be filtered by
16 * process owner, parent process PID, current state (e.g., 'Z'), or may
17 * be the total number of running processes
18 *
19 * License Information:
20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful, but
27 * WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
29 * General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34 *
35 ******************************************************************************/
37 const char *progname = "check_procs";
38 #define REVISION "$Revision$"
39 #define COPYRIGHT "1999-2002"
40 #define AUTHOR "Ethan Galstad"
41 #define EMAIL "nagios@nagios.org"
42 #define SUMMARY "Check the number of currently running processes and generates WARNING or\n\
43 CRITICAL states if the process count is outside the specified threshold\n\
44 ranges. The process count can be filtered by process owner, parent process\n\
45 PID, current state (e.g., 'Z'), or may be the total number of running\n\
46 processes\n"
48 #include "config.h"
49 #include <pwd.h>
50 #include "common.h"
51 #include "popen.h"
52 #include "utils.h"
54 int process_arguments (int, char **);
55 int validate_arguments (void);
56 void print_usage (void);
57 void print_help (void);
59 int wmax = -1;
60 int cmax = -1;
61 int wmin = -1;
62 int cmin = -1;
64 int options = 0; /* bitmask of filter criteria to test against */
65 #define ALL 1
66 #define STAT 2
67 #define PPID 4
68 #define USER 8
69 #define PROG 16
70 #define ARGS 32
72 int verbose = FALSE;
73 int uid;
74 int ppid;
75 char *statopts = "";
76 char *prog = "";
77 char *args = "";
78 char *fmt = "";
79 char tmp[MAX_INPUT_BUFFER];
80 const char *zombie = "Z";
82 int
83 main (int argc, char **argv)
84 {
85         char input_buffer[MAX_INPUT_BUFFER];
87         int procuid = 0;
88         int procppid = 0;
89         char procstat[8];
90         char procprog[MAX_INPUT_BUFFER];
91         char *procargs;
93         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 */
95         int procs = 0; /* counter for number of processes meeting filter criteria */
96         int pos; /* number of spaces before 'args' in `ps` output */
97         int cols; /* number of columns in ps output */
99         int result = STATE_UNKNOWN;
101         if (process_arguments (argc, argv) == ERROR)
102                 usage ("Unable to parse command line\n");
104         /* run the command */
105         if (verbose)
106                 printf ("%s\n", PS_COMMAND);
107         child_process = spopen (PS_COMMAND);
108         if (child_process == NULL) {
109                 printf ("Could not open pipe: %s\n", PS_COMMAND);
110                 return STATE_UNKNOWN;
111         }
113         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
114         if (child_stderr == NULL)
115                 printf ("Could not open stderr for %s\n", PS_COMMAND);
117         fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
119         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
120 #ifdef USE_PS_VARS
121                 cols = sscanf (input_buffer, PS_FORMAT, PS_VARLIST);
122 #else
123                 cols = sscanf (input_buffer, PS_FORMAT, procstat, &procuid, 
124                                                         &procppid, &pos, procprog);
125 #endif
126                 /* Zombie processes do not give a procprog command */
127                 if ( cols == 3 && strstr(procstat, zombie) ) {
128                         strcpy(procprog, "");
129                         cols = 4;
130                 }
131                 if ( cols >= 4 ) {
132                         found++;
133                         resultsum = 0;
134                         asprintf (&procargs, "%s", input_buffer + pos);
135                         strip (procargs);
136                         if ((options & STAT) && (strstr (statopts, procstat)))
137                                 resultsum |= STAT;
138                         if ((options & ARGS) && procargs && (strstr (procargs, args) == procargs))
139                                 resultsum |= ARGS;
140                         if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0))
141                                 resultsum |= PROG;
142                         if ((options & PPID) && (procppid == ppid))
143                                 resultsum |= PPID;
144                         if ((options & USER) && (procuid == uid))
145                                 resultsum |= USER;
146 #ifdef DEBUG1
147                         if (procargs == NULL)
148                                 printf ("%d %d %d %s %s\n", procs, procuid, procppid, procstat,
149                                                                 procprog);
150                         else
151                                 printf ("%d %d %d %s %s %s\n", procs, procuid, procppid, procstat,
152                                                                 procprog, procargs);
153 #endif
154                         if (options == resultsum)
155                                 procs++;
156                 } 
157                 /* This should not happen */
158                 else if (verbose) {
159                         printf("Not parseable: %s", input_buffer);
160                 }
161         }
163         /* If we get anything on STDERR, at least set warning */
164         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
165                 if (verbose)
166                         printf ("STDERR: %s", input_buffer);
167                 result = max_state (result, STATE_WARNING);
168                 printf ("System call sent warnings to stderr\n");
169         }
170         
171         (void) fclose (child_stderr);
173         /* close the pipe */
174         if (spclose (child_process)) {
175                 printf ("System call returned nonzero status\n");
176                 result = max_state (result, STATE_WARNING);
177         }
179         if (options == ALL)
180                 procs = found;
182         if (found == 0) {                                                       /* no process lines parsed so return STATE_UNKNOWN */
183                 printf ("Unable to read output\n");
185                 return result;
186         }
188         if (verbose && (options & STAT))
189                 printf ("%s ", statopts);
190         if (verbose && (options & PROG))
191                 printf ("%s ", prog);
192         if (verbose && (options & PPID))
193                 printf ("%d ", ppid);
194         if (verbose && (options & USER))
195                 printf ("%d ", uid);
197         if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
198                 if (result == STATE_UNKNOWN)
199                         result = STATE_OK;
200                 printf (fmt, "OK", procs);
201                 return result;
202         }
203         else if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
204                 if (procs > cmax && procs < cmin) {
205                         printf (fmt, "CRITICAL", procs);
206                         return STATE_CRITICAL;
207                 }
208         }
209         else if (cmax >= 0 && procs > cmax) {
210                 printf (fmt, "CRITICAL", procs);
211                 return STATE_CRITICAL;
212         }
213         else if (cmin >= 0 && procs < cmin) {
214                 printf (fmt, "CRITICAL", procs);
215                 return STATE_CRITICAL;
216         }
218         if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
219                 if (procs > wmax && procs < wmin) {
220                         printf (fmt, "CRITICAL", procs);
221                         return STATE_CRITICAL;
222                 }
223         }
224         else if (wmax >= 0 && procs > wmax) {
225                 printf (fmt, "WARNING", procs);
226                 return max_state (result, STATE_WARNING);
227         }
228         else if (wmin >= 0 && procs < wmin) {
229                 printf (fmt, "WARNING", procs);
230                 return max_state (result, STATE_WARNING);
231         }
233         printf (fmt, "OK", procs);
234         if ( result == STATE_UNKNOWN ) {
235                 result = STATE_OK;
236         }
237         return result;
240 /* process command-line arguments */
241 int
242 process_arguments (int argc, char **argv)
244         int c = 1;
245         char *user;
246         struct passwd *pw;
247         int option_index = 0;
248         static struct option long_options[] = {
249                 {"warning", required_argument, 0, 'w'},
250                 {"critical", required_argument, 0, 'c'},
251                 {"timeout", required_argument, 0, 't'},
252                 {"status", required_argument, 0, 's'},
253                 {"ppid", required_argument, 0, 'p'},
254                 {"command", required_argument, 0, 'C'},
255                 {"argument-array", required_argument, 0, 'a'},
256                 {"help", no_argument, 0, 'h'},
257                 {"version", no_argument, 0, 'V'},
258                 {"verbose", no_argument, 0, 'v'},
259                 {0, 0, 0, 0}
260         };
262         for (c = 1; c < argc; c++)
263                 if (strcmp ("-to", argv[c]) == 0)
264                         strcpy (argv[c], "-t");
266         while (1) {
267                 c = getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:", long_options, &option_index);
269                 if (c == -1 || c == EOF)
270                         break;
272                 switch (c) {
273                 case '?':                                                                       /* help */
274                         print_usage ();
275                         exit (STATE_UNKNOWN);
276                 case 'h':                                                                       /* help */
277                         print_help ();
278                         exit (STATE_OK);
279                 case 'V':                                                                       /* version */
280                         print_revision (progname, REVISION);
281                         exit (STATE_OK);
282                 case 't':                                                                       /* timeout period */
283                         if (!is_integer (optarg)) {
284                                 printf ("%s: Timeout Interval must be an integer!\n\n",
285                                         progname);
286                                 print_usage ();
287                                 exit (STATE_UNKNOWN);
288                         }
289                         timeout_interval = atoi (optarg);
290                         break;
291                 case 'c':                                                                       /* critical threshold */
292                         if (is_integer (optarg)) {
293                                 cmax = atoi (optarg);
294                                 break;
295                         }
296                         else if (sscanf (optarg, ":%d", &cmax) == 1) {
297                                 break;
298                         }
299                         else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2) {
300                                 break;
301                         }
302                         else if (sscanf (optarg, "%d:", &cmin) == 1) {
303                                 break;
304                         }
305                         else {
306                                 printf ("%s: Critical Process Count must be an integer!\n\n",
307                                         progname);
308                                 print_usage ();
309                                 exit (STATE_UNKNOWN);
310                         }
311                 case 'w':                                                                       /* warning time threshold */
312                         if (is_integer (optarg)) {
313                                 wmax = atoi (optarg);
314                                 break;
315                         }
316                         else if (sscanf (optarg, ":%d", &wmax) == 1) {
317                                 break;
318                         }
319                         else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2) {
320                                 break;
321                         }
322                         else if (sscanf (optarg, "%d:", &wmin) == 1) {
323                                 break;
324                         }
325                         else {
326                                 printf ("%s: Warning Process Count must be an integer!\n\n",
327                                         progname);
328                                 print_usage ();
329                                 exit (STATE_UNKNOWN);
330                         }
331                 case 'p':                                                                       /* process id */
332                         if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
333                                 asprintf (&fmt, "%s%sPPID = %d", fmt, (options ? ", " : ""), ppid);
334                                 options |= PPID;
335                                 break;
336                         }
337                         printf ("%s: Parent Process ID must be an integer!\n\n",
338                                 progname);
339                         print_usage ();
340                         exit (STATE_UNKNOWN);
341                 case 's':                                                                       /* status */
342                         asprintf (&statopts, "%s", optarg);
343                         asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
344                         options |= STAT;
345                         break;
346                 case 'u':                                                                       /* user or user id */
347                         if (is_integer (optarg)) {
348                                 uid = atoi (optarg);
349                                 pw = getpwuid ((uid_t) uid);
350                                 /*  check to be sure user exists */
351                                 if (pw == NULL) {
352                                         printf ("UID %d was not found\n", uid);
353                                         print_usage ();
354                                         exit (STATE_UNKNOWN);
355                                 }
356                         }
357                         else {
358                                 pw = getpwnam (optarg);
359                                 /*  check to be sure user exists */
360                                 if (pw == NULL) {
361                                         printf ("User name %s was not found\n", optarg);
362                                         print_usage ();
363                                         exit (STATE_UNKNOWN);
364                                 }
365                                 /*  then get uid */
366                                 uid = pw->pw_uid;
367                         }
368                         user = pw->pw_name;
369                         asprintf (&fmt, "%s%sUID = %d (%s)", fmt, (options ? ", " : ""),
370                                   uid, user);
371                         options |= USER;
372                         break;
373                 case 'C':                                                                       /* command */
374                         asprintf (&prog, "%s", optarg);
375                         asprintf (&fmt, "%s%scommand name %s", fmt, (options ? ", " : ""),
376                                   prog);
377                         options |= PROG;
378                         break;
379                 case 'a':                                                                       /* args (full path name with args) */
380                         asprintf (&args, "%s", optarg);
381                         asprintf (&fmt, "%s%sargs %s", fmt, (options ? ", " : ""), args);
382                         options |= ARGS;
383                         break;
384                 case 'v':                                                                       /* command */
385                         verbose = TRUE;
386                         break;
387                 }
388         }
390         c = optind;
391         if (wmax == -1 && argv[c])
392                 wmax = atoi (argv[c++]);
393         if (cmax == -1 && argv[c])
394                 cmax = atoi (argv[c++]);
395         if (statopts == NULL && argv[c]) {
396                 asprintf (&statopts, "%s", argv[c++]);
397                 asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
398                 options |= STAT;
399         }
401         return validate_arguments ();
405 int
406 validate_arguments ()
409 if (wmax >= 0 && wmin == -1)
410                 wmin = 0;
411         if (cmax >= 0 && cmin == -1)
412                 cmin = 0;
413         if (wmax >= wmin && cmax >= cmin) {     /* standard ranges */
414                 if (wmax > cmax && cmax != -1) {
415                         printf ("wmax (%d) cannot be greater than cmax (%d)\n", wmax, cmax);
416                         return ERROR;
417                 }
418                 if (cmin > wmin && wmin != -1) {
419                         printf ("wmin (%d) cannot be less than cmin (%d)\n", wmin, cmin);
420                         return ERROR;
421                 }
422         }
424 /*      if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) { */
425 /*              printf ("At least one threshold must be set\n"); */
426 /*              return ERROR; */
427 /*      } */
429         if (options == 0) {
430                 options = 1;
431                 asprintf (&fmt, "%%s - %%d processes running\n");
432         }
433         else {
434                 asprintf (&fmt, "%%s - %%d processes running with %s\n", fmt);
435         }
437         return options;
441 void
442 print_help (void)
444         print_revision (progname, REVISION);
445         printf
446                 ("Copyright (c) %s %s <%s>\n\n%s\n",
447                  COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
448         print_usage ();
449         printf
450                 ("\nRequired Arguments:\n"
451                  " -w, --warning=RANGE\n"
452                  "    generate warning state if process count is outside this range\n"
453                  " -c, --critical=RANGE\n"
454                  "    generate critical state if process count is outside this range\n\n"
455                  "Optional Filters:\n"
456                  " -s, --state=STATUSFLAGS\n"
457                  "    Only scan for processes that have, in the output of `ps`, one or\n"
458                  "    more of the status flags you specify (for example R, Z, S, RS,\n"
459                  "    RSZDT, plus others based on the output of your 'ps' command).\n"
460                  " -p, --ppid=PPID\n"
461                  "    Only scan for children of the parent process ID indicated.\n"
462                  " -u, --user=USER\n"
463                  "    Only scan for proceses with user name or ID indicated.\n"
464                  " -a, --argument-array=STRING\n"
465                  "    Only scan for ARGS that match up to the length of the given STRING\n"
466                  " -C, --command=COMMAND\n"
467                  "    Only scan for exact matches to the named COMMAND.\n\n"
468                  "RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n"
469                  "specified 'max:min', a warning status will be generated if the\n"
471                  "count is inside the specified range\n");}
474 void
475 print_usage (void)
477         printf
478                 ("Usage:\n"
479                  " check_procs -w <range> -c <range> [-s state] [-p ppid] [-u user]\n"
480                  "             [-a argument-array] [-C command]\n"
481                  " check_procs --version\n" " check_procs --help\n");