Code

--disable-nls throws an error on check_dns, check_procs and
[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  $Id$
18  
19 ******************************************************************************/
21 const char *progname = "check_procs";
22 const char *revision = "$Revision$";
23 const char *copyright = "2000-2003";
24 const char *email = "nagiosplug-devel@lists.sourceforge.net";
26 #include "common.h"
27 #include "popen.h"
28 #include "utils.h"
29 #include <pwd.h>
31 int process_arguments (int, char **);
32 int validate_arguments (void);
33 int check_thresholds (int);
34 void print_help (void);
35 void print_usage (void);
37 int wmax = -1;
38 int cmax = -1;
39 int wmin = -1;
40 int cmin = -1;
42 int options = 0; /* bitmask of filter criteria to test against */
43 #define ALL 1
44 #define STAT 2
45 #define PPID 4
46 #define USER 8
47 #define PROG 16
48 #define ARGS 32
49 #define VSZ  64
50 #define RSS  128
51 #define PCPU 256
53 /* Different metrics */
54 char *metric_name;
55 enum metric {
56         METRIC_PROCS,
57         METRIC_VSZ,
58         METRIC_RSS,
59         METRIC_CPU
60 };
61 enum metric metric = METRIC_PROCS;
63 int verbose = 0;
64 int uid;
65 int ppid;
66 int vsz;
67 int rss;
68 float pcpu;
69 char *statopts;
70 char *prog;
71 char *args;
72 char *fmt;
73 char *fails;
74 char tmp[MAX_INPUT_BUFFER];
78 int
79 main (int argc, char **argv)
80 {
81         char *input_buffer;
82         char *input_line;
83         char *procprog;
85         int procuid = 0;
86         int procppid = 0;
87         int procvsz = 0;
88         int procrss = 0;
89         float procpcpu = 0;
90         char procstat[8];
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         input_buffer = malloc (MAX_INPUT_BUFFER);
113         procprog = malloc (MAX_INPUT_BUFFER);
115         asprintf (&metric_name, "PROCS");
116         metric = METRIC_PROCS;
118         if (process_arguments (argc, argv) != TRUE)
119                 usage4 (_("Could not parse arguments"));
121         /* Set signal handling and alarm timeout */
122         if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
123                 usage4 (_("Cannot catch SIGALRM"));
124         }
125         alarm (timeout_interval);
127         if (verbose >= 2)
128                 printf (_("CMD: %s\n"), PS_COMMAND);
130         child_process = spopen (PS_COMMAND);
131         if (child_process == NULL) {
132                 printf (_("Could not open pipe: %s\n"), PS_COMMAND);
133                 return STATE_UNKNOWN;
134         }
136         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
137         if (child_stderr == NULL)
138                 printf (_("Could not open stderr for %s\n"), PS_COMMAND);
140         /* flush first line */
141         fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
142         while ( input_buffer[strlen(input_buffer)-1] != '\n' )
143                 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
145         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
146                 asprintf (&input_line, "%s", input_buffer);
147                 while ( input_buffer[strlen(input_buffer)-1] != '\n' ) {
148                         fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
149                         asprintf (&input_line, "%s%s", input_line, input_buffer);
150                 }
152                 if (verbose >= 3)
153                         printf ("%s", input_line);
155                 strcpy (procprog, "");
156                 asprintf (&procargs, "%s", "");
158                 cols = sscanf (input_line, PS_FORMAT, PS_VARLIST);
160                 /* Zombie processes do not give a procprog command */
161                 if ( cols == (expected_cols - 1) && strstr(procstat, zombie) ) {
162                         cols = expected_cols;
163                 }
164                 if ( cols >= expected_cols ) {
165                         resultsum = 0;
166                         asprintf (&procargs, "%s", input_line + pos);
167                         strip (procargs);
169                         /* Some ps return full pathname for command. This removes path */
170                         temp_string = strtok ((char *)procprog, "/");
171                         while (temp_string) {
172                                 strcpy(procprog, temp_string);
173                                 temp_string = strtok (NULL, "/");
174                         }
176                         if (verbose >= 3)
177                                 printf ("%d %d %d %d %d %.2f %s %s %s\n", 
178                                         procs, procuid, procvsz, procrss,
179                                         procppid, procpcpu, procstat, procprog, procargs);
181                         /* Ignore self */
182                         if (strcmp (procprog, progname) == 0) {
183                                 continue;
184                         }
186                         if ((options & STAT) && (strstr (statopts, procstat)))
187                                 resultsum |= STAT;
188                         if ((options & ARGS) && procargs && (strstr (procargs, args) != NULL))
189                                 resultsum |= ARGS;
190                         if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0))
191                                 resultsum |= PROG;
192                         if ((options & PPID) && (procppid == ppid))
193                                 resultsum |= PPID;
194                         if ((options & USER) && (procuid == uid))
195                                 resultsum |= USER;
196                         if ((options & VSZ)  && (procvsz >= vsz))
197                                 resultsum |= VSZ;
198                         if ((options & RSS)  && (procrss >= rss))
199                                 resultsum |= RSS;
200                         if ((options & PCPU)  && (procpcpu >= pcpu))
201                                 resultsum |= PCPU;
203                         found++;
205                         /* Next line if filters not matched */
206                         if (!(options == resultsum || options == ALL))
207                                 continue;
209                         procs++;
211                         if (metric == METRIC_VSZ)
212                                 i = check_thresholds (procvsz);
213                         else if (metric == METRIC_RSS)
214                                 i = check_thresholds (procrss);
215                         /* TODO? float thresholds for --metric=CPU */
216                         else if (metric == METRIC_CPU)
217                                 i = check_thresholds ((int)procpcpu); 
219                         if (metric != METRIC_PROCS) {
220                                 if (i == STATE_WARNING) {
221                                         warn++;
222                                         asprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog);
223                                         result = max_state (result, i);
224                                 }
225                                 if (i == STATE_CRITICAL) {
226                                         crit++;
227                                         asprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog);
228                                         result = max_state (result, i);
229                                 }
230                         }
231                 } 
232                 /* This should not happen */
233                 else if (verbose) {
234                         printf(_("Not parseable: %s"), input_buffer);
235                 }
236         }
238         /* If we get anything on STDERR, at least set warning */
239         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
240                 if (verbose)
241                         printf (_("STDERR: %s"), input_buffer);
242                 result = max_state (result, STATE_WARNING);
243                 printf (_("System call sent warnings to stderr\n"));
244         }
245         
246         (void) fclose (child_stderr);
248         /* close the pipe */
249         if (spclose (child_process)) {
250                 printf (_("System call returned nonzero status\n"));
251                 result = max_state (result, STATE_WARNING);
252         }
254         if (found == 0) {                                                       /* no process lines parsed so return STATE_UNKNOWN */
255                 printf (_("Unable to read output\n"));
256                 return result;
257         }
259         if ( result == STATE_UNKNOWN ) 
260                 result = STATE_OK;
262         /* Needed if procs found, but none match filter */
263         if ( metric == METRIC_PROCS ) {
264                 result = max_state (result, check_thresholds (procs) );
265         }
267         if ( result == STATE_OK ) {
268                 printf ("%s %s: ", metric_name, _("OK"));
269         } else if (result == STATE_WARNING) {
270                 printf ("%s %s: ", metric_name, _("WARNING"));
271                 if ( metric != METRIC_PROCS ) {
272                         printf (_("%d warn out of "), warn);
273                 }
274         } else if (result == STATE_CRITICAL) {
275                 printf ("%s %s: ", metric_name, _("CRITICAL"));
276                 if (metric != METRIC_PROCS) {
277                         printf (_("%d crit, %d warn out of "), crit, warn);
278                 }
279         } 
280         printf ("%d %s", procs, procs == 1 ? _("process") : _("processes"));
281         
282         if (strcmp(fmt,"") != 0) {
283                 printf (_(" with %s"), fmt);
284         }
286         if ( verbose >= 1 && strcmp(fails,"") )
287                 printf (" [%s]", fails);
289         printf ("\n");
290         return result;
295 /* process command-line arguments */
296 int
297 process_arguments (int argc, char **argv)
299         int c = 1;
300         char *user;
301         struct passwd *pw;
302         int option = 0;
303         static struct option longopts[] = {
304                 {"warning", required_argument, 0, 'w'},
305                 {"critical", required_argument, 0, 'c'},
306                 {"metric", required_argument, 0, 'm'},
307                 {"timeout", required_argument, 0, 't'},
308                 {"status", required_argument, 0, 's'},
309                 {"ppid", required_argument, 0, 'p'},
310                 {"command", required_argument, 0, 'C'},
311                 {"vsz", required_argument, 0, 'z'},
312                 {"rss", required_argument, 0, 'r'},
313                 {"pcpu", required_argument, 0, 'P'},
314                 {"argument-array", required_argument, 0, 'a'},
315                 {"help", no_argument, 0, 'h'},
316                 {"version", no_argument, 0, 'V'},
317                 {"verbose", no_argument, 0, 'v'},
318                 {0, 0, 0, 0}
319         };
321         for (c = 1; c < argc; c++)
322                 if (strcmp ("-to", argv[c]) == 0)
323                         strcpy (argv[c], "-t");
325         while (1) {
326                 c = getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:z:r:m:P:", 
327                         longopts, &option);
329                 if (c == -1 || c == EOF)
330                         break;
332                 switch (c) {
333                 case '?':                                                                       /* help */
334                         printf (_("%s: Unknown argument: %s\n\n"), progname, optarg);
335                         print_usage ();
336                         exit (STATE_UNKNOWN);
337                 case 'h':                                                                       /* help */
338                         print_help ();
339                         exit (STATE_OK);
340                 case 'V':                                                                       /* version */
341                         print_revision (progname, revision);
342                         exit (STATE_OK);
343                 case 't':                                                                       /* timeout period */
344                         if (!is_integer (optarg))
345                                 usage2 (_("Timeout interval must be a positive integer"), optarg);
346                         else
347                                 timeout_interval = atoi (optarg);
348                         break;
349                 case 'c':                                                                       /* critical threshold */
350                         if (is_integer (optarg))
351                                 cmax = atoi (optarg);
352                         else if (sscanf (optarg, ":%d", &cmax) == 1)
353                                 break;
354                         else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2)
355                                 break;
356                         else if (sscanf (optarg, "%d:", &cmin) == 1)
357                                 break;
358                         else
359                                 usage (_("Critical Process Count must be an integer!\n\n"));
360                         break;                                                   
361                 case 'w':                                                                       /* warning threshold */
362                         if (is_integer (optarg))
363                                 wmax = atoi (optarg);
364                         else if (sscanf (optarg, ":%d", &wmax) == 1)
365                                 break;
366                         else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2)
367                                 break;
368                         else if (sscanf (optarg, "%d:", &wmin) == 1)
369                                 break;
370                         else
371                                 usage (_("Warning Process Count must be an integer!\n\n"));
372                         break;
373                 case 'p':                                                                       /* process id */
374                         if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
375                                 asprintf (&fmt, "%s%sPPID = %d", (fmt ? fmt : "") , (options ? ", " : ""), ppid);
376                                 options |= PPID;
377                                 break;
378                         }
379                         usage2 (_("%s: Parent Process ID must be an integer!\n\n"),  progname);
380                 case 's':                                                                       /* status */
381                         if (statopts)
382                                 break;
383                         else
384                                 statopts = optarg;
385                         asprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts);
386                         options |= STAT;
387                         break;
388                 case 'u':                                                                       /* user or user id */
389                         if (is_integer (optarg)) {
390                                 uid = atoi (optarg);
391                                 pw = getpwuid ((uid_t) uid);
392                                 /*  check to be sure user exists */
393                                 if (pw == NULL)
394                                         usage2 (_("UID %s was not found\n"), optarg);
395                         }
396                         else {
397                                 pw = getpwnam (optarg);
398                                 /*  check to be sure user exists */
399                                 if (pw == NULL)
400                                         usage2 (_("User name %s was not found\n"), optarg);
401                                 /*  then get uid */
402                                 uid = pw->pw_uid;
403                         }
404                         user = pw->pw_name;
405                         asprintf (&fmt, _("%s%sUID = %d (%s)"), (fmt ? fmt : ""), (options ? ", " : ""),
406                                   uid, user);
407                         options |= USER;
408                         break;
409                 case 'C':                                                                       /* command */
410                         if (prog)
411                                 break;
412                         else
413                                 prog = optarg;
414                         asprintf (&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""),
415                                   prog);
416                         options |= PROG;
417                         break;
418                 case 'a':                                                                       /* args (full path name with args) */
419                         if (args)
420                                 break;
421                         else
422                                 args = optarg;
423                         asprintf (&fmt, _("%s%sargs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), args);
424                         options |= ARGS;
425                         break;
426                 case 'r':                                       /* RSS */
427                         if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) {
428                                 asprintf (&fmt, _("%s%sRSS >= %d"), (fmt ? fmt : ""), (options ? ", " : ""), rss);
429                                 options |= RSS;
430                                 break;
431                         }
432                         usage2 (_("%s: RSS must be an integer!\n\n"), progname);
433                 case 'z':                                       /* VSZ */
434                         if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) {
435                                 asprintf (&fmt, _("%s%sVSZ >= %d"), (fmt ? fmt : ""), (options ? ", " : ""), vsz);
436                                 options |= VSZ;
437                                 break;
438                         }
439                         usage2 (_("%s: VSZ must be an integer!\n\n"), progname);
440                 case 'P':                                       /* PCPU */
441                         /* TODO: -P 1.5.5 is accepted */
442                         if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) {
443                                 asprintf (&fmt, _("%s%sPCPU >= %.2f"), (fmt ? fmt : ""), (options ? ", " : ""), pcpu);
444                                 options |= PCPU;
445                                 break;
446                         }
447                         usage2 (_("%s: PCPU must be a float!\n\n"), progname);
448                 case 'm':
449                         asprintf (&metric_name, "%s", optarg);
450                         if ( strcmp(optarg, "PROCS") == 0) {
451                                 metric = METRIC_PROCS;
452                                 break;
453                         } 
454                         else if ( strcmp(optarg, "VSZ") == 0) {
455                                 metric = METRIC_VSZ;
456                                 break;
457                         } 
458                         else if ( strcmp(optarg, "RSS") == 0 ) {
459                                 metric = METRIC_RSS;
460                                 break;
461                         }
462                         else if ( strcmp(optarg, "CPU") == 0 ) {
463                                 metric = METRIC_CPU;
464                                 break;
465                         }
466                         printf (_("%s: metric must be one of PROCS, VSZ, RSS, CPU!\n\n"),
467                                 progname);
468                         print_usage ();
469                         exit (STATE_UNKNOWN);
470                 case 'v':                                                                       /* command */
471                         verbose++;
472                         break;
473                 }
474         }
476         c = optind;
477         if (wmax == -1 && argv[c])
478                 wmax = atoi (argv[c++]);
479         if (cmax == -1 && argv[c])
480                 cmax = atoi (argv[c++]);
481         if (statopts == NULL && argv[c]) {
482                 asprintf (&statopts, "%s", argv[c++]);
483                 asprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts);
484                 options |= STAT;
485         }
487         return validate_arguments ();
492 int
493 validate_arguments ()
496         if (wmax >= 0 && wmin == -1)
497                 wmin = 0;
498         if (cmax >= 0 && cmin == -1)
499                 cmin = 0;
500         if (wmax >= wmin && cmax >= cmin) {     /* standard ranges */
501                 if (wmax > cmax && cmax != -1) {
502                         printf (_("wmax (%d) cannot be greater than cmax (%d)\n"), wmax, cmax);
503                         return ERROR;
504                 }
505                 if (cmin > wmin && wmin != -1) {
506                         printf (_("wmin (%d) cannot be less than cmin (%d)\n"), wmin, cmin);
507                         return ERROR;
508                 }
509         }
511 /*      if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) { */
512 /*              printf ("At least one threshold must be set\n"); */
513 /*              return ERROR; */
514 /*      } */
516         if (options == 0)
517                 options = ALL;
519         if (statopts==NULL)
520                 statopts = strdup("");
522         if (prog==NULL)
523                 prog = strdup("");
525         if (args==NULL)
526                 args = strdup("");
528         if (fmt==NULL)
529                 fmt = strdup("");
531         if (fails==NULL)
532                 fails = strdup("");
534         return options;
539 /* Check thresholds against value */
540 int
541 check_thresholds (int value)
543         if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
544                 return OK;
545         }
546         else if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
547                 if (value > cmax && value < cmin)
548                         return STATE_CRITICAL;
549         }
550         else if (cmax >= 0 && value > cmax) {
551                 return STATE_CRITICAL;
552         }
553         else if (cmin >= 0 && value < cmin) {
554                 return STATE_CRITICAL;
555         }
557         if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
558                 if (value > wmax && value < wmin) {
559                         return STATE_WARNING;
560                 }
561         }
562         else if (wmax >= 0 && value > wmax) {
563                 return STATE_WARNING;
564         }
565         else if (wmin >= 0 && value < wmin) {
566                 return STATE_WARNING;
567         }
568         return STATE_OK;
573 void
574 print_help (void)
576         print_revision (progname, revision);
578         printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>");
579         printf (COPYRIGHT, copyright, email);
581         printf(_("\
582 Checks all processes and generates WARNING or CRITICAL states if the specified\n\
583 metric is outside the required threshold ranges. The metric defaults to number\n\
584 of processes.  Search filters can be applied to limit the processes to check.\n\n"));
586         print_usage ();
588         printf(_("\n\
589 Required Arguments:\n\
590  -w, --warning=RANGE\n\
591    Generate warning state if metric is outside this range\n\
592  -c, --critical=RANGE\n\
593    Generate critical state if metric is outside this range\n"));
595         printf(_("\n\
596 Optional Arguments:\n\
597  -m, --metric=TYPE\n\
598    Check thresholds against metric. Valid types:\n\
599    PROCS - number of processes (default)\n\
600    VSZ  - virtual memory size\n\
601    RSS  - resident set memory size\n\
602    CPU  - percentage cpu\n"));
604         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
606         printf(_("\
607  -v, --verbose\n\
608    Extra information. Up to 3 verbosity levels\n"));
610         printf(_("\n\
611 Optional Filters:\n\
612  -s, --state=STATUSFLAGS\n\
613    Only scan for processes that have, in the output of `ps`, one or\n\
614    more of the status flags you specify (for example R, Z, S, RS,\n\
615    RSZDT, plus others based on the output of your 'ps' command).\n\
616  -p, --ppid=PPID\n\
617    Only scan for children of the parent process ID indicated.\n\
618  -z, --vsz=VSZ\n\
619    Only scan for processes with vsz higher than indicated.\n\
620  -r, --rss=RSS\n\
621    Only scan for processes with rss higher than indicated.\n"));
623         printf(_("\
624  -P, --pcpu=PCPU\n\
625    Only scan for processes with pcpu higher than indicated.\n\
626  -u, --user=USER\n\
627    Only scan for processes with user name or ID indicated.\n\
628  -a, --argument-array=STRING\n\
629    Only scan for processes with args that contain STRING.\n\
630  -C, --command=COMMAND\n\
631    Only scan for exact matches of COMMAND (without path).\n"));
633         printf(_("\n\
634 RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\
635 specified 'max:min', a warning status will be generated if the\n\
636 count is inside the specified range\n\n"));
638         printf(_("\
639 This plugin checks the number of currently running processes and\n\
640 generates WARNING or CRITICAL states if the process count is outside\n\
641 the specified threshold ranges. The process count can be filtered by\n\
642 process owner, parent process PID, current state (e.g., 'Z'), or may\n\
643 be the total number of running processes\n\n"));
645         printf(_("\
646 Examples:\n\
647  check_procs -w 2:2 -c 2:1024 -C portsentry\n\
648    Warning if not two processes with command name portsentry. Critical\n\
649    if < 2 or > 1024 processes\n\n\
650  check_procs -w 10 -a '/usr/local/bin/perl' -u root\n\
651    Warning alert if > 10 processes with command arguments containing \n\
652    '/usr/local/bin/perl' and owned by root\n\n\
653  check_procs -w 50000 -c 100000 --metric=VSZ\n\
654    Alert if vsz of any processes over 50K or 100K\n\
655  check_procs -w 10 -c 20 --metric=CPU\n\
656    Alert if cpu of any processes over 10% or 20%\n\n"));
658         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] [-t timeout] [-v]\n", progname); 
670         printf (_(UT_HLP_VRS), progname, progname);