Code

Fix for zombie processes on Solaris (Bug 677803 - Matthew Brown)
[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                 /*Cannot use max() any more as STATE_UNKNOWN is gt STATE_CRITICAL 
168                 result = max (result, STATE_WARNING); */
169                 if ( !(result == STATE_CRITICAL) ) {
170                         result = STATE_WARNING;
171                 }
172                         printf ("System call sent warnings to stderr\n");
173         }
174         
175 /*      if (result == STATE_UNKNOWN || result == STATE_WARNING)
176                 printf ("System call sent warnings to stderr\n");
177 */
178         (void) fclose (child_stderr);
180         /* close the pipe */
181         if (spclose (child_process)) {
182                 printf ("System call returned nonzero status\n");
183                 if ( !(result == STATE_CRITICAL) ) {
184                         return STATE_WARNING;
185                 }
186                 else {
187                         return result ;
188                 }
189         }
191         if (options == ALL)
192                 procs = found;
194         if (found == 0) {                                                       /* no process lines parsed so return STATE_UNKNOWN */
195                 printf ("Unable to read output\n");
197                 return result;
198         }
200         if (verbose && (options & STAT))
201                 printf ("%s ", statopts);
202         if (verbose && (options & PROG))
203                 printf ("%s ", prog);
204         if (verbose && (options & PPID))
205                 printf ("%d ", ppid);
206         if (verbose && (options & USER))
207                 printf ("%d ", uid);
209         if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
210                 if (result == STATE_UNKNOWN)
211                         result = STATE_OK;
212                 printf (fmt, "OK", procs);
213                 return result;
214         }
215         else if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
216                 if (procs > cmax && procs < cmin) {
217                         printf (fmt, "CRITICAL", procs);
218                         return STATE_CRITICAL;
219                 }
220         }
221         else if (cmax >= 0 && procs > cmax) {
222                 printf (fmt, "CRITICAL", procs);
223                 return STATE_CRITICAL;
224         }
225         else if (cmin >= 0 && procs < cmin) {
226                 printf (fmt, "CRITICAL", procs);
227                 return STATE_CRITICAL;
228         }
230         if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
231                 if (procs > wmax && procs < wmin) {
232                         printf (fmt, "CRITICAL", procs);
233                         return STATE_CRITICAL;
234                 }
235         }
236         else if (wmax >= 0 && procs > wmax) {
237                 printf (fmt, "WARNING", procs);
238                 if ( !(result == STATE_CRITICAL) ) {
239                         return STATE_WARNING;
240                 }
241                 else {
242                         return result ;
243                 }
244                 /*return max (result, STATE_WARNING); */
245         }
246         else if (wmin >= 0 && procs < wmin) {
247                 printf (fmt, "WARNING", procs);
248                 if ( !(result == STATE_CRITICAL) ) {
249                         return STATE_WARNING;
250                 }
251                 else {
252                         return result ;
253                 }
254                 /*return max (result, STATE_WARNING); */
255         }
257         printf (fmt, "OK", procs);
258         if ( result == STATE_UNKNOWN ) {
259                 result = STATE_OK;
260         }
261         return result;
264 /* process command-line arguments */
265 int
266 process_arguments (int argc, char **argv)
268         int c = 1;
269         char *user;
270         struct passwd *pw;
271 #ifdef HAVE_GETOPT_H
272         int option_index = 0;
273         static struct option long_options[] = {
274                 {"warning", required_argument, 0, 'w'},
275                 {"critical", required_argument, 0, 'c'},
276                 {"timeout", required_argument, 0, 't'},
277                 {"status", required_argument, 0, 's'},
278                 {"ppid", required_argument, 0, 'p'},
279                 {"command", required_argument, 0, 'C'},
280                 {"argument-array", required_argument, 0, 'a'},
281                 {"help", no_argument, 0, 'h'},
282                 {"version", no_argument, 0, 'V'},
283                 {"verbose", no_argument, 0, 'v'},
284                 {0, 0, 0, 0}
285         };
286 #endif
288         for (c = 1; c < argc; c++)
289                 if (strcmp ("-to", argv[c]) == 0)
290                         strcpy (argv[c], "-t");
292         while (1) {
293 #ifdef HAVE_GETOPT_H
294                 c =     getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:", long_options, &option_index);
295 #else
296                 c = getopt (argc, argv, "Vvht:c:w:p:s:u:C:a:");
297 #endif
298                 if (c == -1 || c == EOF)
299                         break;
301                 switch (c) {
302                 case '?':                                                                       /* help */
303                         print_usage ();
304                         exit (STATE_UNKNOWN);
305                 case 'h':                                                                       /* help */
306                         print_help ();
307                         exit (STATE_OK);
308                 case 'V':                                                                       /* version */
309                         print_revision (progname, REVISION);
310                         exit (STATE_OK);
311                 case 't':                                                                       /* timeout period */
312                         if (!is_integer (optarg)) {
313                                 printf ("%s: Timeout Interval must be an integer!\n\n",
314                                         progname);
315                                 print_usage ();
316                                 exit (STATE_UNKNOWN);
317                         }
318                         timeout_interval = atoi (optarg);
319                         break;
320                 case 'c':                                                                       /* critical threshold */
321                         if (is_integer (optarg)) {
322                                 cmax = atoi (optarg);
323                                 break;
324                         }
325                         else if (sscanf (optarg, ":%d", &cmax) == 1) {
326                                 break;
327                         }
328                         else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2) {
329                                 break;
330                         }
331                         else if (sscanf (optarg, "%d:", &cmin) == 1) {
332                                 break;
333                         }
334                         else {
335                                 printf ("%s: Critical Process Count must be an integer!\n\n",
336                                         progname);
337                                 print_usage ();
338                                 exit (STATE_UNKNOWN);
339                         }
340                 case 'w':                                                                       /* warning time threshold */
341                         if (is_integer (optarg)) {
342                                 wmax = atoi (optarg);
343                                 break;
344                         }
345                         else if (sscanf (optarg, ":%d", &wmax) == 1) {
346                                 break;
347                         }
348                         else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2) {
349                                 break;
350                         }
351                         else if (sscanf (optarg, "%d:", &wmin) == 1) {
352                                 break;
353                         }
354                         else {
355                                 printf ("%s: Warning Process Count must be an integer!\n\n",
356                                         progname);
357                                 print_usage ();
358                                 exit (STATE_UNKNOWN);
359                         }
360                 case 'p':                                                                       /* process id */
361                         if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
362                                 asprintf (&fmt, "%s%sPPID = %d", fmt, (options ? ", " : ""), ppid);
363                                 options |= PPID;
364                                 break;
365                         }
366                         printf ("%s: Parent Process ID must be an integer!\n\n",
367                                 progname);
368                         print_usage ();
369                         exit (STATE_UNKNOWN);
370                 case 's':                                                                       /* status */
371                         asprintf (&statopts, "%s", optarg);
372                         asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
373                         options |= STAT;
374                         break;
375                 case 'u':                                                                       /* user or user id */
376                         if (is_integer (optarg)) {
377                                 uid = atoi (optarg);
378                                 pw = getpwuid ((uid_t) uid);
379                                 /*  check to be sure user exists */
380                                 if (pw == NULL) {
381                                         printf ("UID %d was not found\n", uid);
382                                         print_usage ();
383                                         exit (STATE_UNKNOWN);
384                                 }
385                         }
386                         else {
387                                 pw = getpwnam (optarg);
388                                 /*  check to be sure user exists */
389                                 if (pw == NULL) {
390                                         printf ("User name %s was not found\n", optarg);
391                                         print_usage ();
392                                         exit (STATE_UNKNOWN);
393                                 }
394                                 /*  then get uid */
395                                 uid = pw->pw_uid;
396                         }
397                         user = pw->pw_name;
398                         asprintf (&fmt, "%s%sUID = %d (%s)", fmt, (options ? ", " : ""),
399                                   uid, user);
400                         options |= USER;
401                         break;
402                 case 'C':                                                                       /* command */
403                         asprintf (&prog, "%s", optarg);
404                         asprintf (&fmt, "%s%scommand name %s", fmt, (options ? ", " : ""),
405                                   prog);
406                         options |= PROG;
407                         break;
408                 case 'a':                                                                       /* args (full path name with args) */
409                         asprintf (&args, "%s", optarg);
410                         asprintf (&fmt, "%s%sargs %s", fmt, (options ? ", " : ""), args);
411                         options |= ARGS;
412                         break;
413                 case 'v':                                                                       /* command */
414                         verbose = TRUE;
415                         break;
416                 }
417         }
419         c = optind;
420         if (wmax == -1 && argv[c])
421                 wmax = atoi (argv[c++]);
422         if (cmax == -1 && argv[c])
423                 cmax = atoi (argv[c++]);
424         if (statopts == NULL && argv[c]) {
425                 asprintf (&statopts, "%s", argv[c++]);
426                 asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
427                 options |= STAT;
428         }
430         return validate_arguments ();
434 int
435 validate_arguments ()
438 if (wmax >= 0 && wmin == -1)
439                 wmin = 0;
440         if (cmax >= 0 && cmin == -1)
441                 cmin = 0;
442         if (wmax >= wmin && cmax >= cmin) {     /* standard ranges */
443                 if (wmax > cmax && cmax != -1) {
444                         printf ("wmax (%d) cannot be greater than cmax (%d)\n", wmax, cmax);
445                         return ERROR;
446                 }
447                 if (cmin > wmin && wmin != -1) {
448                         printf ("wmin (%d) cannot be less than cmin (%d)\n", wmin, cmin);
449                         return ERROR;
450                 }
451         }
453 /*      if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) { */
454 /*              printf ("At least one threshold must be set\n"); */
455 /*              return ERROR; */
456 /*      } */
458         if (options == 0) {
459                 options = 1;
460                 asprintf (&fmt, "%%s - %%d processes running\n");
461         }
462         else {
463                 asprintf (&fmt, "%%s - %%d processes running with %s\n", fmt);
464         }
466         return options;
470 void
471 print_help (void)
473         print_revision (progname, REVISION);
474         printf
475                 ("Copyright (c) %s %s <%s>\n\n%s\n",
476                  COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
477         print_usage ();
478         printf
479                 ("\nRequired Arguments:\n"
480                  " -w, --warning=RANGE\n"
481                  "    generate warning state if process count is outside this range\n"
482                  " -c, --critical=RANGE\n"
483                  "    generate critical state if process count is outside this range\n\n"
484                  "Optional Filters:\n"
485                  " -s, --state=STATUSFLAGS\n"
486                  "    Only scan for processes that have, in the output of `ps`, one or\n"
487                  "    more of the status flags you specify (for example R, Z, S, RS,\n"
488                  "    RSZDT, plus others based on the output of your 'ps' command).\n"
489                  " -p, --ppid=PPID\n"
490                  "    Only scan for children of the parent process ID indicated.\n"
491                  " -u, --user=USER\n"
492                  "    Only scan for proceses with user name or ID indicated.\n"
493                  " -a, --argument-array=STRING\n"
494                  "    Only scan for ARGS that match up to the length of the given STRING\n"
495                  " -C, --command=COMMAND\n"
496                  "    Only scan for exact matches to the named COMMAND.\n\n"
497                  "RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n"
498                  "specified 'max:min', a warning status will be generated if the\n"
500                  "count is inside the specified range\n");}
503 void
504 print_usage (void)
506         printf
507                 ("Usage:\n"
508                  " check_procs -w <range> -c <range> [-s state] [-p ppid] [-u user]\n"
509                  "             [-a argument-array] [-C command]\n"
510                  " check_procs --version\n" " check_procs --help\n");