Code

Removal of ps_raw and ps_vars
[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                 cols = sscanf (input_buffer, PS_FORMAT, PS_VARLIST);
122                 /* Zombie processes do not give a procprog command */
123                 if ( cols == 3 && strstr(procstat, zombie) ) {
124                         strcpy(procprog, "");
125                         cols = 4;
126                 }
127                 if ( cols >= 4 ) {
128                         found++;
129                         resultsum = 0;
130                         asprintf (&procargs, "%s", input_buffer + pos);
131                         strip (procargs);
132                         if ((options & STAT) && (strstr (statopts, procstat)))
133                                 resultsum |= STAT;
134                         if ((options & ARGS) && procargs && (strstr (procargs, args) == procargs))
135                                 resultsum |= ARGS;
136                         if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0))
137                                 resultsum |= PROG;
138                         if ((options & PPID) && (procppid == ppid))
139                                 resultsum |= PPID;
140                         if ((options & USER) && (procuid == uid))
141                                 resultsum |= USER;
142 #ifdef DEBUG1
143                         if (procargs == NULL)
144                                 printf ("%d %d %d %s %s\n", procs, procuid, procppid, procstat,
145                                                                 procprog);
146                         else
147                                 printf ("%d %d %d %s %s %s\n", procs, procuid, procppid, procstat,
148                                                                 procprog, procargs);
149 #endif
150                         if (options == resultsum)
151                                 procs++;
152                 } 
153                 /* This should not happen */
154                 else if (verbose) {
155                         printf("Not parseable: %s", input_buffer);
156                 }
157         }
159         /* If we get anything on STDERR, at least set warning */
160         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
161                 if (verbose)
162                         printf ("STDERR: %s", input_buffer);
163                 result = max_state (result, STATE_WARNING);
164                 printf ("System call sent warnings to stderr\n");
165         }
166         
167         (void) fclose (child_stderr);
169         /* close the pipe */
170         if (spclose (child_process)) {
171                 printf ("System call returned nonzero status\n");
172                 result = max_state (result, STATE_WARNING);
173         }
175         if (options == ALL)
176                 procs = found;
178         if (found == 0) {                                                       /* no process lines parsed so return STATE_UNKNOWN */
179                 printf ("Unable to read output\n");
181                 return result;
182         }
184         if (verbose && (options & STAT))
185                 printf ("%s ", statopts);
186         if (verbose && (options & PROG))
187                 printf ("%s ", prog);
188         if (verbose && (options & PPID))
189                 printf ("%d ", ppid);
190         if (verbose && (options & USER))
191                 printf ("%d ", uid);
193         if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
194                 if (result == STATE_UNKNOWN)
195                         result = STATE_OK;
196                 printf (fmt, "OK", procs);
197                 return result;
198         }
199         else if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
200                 if (procs > cmax && procs < cmin) {
201                         printf (fmt, "CRITICAL", procs);
202                         return STATE_CRITICAL;
203                 }
204         }
205         else if (cmax >= 0 && procs > cmax) {
206                 printf (fmt, "CRITICAL", procs);
207                 return STATE_CRITICAL;
208         }
209         else if (cmin >= 0 && procs < cmin) {
210                 printf (fmt, "CRITICAL", procs);
211                 return STATE_CRITICAL;
212         }
214         if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
215                 if (procs > wmax && procs < wmin) {
216                         printf (fmt, "CRITICAL", procs);
217                         return STATE_CRITICAL;
218                 }
219         }
220         else if (wmax >= 0 && procs > wmax) {
221                 printf (fmt, "WARNING", procs);
222                 return max_state (result, STATE_WARNING);
223         }
224         else if (wmin >= 0 && procs < wmin) {
225                 printf (fmt, "WARNING", procs);
226                 return max_state (result, STATE_WARNING);
227         }
229         printf (fmt, "OK", procs);
230         if ( result == STATE_UNKNOWN ) {
231                 result = STATE_OK;
232         }
233         return result;
236 /* process command-line arguments */
237 int
238 process_arguments (int argc, char **argv)
240         int c = 1;
241         char *user;
242         struct passwd *pw;
243         int option_index = 0;
244         static struct option long_options[] = {
245                 {"warning", required_argument, 0, 'w'},
246                 {"critical", required_argument, 0, 'c'},
247                 {"timeout", required_argument, 0, 't'},
248                 {"status", required_argument, 0, 's'},
249                 {"ppid", required_argument, 0, 'p'},
250                 {"command", required_argument, 0, 'C'},
251                 {"argument-array", required_argument, 0, 'a'},
252                 {"help", no_argument, 0, 'h'},
253                 {"version", no_argument, 0, 'V'},
254                 {"verbose", no_argument, 0, 'v'},
255                 {0, 0, 0, 0}
256         };
258         for (c = 1; c < argc; c++)
259                 if (strcmp ("-to", argv[c]) == 0)
260                         strcpy (argv[c], "-t");
262         while (1) {
263                 c = getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:", long_options, &option_index);
265                 if (c == -1 || c == EOF)
266                         break;
268                 switch (c) {
269                 case '?':                                                                       /* help */
270                         print_usage ();
271                         exit (STATE_UNKNOWN);
272                 case 'h':                                                                       /* help */
273                         print_help ();
274                         exit (STATE_OK);
275                 case 'V':                                                                       /* version */
276                         print_revision (progname, REVISION);
277                         exit (STATE_OK);
278                 case 't':                                                                       /* timeout period */
279                         if (!is_integer (optarg)) {
280                                 printf ("%s: Timeout Interval must be an integer!\n\n",
281                                         progname);
282                                 print_usage ();
283                                 exit (STATE_UNKNOWN);
284                         }
285                         timeout_interval = atoi (optarg);
286                         break;
287                 case 'c':                                                                       /* critical threshold */
288                         if (is_integer (optarg)) {
289                                 cmax = atoi (optarg);
290                                 break;
291                         }
292                         else if (sscanf (optarg, ":%d", &cmax) == 1) {
293                                 break;
294                         }
295                         else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2) {
296                                 break;
297                         }
298                         else if (sscanf (optarg, "%d:", &cmin) == 1) {
299                                 break;
300                         }
301                         else {
302                                 printf ("%s: Critical Process Count must be an integer!\n\n",
303                                         progname);
304                                 print_usage ();
305                                 exit (STATE_UNKNOWN);
306                         }
307                 case 'w':                                                                       /* warning time threshold */
308                         if (is_integer (optarg)) {
309                                 wmax = atoi (optarg);
310                                 break;
311                         }
312                         else if (sscanf (optarg, ":%d", &wmax) == 1) {
313                                 break;
314                         }
315                         else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2) {
316                                 break;
317                         }
318                         else if (sscanf (optarg, "%d:", &wmin) == 1) {
319                                 break;
320                         }
321                         else {
322                                 printf ("%s: Warning Process Count must be an integer!\n\n",
323                                         progname);
324                                 print_usage ();
325                                 exit (STATE_UNKNOWN);
326                         }
327                 case 'p':                                                                       /* process id */
328                         if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
329                                 asprintf (&fmt, "%s%sPPID = %d", fmt, (options ? ", " : ""), ppid);
330                                 options |= PPID;
331                                 break;
332                         }
333                         printf ("%s: Parent Process ID must be an integer!\n\n",
334                                 progname);
335                         print_usage ();
336                         exit (STATE_UNKNOWN);
337                 case 's':                                                                       /* status */
338                         asprintf (&statopts, "%s", optarg);
339                         asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
340                         options |= STAT;
341                         break;
342                 case 'u':                                                                       /* user or user id */
343                         if (is_integer (optarg)) {
344                                 uid = atoi (optarg);
345                                 pw = getpwuid ((uid_t) uid);
346                                 /*  check to be sure user exists */
347                                 if (pw == NULL) {
348                                         printf ("UID %d was not found\n", uid);
349                                         print_usage ();
350                                         exit (STATE_UNKNOWN);
351                                 }
352                         }
353                         else {
354                                 pw = getpwnam (optarg);
355                                 /*  check to be sure user exists */
356                                 if (pw == NULL) {
357                                         printf ("User name %s was not found\n", optarg);
358                                         print_usage ();
359                                         exit (STATE_UNKNOWN);
360                                 }
361                                 /*  then get uid */
362                                 uid = pw->pw_uid;
363                         }
364                         user = pw->pw_name;
365                         asprintf (&fmt, "%s%sUID = %d (%s)", fmt, (options ? ", " : ""),
366                                   uid, user);
367                         options |= USER;
368                         break;
369                 case 'C':                                                                       /* command */
370                         asprintf (&prog, "%s", optarg);
371                         asprintf (&fmt, "%s%scommand name %s", fmt, (options ? ", " : ""),
372                                   prog);
373                         options |= PROG;
374                         break;
375                 case 'a':                                                                       /* args (full path name with args) */
376                         asprintf (&args, "%s", optarg);
377                         asprintf (&fmt, "%s%sargs %s", fmt, (options ? ", " : ""), args);
378                         options |= ARGS;
379                         break;
380                 case 'v':                                                                       /* command */
381                         verbose = TRUE;
382                         break;
383                 }
384         }
386         c = optind;
387         if (wmax == -1 && argv[c])
388                 wmax = atoi (argv[c++]);
389         if (cmax == -1 && argv[c])
390                 cmax = atoi (argv[c++]);
391         if (statopts == NULL && argv[c]) {
392                 asprintf (&statopts, "%s", argv[c++]);
393                 asprintf (&fmt, "%s%sSTATE = %s", fmt, (options ? ", " : ""), statopts);
394                 options |= STAT;
395         }
397         return validate_arguments ();
401 int
402 validate_arguments ()
405 if (wmax >= 0 && wmin == -1)
406                 wmin = 0;
407         if (cmax >= 0 && cmin == -1)
408                 cmin = 0;
409         if (wmax >= wmin && cmax >= cmin) {     /* standard ranges */
410                 if (wmax > cmax && cmax != -1) {
411                         printf ("wmax (%d) cannot be greater than cmax (%d)\n", wmax, cmax);
412                         return ERROR;
413                 }
414                 if (cmin > wmin && wmin != -1) {
415                         printf ("wmin (%d) cannot be less than cmin (%d)\n", wmin, cmin);
416                         return ERROR;
417                 }
418         }
420 /*      if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) { */
421 /*              printf ("At least one threshold must be set\n"); */
422 /*              return ERROR; */
423 /*      } */
425         if (options == 0) {
426                 options = 1;
427                 asprintf (&fmt, "%%s - %%d processes running\n");
428         }
429         else {
430                 asprintf (&fmt, "%%s - %%d processes running with %s\n", fmt);
431         }
433         return options;
437 void
438 print_help (void)
440         print_revision (progname, REVISION);
441         printf
442                 ("Copyright (c) %s %s <%s>\n\n%s\n",
443                  COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
444         print_usage ();
445         printf
446                 ("\nRequired Arguments:\n"
447                  " -w, --warning=RANGE\n"
448                  "    generate warning state if process count is outside this range\n"
449                  " -c, --critical=RANGE\n"
450                  "    generate critical state if process count is outside this range\n\n"
451                  "Optional Filters:\n"
452                  " -s, --state=STATUSFLAGS\n"
453                  "    Only scan for processes that have, in the output of `ps`, one or\n"
454                  "    more of the status flags you specify (for example R, Z, S, RS,\n"
455                  "    RSZDT, plus others based on the output of your 'ps' command).\n"
456                  " -p, --ppid=PPID\n"
457                  "    Only scan for children of the parent process ID indicated.\n"
458                  " -u, --user=USER\n"
459                  "    Only scan for proceses with user name or ID indicated.\n"
460                  " -a, --argument-array=STRING\n"
461                  "    Only scan for ARGS that match up to the length of the given STRING\n"
462                  " -C, --command=COMMAND\n"
463                  "    Only scan for exact matches to the named COMMAND.\n\n"
464                  "RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n"
465                  "specified 'max:min', a warning status will be generated if the\n"
467                  "count is inside the specified range\n");}
470 void
471 print_usage (void)
473         printf
474                 ("Usage:\n"
475                  " check_procs -w <range> -c <range> [-s state] [-p ppid] [-u user]\n"
476                  "             [-a argument-array] [-C command]\n"
477                  " check_procs --version\n" " check_procs --help\n");