Code

add a few comments, trap a few place where a NULL string might have been handled
[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 #define PROGNAME "check_snmp"
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 = NULL;
76 char *prog = NULL;
77 char *args = NULL;
78 char *fmt = NULL;
79 char tmp[MAX_INPUT_BUFFER];
81 int
82 main (int argc, char **argv)
83 {
84         char input_buffer[MAX_INPUT_BUFFER];
86         int procuid = 0;
87         int procppid = 0;
88         char procstat[8];
89         char procprog[MAX_INPUT_BUFFER];
90         char *procargs;
92         int resultsum = 0; /* bitmask of the filter criteria met by a process */
93         int found = 0; /* counter for number of lines returned in `ps` output */
94         int procs = 0; /* counter for number of processes meeting filter criteria */
95         int pos; /* number of spaces before 'args' in `ps` output */
97         int result = STATE_UNKNOWN;
99         if (process_arguments (argc, argv) == ERROR)
100                 usage ("Unable to parse command line\n");
102         /* run the command */
103         if (verbose)
104                 printf ("%s\n", PS_COMMAND);
105         child_process = spopen (PS_COMMAND);
106         if (child_process == NULL) {
107                 printf ("Could not open pipe: %s\n", PS_COMMAND);
108                 return STATE_UNKNOWN;
109         }
111         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
112         if (child_stderr == NULL)
113                 printf ("Could not open stderr for %s\n", PS_COMMAND);
115         fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
117         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
118                 if (
119 #ifdef USE_PS_VARS
120                                  sscanf (input_buffer, PS_FORMAT, PS_VARLIST) >= 4
121 #else
122                                  sscanf (input_buffer, PS_FORMAT, procstat, &procuid, &procppid, &pos,
123                                                                  procprog) >= 4
124 #endif
125                         ) {
126                         found++;
127                         resultsum = 0;
128                         asprintf (&procargs, "%s", input_buffer + pos);
129                         strip (procargs);
130                         if ((options & STAT) && (strstr (statopts, procstat)))
131                                 resultsum |= STAT;
132                         if ((options & ARGS) && procargs && (strstr (procargs, args) == procargs))
133                                 resultsum |= ARGS;
134                         if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0))
135                                 resultsum |= PROG;
136                         if ((options & PPID) && (procppid == ppid))
137                                 resultsum |= PPID;
138                         if ((options & USER) && (procuid == uid))
139                                 resultsum |= USER;
140 #ifdef DEBUG1
141                         if (procargs == NULL)
142                                 printf ("%d %d %d %s %s\n", procs, procuid, procppid, procstat,
143                                                                 procprog);
144                         else
145                                 printf ("%d %d %d %s %s %s\n", procs, procuid, procppid, procstat,
146                                                                 procprog, procargs);
147 #endif
148                         if (options == resultsum)
149                                 procs++;
150                 }
151         }
153         /* If we get anything on STDERR, at least set warning */
154         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
155                 if (verbose)
156                         printf ("STDERR: %s", input_buffer);
157                 /*Cannot use max() any more as STATE_UNKNOWN is gt STATE_CRITICAL 
158                 result = max (result, STATE_WARNING); */
159                 if ( !(result == STATE_CRITICAL) ) {
160                         result = STATE_WARNING;
161                 }
162                         printf ("System call sent warnings to stderr\n");
163         }
164         
165 /*      if (result == STATE_UNKNOWN || result == STATE_WARNING)
166                 printf ("System call sent warnings to stderr\n");
167 */
168         (void) fclose (child_stderr);
170         /* close the pipe */
171         if (spclose (child_process)) {
172                 printf ("System call returned nonzero status\n");
173                 if ( !(result == STATE_CRITICAL) ) {
174                         return STATE_WARNING;
175                 }
176                 else {
177                         return result ;
178                 }
179         }
181         if (options == ALL)
182                 procs = found;
184         if (found == 0) {                                                       /* no process lines parsed so return STATE_UNKNOWN */
185                 printf ("Unable to read output\n");
187                 return result;
188         }
190         if (verbose && (options & STAT))
191                 printf ("%s ", statopts);
192         if (verbose && (options & PROG))
193                 printf ("%s ", prog);
194         if (verbose && (options & PPID))
195                 printf ("%d ", ppid);
196         if (verbose && (options & USER))
197                 printf ("%d ", uid);
199         if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
200                 if (result == STATE_UNKNOWN)
201                         result = STATE_OK;
202                 printf (fmt, "OK", procs);
203                 return result;
204         }
205         else if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
206                 if (procs > cmax && procs < cmin) {
207                         printf (fmt, "CRITICAL", procs);
208                         return STATE_CRITICAL;
209                 }
210         }
211         else if (cmax >= 0 && procs > cmax) {
212                 printf (fmt, "CRITICAL", procs);
213                 return STATE_CRITICAL;
214         }
215         else if (cmin >= 0 && procs < cmin) {
216                 printf (fmt, "CRITICAL", procs);
217                 return STATE_CRITICAL;
218         }
220         if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
221                 if (procs > wmax && procs < wmin) {
222                         printf (fmt, "CRITICAL", procs);
223                         return STATE_CRITICAL;
224                 }
225         }
226         else if (wmax >= 0 && procs > wmax) {
227                 printf (fmt, "WARNING", procs);
228                 if ( !(result == STATE_CRITICAL) ) {
229                         return STATE_WARNING;
230                 }
231                 else {
232                         return result ;
233                 }
234                 /*return max (result, STATE_WARNING); */
235         }
236         else if (wmin >= 0 && procs < wmin) {
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         }
247         printf (fmt, "OK", procs);
248         if ( result == STATE_UNKNOWN ) {
249                 result = STATE_OK;
250         }
251         return result;
254 /* process command-line arguments */
255 int
256 process_arguments (int argc, char **argv)
258         int c = 1;
259         char *user;
260         struct passwd *pw;
261 #ifdef HAVE_GETOPT_H
262         int option_index = 0;
263         static struct option long_options[] = {
264                 {"warning", required_argument, 0, 'w'},
265                 {"critical", required_argument, 0, 'c'},
266                 {"timeout", required_argument, 0, 't'},
267                 {"status", required_argument, 0, 's'},
268                 {"ppid", required_argument, 0, 'p'},
269                 {"command", required_argument, 0, 'C'},
270                 {"argument-array", required_argument, 0, 'a'},
271                 {"help", no_argument, 0, 'h'},
272                 {"version", no_argument, 0, 'V'},
273                 {"verbose", no_argument, 0, 'v'},
274                 {0, 0, 0, 0}
275         };
276 #endif
278         asprintf (&fmt, "");
280         for (c = 1; c < argc; c++)
281                 if (strcmp ("-to", argv[c]) == 0)
282                         strcpy (argv[c], "-t");
284         while (1) {
285 #ifdef HAVE_GETOPT_H
286                 c =     getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:", long_options, &option_index);
287 #else
288                 c = getopt (argc, argv, "Vvht:c:w:p:s:u:C:a:");
289 #endif
290                 if (c == -1 || c == EOF)
291                         break;
293                 switch (c) {
294                 case '?':                                                                       /* help */
295                         print_usage ();
296                         exit (STATE_UNKNOWN);
297                 case 'h':                                                                       /* help */
298                         print_help ();
299                         exit (STATE_OK);
300                 case 'V':                                                                       /* version */
301                         print_revision (PROGNAME, REVISION);
302                         exit (STATE_OK);
303                 case 't':                                                                       /* timeout period */
304                         if (!is_integer (optarg)) {
305                                 printf ("%s: Timeout Interval must be an integer!\n\n",
306                                         my_basename (argv[0]));
307                                 print_usage ();
308                                 exit (STATE_UNKNOWN);
309                         }
310                         timeout_interval = atoi (optarg);
311                         break;
312                 case 'c':                                                                       /* critical threshold */
313                         if (is_integer (optarg)) {
314                                 cmax = atoi (optarg);
315                                 break;
316                         }
317                         else if (sscanf (optarg, ":%d", &cmax) == 1) {
318                                 break;
319                         }
320                         else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2) {
321                                 break;
322                         }
323                         else if (sscanf (optarg, "%d:", &cmin) == 1) {
324                                 break;
325                         }
326                         else {
327                                 printf ("%s: Critical Process Count must be an integer!\n\n",
328                                         my_basename (argv[0]));
329                                 print_usage ();
330                                 exit (STATE_UNKNOWN);
331                         }
332                 case 'w':                                                                       /* warning time threshold */
333                         if (is_integer (optarg)) {
334                                 wmax = atoi (optarg);
335                                 break;
336                         }
337                         else if (sscanf (optarg, ":%d", &wmax) == 1) {
338                                 break;
339                         }
340                         else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2) {
341                                 break;
342                         }
343                         else if (sscanf (optarg, "%d:", &wmin) == 1) {
344                                 break;
345                         }
346                         else {
347                                 printf ("%s: Warning Process Count must be an integer!\n\n",
348                                         my_basename (argv[0]));
349                                 print_usage ();
350                                 exit (STATE_UNKNOWN);
351                         }
352                 case 'p':                                                                       /* process id */
353                         if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
354                                 asprintf (&fmt, "%s%sPPID = %d", (options ? ", " : ""), ppid);
355                                 options |= PPID;
356                                 break;
357                         }
358                         printf ("%s: Parent Process ID must be an integer!\n\n",
359                                 my_basename (argv[0]));
360                         print_usage ();
361                         exit (STATE_UNKNOWN);
362                 case 's':                                                                       /* status */
363                         asprintf (&statopts, "%s", optarg);
364                         asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
365                         options |= STAT;
366                         break;
367                 case 'u':                                                                       /* user or user id */
368                         if (is_integer (optarg)) {
369                                 uid = atoi (optarg);
370                                 pw = getpwuid ((uid_t) uid);
371                                 /*  check to be sure user exists */
372                                 if (pw == NULL) {
373                                         printf ("UID %d was not found\n", uid);
374                                         print_usage ();
375                                         exit (STATE_UNKNOWN);
376                                 }
377                         }
378                         else {
379                                 pw = getpwnam (optarg);
380                                 /*  check to be sure user exists */
381                                 if (pw == NULL) {
382                                         printf ("User name %s was not found\n", optarg);
383                                         print_usage ();
384                                         exit (STATE_UNKNOWN);
385                                 }
386                                 /*  then get uid */
387                                 uid = pw->pw_uid;
388                         }
389                         user = pw->pw_name;
390                         asprintf (&fmt, "%s%sUID = %d (%s)", (options ? ", " : ""), fmt,
391                                   uid, user);
392                         options |= USER;
393                         break;
394                 case 'C':                                                                       /* command */
395                         asprintf (&prog, "%s", optarg);
396                         asprintf (&fmt, "%s%scommand name %s", fmt, (options ? ", " : ""),
397                                   prog);
398                         options |= PROG;
399                         break;
400                 case 'a':                                                                       /* args (full path name with args) */
401                         asprintf (&args, "%s", optarg);
402                         asprintf (&fmt, "%s%sargs %s", fmt, (options ? ", " : ""), args);
403                         options |= ARGS;
404                         break;
405                 case 'v':                                                                       /* command */
406                         verbose = TRUE;
407                         break;
408                 }
409         }
411         c = optind;
412         if (wmax == -1 && argv[c])
413                 wmax = atoi (argv[c++]);
414         if (cmax == -1 && argv[c])
415                 cmax = atoi (argv[c++]);
416         if (statopts == NULL && argv[c]) {
417                 asprintf (&statopts, "%s", argv[c++]);
418                 asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
419                 options |= STAT;
420         }
422         return validate_arguments ();
426 int
427 validate_arguments ()
430 if (wmax >= 0 && wmin == -1)
431                 wmin = 0;
432         if (cmax >= 0 && cmin == -1)
433                 cmin = 0;
434         if (wmax >= wmin && cmax >= cmin) {     /* standard ranges */
435                 if (wmax > cmax && cmax != -1) {
436                         printf ("wmax (%d) cannot be greater than cmax (%d)\n", wmax, cmax);
437                         return ERROR;
438                 }
439                 if (cmin > wmin && wmin != -1) {
440                         printf ("wmin (%d) cannot be less than cmin (%d)\n", wmin, cmin);
441                         return ERROR;
442                 }
443         }
445 /*      if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) { */
446 /*              printf ("At least one threshold must be set\n"); */
447 /*              return ERROR; */
448 /*      } */
450         if (options == 0) {
451                 options = 1;
452                 asprintf (&fmt, "%%s - %%d processes running\n");
453         }
454         else {
455                 asprintf (&fmt, "%%s - %%d processes running with %s\n", fmt);
456         }
458         return options;
462 void
463 print_help (void)
465         print_revision (PROGNAME, REVISION);
466         printf
467                 ("Copyright (c) %s %s <%s>\n\n%s\n",
468                  COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
469         print_usage ();
470         printf
471                 ("\nRequired Arguments:\n"
472                  " -w, --warning=RANGE\n"
473                  "    generate warning state if process count is outside this range\n"
474                  " -c, --critical=RANGE\n"
475                  "    generate critical state if process count is outside this range\n\n"
476                  "Optional Filters:\n"
477                  " -s, --state=STATUSFLAGS\n"
478                  "    Only scan for processes that have, in the output of `ps`, one or\n"
479                  "    more of the status flags you specify (for example R, Z, S, RS,\n"
480                  "    RSZDT, plus others based on the output of your 'ps' command).\n"
481                  " -p, --ppid=PPID\n"
482                  "    Only scan for children of the parent process ID indicated.\n"
483                  " -u, --user=USER\n"
484                  "    Only scan for proceses with user name or ID indicated.\n"
485                  " -a, --argument-array=STRING\n"
486                  "    Only scan for ARGS that match up to the length of the given STRING\n"
487                  " -C, --command=COMMAND\n"
488                  "    Only scan for exact matches to the named COMMAND.\n\n"
489                  "RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n"
490                  "specified 'max:min', a warning status will be generated if the\n"
492                  "count is inside the specified range\n");}
495 void
496 print_usage (void)
498         printf
499                 ("Usage:\n"
500                  " check_procs -w <range> -c <range> [-s state] [-p ppid] [-u user]\n"
501                  "             [-a argument-array] [-C command]\n"
502                  " check_procs --version\n" " check_procs --help\n");