Code

- bindtextdomain for gettext, a few other smale cleanups here and there
[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 ******************************************************************************/
19 const char *progname = "check_procs";
20 const char *revision = "$Revision$";
21 const char *copyright = "2000-2003";
22 const char *email = "nagiosplug-devel@lists.sourceforge.net";
24 #include "common.h"
25 #include "popen.h"
26 #include "utils.h"
27 #include <pwd.h>
29 int process_arguments (int, char **);
30 int validate_arguments (void);
31 int check_thresholds (int);
32 void print_help (void);
33 void print_usage (void);
35 int wmax = -1;
36 int cmax = -1;
37 int wmin = -1;
38 int cmin = -1;
40 int options = 0; /* bitmask of filter criteria to test against */
41 #define ALL 1
42 #define STAT 2
43 #define PPID 4
44 #define USER 8
45 #define PROG 16
46 #define ARGS 32
47 #define VSZ  64
48 #define RSS  128
49 #define PCPU 256
51 /* Different metrics */
52 char *metric_name;
53 enum metric {
54         METRIC_PROCS,
55         METRIC_VSZ,
56         METRIC_RSS,
57         METRIC_CPU
58 };
59 enum metric metric = METRIC_PROCS;
61 int verbose = 0;
62 int uid;
63 int ppid;
64 int vsz;
65 int rss;
66 float pcpu;
67 char *statopts;
68 char *prog;
69 char *args;
70 char *fmt;
71 char *fails;
72 char tmp[MAX_INPUT_BUFFER];
78 \f
79 int
80 main (int argc, char **argv)
81 {
82         char input_buffer[MAX_INPUT_BUFFER];
84         int procuid = 0;
85         int procppid = 0;
86         int procvsz = 0;
87         int procrss = 0;
88         float procpcpu = 0;
89         char procstat[8];
90         char procprog[MAX_INPUT_BUFFER];
91         char *procargs;
93         const char *zombie = "Z";
95         int resultsum = 0; /* bitmask of the filter criteria met by a process */
96         int found = 0; /* counter for number of lines returned in `ps` output */
97         int procs = 0; /* counter for number of processes meeting filter criteria */
98         int pos; /* number of spaces before 'args' in `ps` output */
99         int cols; /* number of columns in ps output */
100         int warn = 0; /* number of processes in warn state */
101         int crit = 0; /* number of processes in crit state */
102         int i = 0;
104         int result = STATE_UNKNOWN;
106         setlocale (LC_ALL, "");
107         bindtextdomain (PACKAGE, LOCALEDIR);
108         textdomain (PACKAGE);
110         asprintf (&metric_name, "PROCS");
111         metric = METRIC_PROCS;
113         if (process_arguments (argc, argv) == ERROR)
114                 usage (_("Unable to parse command line\n"));
116         if (verbose >= 2)
117                 printf (_("CMD: %s\n"), PS_COMMAND);
119         child_process = spopen (PS_COMMAND);
120         if (child_process == NULL) {
121                 printf (_("Could not open pipe: %s\n"), PS_COMMAND);
122                 return STATE_UNKNOWN;
123         }
125         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
126         if (child_stderr == NULL)
127                 printf (_("Could not open stderr for %s\n"), PS_COMMAND);
129         fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
131         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
132                 strcpy (procprog, "");
133                 asprintf (&procargs, "%s", "");
135                 cols = sscanf (input_buffer, PS_FORMAT, PS_VARLIST);
137                 /* Zombie processes do not give a procprog command */
138                 if ( cols == 6 && strstr(procstat, zombie) ) {
139                         cols = 7;
140                         /* Set some value for procargs for the strip command further below 
141                         Seen to be a problem on some Solaris 7 and 8 systems */
142                         input_buffer[pos] = '\n';
143                         input_buffer[pos+1] = 0x0;
144                 }
145                 if ( cols >= 7 ) {
146                         resultsum = 0;
147                         asprintf (&procargs, "%s", input_buffer + pos);
148                         strip (procargs);
150                         if ((options & STAT) && (strstr (statopts, procstat)))
151                                 resultsum |= STAT;
152                         if ((options & ARGS) && procargs && (strstr (procargs, args) != NULL))
153                                 resultsum |= ARGS;
154                         if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0))
155                                 resultsum |= PROG;
156                         if ((options & PPID) && (procppid == ppid))
157                                 resultsum |= PPID;
158                         if ((options & USER) && (procuid == uid))
159                                 resultsum |= USER;
160                         if ((options & VSZ)  && (procvsz >= vsz))
161                                 resultsum |= VSZ;
162                         if ((options & RSS)  && (procrss >= rss))
163                                 resultsum |= RSS;
164                         if ((options & PCPU)  && (procpcpu >= pcpu))
165                                 resultsum |= PCPU;
167                         if (verbose >= 3)
168                                 printf ("%d %d %d %d %d %.2f %s %s %s\n", 
169                                         procs, procuid, procvsz, procrss,
170                                         procppid, procpcpu, procstat, procprog, procargs);
172                         /* Ignore self */
173                         if (strcmp (procprog, progname) == 0)
174                                 continue;
176                         found++;
178                         /* Next line if filters not matched */
179                         if (!(options == resultsum || options == ALL))
180                                 continue;
182                         procs++;
184                         if (metric == METRIC_VSZ)
185                                 i = check_thresholds (procvsz);
186                         else if (metric == METRIC_RSS)
187                                 i = check_thresholds (procrss);
188                         /* TODO? float thresholds for --metric=CPU */
189                         else if (metric == METRIC_CPU)
190                                 i = check_thresholds ((int)procpcpu); 
192                         if (metric != METRIC_PROCS) {
193                                 if (i == STATE_WARNING) {
194                                         warn++;
195                                         asprintf (&fails, "%s%s%s", fails, (fails == "" ? "" : ", "), procprog);
196                                 }
197                                 if (i == STATE_CRITICAL) {
198                                         crit++;
199                                         asprintf (&fails, "%s%s%s", fails, (fails == "" ? "" : ", "), procprog);
200                                 }
201                                 result = max_state (result, i);
202                         }
203                 } 
204                 /* This should not happen */
205                 else if (verbose) {
206                         printf(_("Not parseable: %s"), input_buffer);
207                 }
208         }
210         /* If we get anything on STDERR, at least set warning */
211         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
212                 if (verbose)
213                         printf (_("STDERR: %s"), input_buffer);
214                 result = max_state (result, STATE_WARNING);
215                 printf (_("System call sent warnings to stderr\n"));
216         }
217         
218         (void) fclose (child_stderr);
220         /* close the pipe */
221         if (spclose (child_process)) {
222                 printf (_("System call returned nonzero status\n"));
223                 result = max_state (result, STATE_WARNING);
224         }
226         if (found == 0) {                                                       /* no process lines parsed so return STATE_UNKNOWN */
227                 printf (_("Unable to read output\n"));
228                 return result;
229         }
231         if ( result == STATE_UNKNOWN ) 
232                 result = STATE_OK;
234         /* Needed if procs found, but none match filter */
235         if ( metric == METRIC_PROCS ) {
236                 result = max_state (result, check_thresholds (procs) );
237         }
239         if ( result == STATE_OK ) {
240                 printf (_("%s OK: %d process%s"), 
241                         metric_name, procs, ( procs != 1 ? "es" : "") );
242         } else if (result == STATE_WARNING) {
243                 if ( metric == METRIC_PROCS ) {
244                         printf (_("PROCS WARNING: %d process%s"), procs, 
245                                 ( procs != 1 ? "es" : ""));
246                 } else {
247                         printf (_("%s WARNING: %d warn out of %d process%s"), 
248                                 metric_name, warn, procs, 
249                                 ( procs != 1 ? "es" : ""));
250                 }
251         } else if (result == STATE_CRITICAL) {
252                 if (metric == METRIC_PROCS) {
253                         printf (_("PROCS CRITICAL: %d process%s"), procs, 
254                                 ( procs != 1 ? "es" : ""));
255                 } else {
256                         printf (_("%s CRITICAL: %d crit, %d warn out of %d process%s"), 
257                                 metric_name, crit, warn, procs, 
258                                 ( procs != 1 ? "es" : ""));
259                 }
260         } 
261         
262         if (fmt != "") {
263                 printf (" with %s", fmt);
264         }
266         if ( verbose >= 1 && fails != "" )
267                 printf (" [%s]", fails);
269         printf ("\n");
270         return result;
277 \f
278 /* process command-line arguments */
279 int
280 process_arguments (int argc, char **argv)
282         int c = 1;
283         char *user;
284         struct passwd *pw;
285         int option = 0;
286         static struct option longopts[] = {
287                 {"warning", required_argument, 0, 'w'},
288                 {"critical", required_argument, 0, 'c'},
289                 {"metric", required_argument, 0, 'm'},
290                 {"timeout", required_argument, 0, 't'},
291                 {"status", required_argument, 0, 's'},
292                 {"ppid", required_argument, 0, 'p'},
293                 {"command", required_argument, 0, 'C'},
294                 {"vsz", required_argument, 0, 'z'},
295                 {"rss", required_argument, 0, 'r'},
296                 {"pcpu", required_argument, 0, 'P'},
297                 {"argument-array", required_argument, 0, 'a'},
298                 {"help", no_argument, 0, 'h'},
299                 {"version", no_argument, 0, 'V'},
300                 {"verbose", no_argument, 0, 'v'},
301                 {0, 0, 0, 0}
302         };
304         for (c = 1; c < argc; c++)
305                 if (strcmp ("-to", argv[c]) == 0)
306                         strcpy (argv[c], "-t");
308         while (1) {
309                 c = getopt_long (argc, argv, "Vvht:c:w:p:s:u:C:a:z:r:m:P:", 
310                         longopts, &option);
312                 if (c == -1 || c == EOF)
313                         break;
315                 switch (c) {
316                 case '?':                                                                       /* help */
317                         print_usage ();
318                         exit (STATE_UNKNOWN);
319                 case 'h':                                                                       /* help */
320                         print_help ();
321                         exit (STATE_OK);
322                 case 'V':                                                                       /* version */
323                         print_revision (progname, revision);
324                         exit (STATE_OK);
325                 case 't':                                                                       /* timeout period */
326                         if (!is_integer (optarg))
327                                 usage (_("Timeout Interval must be an integer!\n\n"));
328                         else
329                                 timeout_interval = atoi (optarg);
330                         break;
331                 case 'c':                                                                       /* critical threshold */
332                         if (is_integer (optarg))
333                                 cmax = atoi (optarg);
334                         else if (sscanf (optarg, ":%d", &cmax) == 1)
335                                 break;
336                         else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2)
337                                 break;
338                         else if (sscanf (optarg, "%d:", &cmin) == 1)
339                                 break;
340                         else
341                                 usage (_("Critical Process Count must be an integer!\n\n"));
342                         break;                                                   
343                 case 'w':                                                                       /* warning time threshold */
344                         if (is_integer (optarg))
345                                 wmax = atoi (optarg);
346                         else if (sscanf (optarg, ":%d", &wmax) == 1)
347                                 break;
348                         else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2)
349                                 break;
350                         else if (sscanf (optarg, "%d:", &wmin) == 1)
351                                 break;
352                         else
353                                 usage (_("%s: Warning Process Count must be an integer!\n\n"));
354                         break;
355                 case 'p':                                                                       /* process id */
356                         if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
357                                 asprintf (&fmt, "%s%sPPID = %d", fmt, (options ? ", " : ""), ppid);
358                                 options |= PPID;
359                                 break;
360                         }
361                         printf (_("%s: Parent Process ID must be an integer!\n\n"),
362                                 progname);
363                         print_usage ();
364                         exit (STATE_UNKNOWN);
365                 case 's':                                                                       /* status */
366                         if (statopts)
367                                 break;
368                         else
369                                 statopts = optarg;
370                         asprintf (&fmt, _("%s%sSTATE = %s"), fmt, (options ? ", " : ""), statopts);
371                         options |= STAT;
372                         break;
373                 case 'u':                                                                       /* user or user id */
374                         if (is_integer (optarg)) {
375                                 uid = atoi (optarg);
376                                 pw = getpwuid ((uid_t) uid);
377                                 /*  check to be sure user exists */
378                                 if (pw == NULL) {
379                                         printf (_("UID %d was not found\n"), uid);
380                                         print_usage ();
381                                         exit (STATE_UNKNOWN);
382                                 }
383                         }
384                         else {
385                                 pw = getpwnam (optarg);
386                                 /*  check to be sure user exists */
387                                 if (pw == NULL) {
388                                         printf (_("User name %s was not found\n"), optarg);
389                                         print_usage ();
390                                         exit (STATE_UNKNOWN);
391                                 }
392                                 /*  then get uid */
393                                 uid = pw->pw_uid;
394                         }
395                         user = pw->pw_name;
396                         asprintf (&fmt, _("%s%sUID = %d (%s)"), fmt, (options ? ", " : ""),
397                                   uid, user);
398                         options |= USER;
399                         break;
400                 case 'C':                                                                       /* command */
401                         if (prog)
402                                 break;
403                         else
404                                 prog = optarg;
405                         asprintf (&fmt, _("%s%scommand name '%s'"), fmt, (options ? ", " : ""),
406                                   prog);
407                         options |= PROG;
408                         break;
409                 case 'a':                                                                       /* args (full path name with args) */
410                         if (args)
411                                 break;
412                         else
413                                 args = optarg;
414                         asprintf (&fmt, _("%s%sargs '%s'"), fmt, (options ? ", " : ""), args);
415                         options |= ARGS;
416                         break;
417                 case 'r':                                       /* RSS */
418                         if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) {
419                                 asprintf (&fmt, _("%s%sRSS >= %d"), fmt, (options ? ", " : ""), rss);
420                                 options |= RSS;
421                                 break;
422                         }
423                         printf (_("%s: RSS must be an integer!\n\n"),
424                                 progname);
425                         print_usage ();
426                         exit (STATE_UNKNOWN);
427                 case 'z':                                       /* VSZ */
428                         if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) {
429                                 asprintf (&fmt, _("%s%sVSZ >= %d"), fmt, (options ? ", " : ""), vsz);
430                                 options |= VSZ;
431                                 break;
432                         }
433                         printf (_("%s: VSZ must be an integer!\n\n"),
434                                 progname);
435                         print_usage ();
436                         exit (STATE_UNKNOWN);
437                 case 'P':                                       /* PCPU */
438                         /* TODO: -P 1.5.5 is accepted */
439                         if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) {
440                                 asprintf (&fmt, _("%s%sPCPU >= %.2f"), fmt, (options ? ", " : ""), pcpu);
441                                 options |= PCPU;
442                                 break;
443                         }
444                         printf (_("%s: PCPU must be a float!\n\n"),
445                                 progname);
446                         print_usage ();
447                         exit (STATE_UNKNOWN);
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, (options ? ", " : ""), statopts);
484                 options |= STAT;
485         }
487         return validate_arguments ();
493 int
494 validate_arguments ()
497         if (wmax >= 0 && wmin == -1)
498                 wmin = 0;
499         if (cmax >= 0 && cmin == -1)
500                 cmin = 0;
501         if (wmax >= wmin && cmax >= cmin) {     /* standard ranges */
502                 if (wmax > cmax && cmax != -1) {
503                         printf (_("wmax (%d) cannot be greater than cmax (%d)\n"), wmax, cmax);
504                         return ERROR;
505                 }
506                 if (cmin > wmin && wmin != -1) {
507                         printf (_("wmin (%d) cannot be less than cmin (%d)\n"), wmin, cmin);
508                         return ERROR;
509                 }
510         }
512 /*      if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) { */
513 /*              printf ("At least one threshold must be set\n"); */
514 /*              return ERROR; */
515 /*      } */
517         if (options == 0)
518                 options = ALL;
520         if (statopts==NULL)
521                 statopts = strdup("");
523         if (prog==NULL)
524                 prog = strdup("");
526         if (args==NULL)
527                 args = strdup("");
529         if (fmt==NULL)
530                 fmt = strdup("");
532         if (fails==NULL)
533                 fails = strdup("");
535         return options;
542 \f
543 /* Check thresholds against value */
544 int
545 check_thresholds (int value)
547         if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
548                 return OK;
549         }
550         else if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
551                 if (value > cmax && value < cmin)
552                         return STATE_CRITICAL;
553         }
554         else if (cmax >= 0 && value > cmax) {
555                 return STATE_CRITICAL;
556         }
557         else if (cmin >= 0 && value < cmin) {
558                 return STATE_CRITICAL;
559         }
561         if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
562                 if (value > wmax && value < wmin) {
563                         return STATE_WARNING;
564                 }
565         }
566         else if (wmax >= 0 && value > wmax) {
567                 return STATE_WARNING;
568         }
569         else if (wmin >= 0 && value < wmin) {
570                 return STATE_WARNING;
571         }
572         return STATE_OK;
579 \f
580 void
581 print_help (void)
583         print_revision (progname, revision);
585         printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>"));
586         printf (_(COPYRIGHT), copyright, email);
588         printf(_("\
589 Checks all processes and generates WARNING or CRITICAL states if the specified\n\
590 metric is outside the required threshold ranges. The metric defaults to number\n\
591 of processes.  Search filters can be applied to limit the processes to check.\n\n"));
593         print_usage ();
595         printf(_("\n\
596 Required Arguments:\n\
597  -w, --warning=RANGE\n\
598    Generate warning state if metric is outside this range\n\
599  -c, --critical=RANGE\n\
600    Generate critical state if metric is outside this range\n"));
602         printf(_("\n\
603 Optional Arguments:\n\
604  -m, --metric=TYPE\n\
605    Check thresholds against metric. Valid types:\n\
606    PROCS - number of processes (default)\n\
607    VSZ  - virtual memory size\n\
608    RSS  - resident set memory size\n\
609    CPU  - percentage cpu\n\
610  -v, --verbose\n\
611    Extra information. Up to 3 verbosity levels\n"));
613         printf(_("\n\
614 Optional Filters:\n\
615  -s, --state=STATUSFLAGS\n\
616    Only scan for processes that have, in the output of `ps`, one or\n\
617    more of the status flags you specify (for example R, Z, S, RS,\n\
618    RSZDT, plus others based on the output of your 'ps' command).\n\
619  -p, --ppid=PPID\n\
620    Only scan for children of the parent process ID indicated.\n\
621  -z, --vsz=VSZ\n\
622    Only scan for processes with vsz higher than indicated.\n\
623  -r, --rss=RSS\n\
624    Only scan for processes with rss higher than indicated.\n"));
626         printf(_("\
627  -P, --pcpu=PCPU\n\
628    Only scan for processes with pcpu higher than indicated.\n\
629  -u, --user=USER\n\
630    Only scan for processes with user name or ID indicated.\n\
631  -a, --argument-array=STRING\n\
632    Only scan for processes with args that contain STRING.\n\
633  -C, --command=COMMAND\n\
634    Only scan for exact matches to the named COMMAND.\n"));
636         printf(_("\n\
637 RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\
638 specified 'max:min', a warning status will be generated if the\n\
639 count is inside the specified range\n\n"));
641         printf(_("\
642 This plugin checks the number of currently running processes and\n\
643 generates WARNING or CRITICAL states if the process count is outside\n\
644 the specified threshold ranges. The process count can be filtered by\n\
645 process owner, parent process PID, current state (e.g., 'Z'), or may\n\
646 be the total number of running processes\n\n"));
648         printf(_("\
649 Examples:\n\
650  check_procs -w 2:2 -c 2:1024 -C portsentry\n\
651    Warning if not two processes with command name portsentry. Critical\n\
652    if < 2 or > 1024 processes\n\n\
653  check_procs -w 10 -a '/usr/local/bin/perl' -u root\n\
654    Warning alert if > 10 processes with command arguments containing \n\
655    '/usr/local/bin/perl' and owned by root\n\n\
656  check_procs -w 50000 -c 100000 --metric=VSZ\n\
657    Alert if vsz of any processes over 50K or 100K\n\n"));
659         printf (_(UT_SUPPORT));
662 void
663 print_usage (void)
665         printf ("\
666 Usage: %s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n\
667   [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n\
668   [-C command] [-v]\n", progname);      
669         printf (_(UT_HLP_VRS), progname, progname);