check_procs.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  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. ******************************************************************************/
  36. #include "config.h"
  37. #include <pwd.h>
  38. #include "common.h"
  39. #include "popen.h"
  40. #include "utils.h"
  41. int process_arguments (int, char **);
  42. int call_getopt (int, char **);
  43. int validate_arguments (void);
  44. void print_usage (void);
  45. void print_help (char *);
  46. int wmax = -1;
  47. int cmax = -1;
  48. int wmin = -1;
  49. int cmin = -1;
  50. int options = 0;
  51. #define ALL 1
  52. #define STAT 2
  53. #define PPID 4
  54. #define USER 8
  55. #define PROG 16
  56. #define ARGS 32
  57. int verbose = FALSE;
  58. int uid;
  59. int ppid;
  60. char *statopts = NULL;
  61. char *prog = NULL;
  62. char *args = NULL;
  63. char *format = NULL;
  64. char tmp[MAX_INPUT_BUFFER];
  65. int
  66. main (int argc, char **argv)
  67. {
  68. char input_buffer[MAX_INPUT_BUFFER];
  69. int procuid = 0;
  70. int procppid = 0;
  71. char procstat[8];
  72. char procprog[MAX_INPUT_BUFFER];
  73. char *procargs;
  74. int resultsum = 0;
  75. int found = 0;
  76. int procs = 0;
  77. int pos;
  78. int result = STATE_UNKNOWN;
  79. procargs = malloc (MAX_INPUT_BUFFER);
  80. if (process_arguments (argc, argv) == ERROR)
  81. usage ("Unable to parse command line\n");
  82. /* run the command */
  83. if (verbose)
  84. printf ("%s\n", PS_COMMAND);
  85. child_process = spopen (PS_COMMAND);
  86. if (child_process == NULL) {
  87. printf ("Could not open pipe: %s\n", PS_COMMAND);
  88. return STATE_UNKNOWN;
  89. }
  90. child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
  91. if (child_stderr == NULL)
  92. printf ("Could not open stderr for %s\n", PS_COMMAND);
  93. fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
  94. while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
  95. if (
  96. #ifdef USE_PS_VARS
  97. sscanf (input_buffer, PS_FORMAT, PS_VARLIST) >= 4
  98. #else
  99. sscanf (input_buffer, PS_FORMAT, procstat, &procuid, &procppid, &pos,
  100. procprog) >= 4
  101. #endif
  102. ) {
  103. found++;
  104. resultsum = 0;
  105. procargs = strcpy (procargs, &input_buffer[pos]);
  106. strip (procargs);
  107. if ((options & STAT) && (strstr (statopts, procstat)))
  108. resultsum |= STAT;
  109. if ((options & ARGS) && (strstr (procargs, args) == procargs))
  110. resultsum |= ARGS;
  111. if ((options & PROG) && (strcmp (prog, procprog) == 0))
  112. resultsum |= PROG;
  113. if ((options & PPID) && (procppid == ppid))
  114. resultsum |= PPID;
  115. if ((options & USER) && (procuid == uid))
  116. resultsum |= USER;
  117. #ifdef DEBUG1
  118. if (procargs == NULL)
  119. printf ("%d %d %d %s %s\n", procs, procuid, procppid, procstat,
  120. procprog);
  121. else
  122. printf ("%d %d %d %s %s %s\n", procs, procuid, procppid, procstat,
  123. procprog, procargs);
  124. #endif
  125. if (options == resultsum)
  126. procs++;
  127. }
  128. }
  129. /* If we get anything on STDERR, at least set warning */
  130. while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
  131. if (verbose)
  132. printf ("%s", input_buffer);
  133. result = max (result, STATE_WARNING);
  134. }
  135. if (result > STATE_OK)
  136. printf ("System call sent warnings to stderr\n");
  137. (void) fclose (child_stderr);
  138. /* close the pipe */
  139. if (spclose (child_process)) {
  140. printf ("System call returned nonzero status\n");
  141. return max (result, STATE_WARNING);
  142. }
  143. if (options == ALL)
  144. procs = found;
  145. if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
  146. printf ("Unable to read output\n");
  147. return max (result, STATE_UNKNOWN);
  148. }
  149. if (verbose && (options & STAT))
  150. printf ("%s ", statopts);
  151. if (verbose && (options & PROG))
  152. printf ("%s ", prog);
  153. if (verbose && (options & PPID))
  154. printf ("%d ", ppid);
  155. if (verbose && (options & USER))
  156. printf ("%d ", uid);
  157. if (cmax >= 0 && cmin >= 0 && cmax < cmin) {
  158. if (procs > cmax && procs < cmin) {
  159. printf (format, "CRITICAL", procs);
  160. return STATE_CRITICAL;
  161. }
  162. }
  163. else if (cmax >= 0 && procs > cmax) {
  164. printf (format, "CRITICAL", procs);
  165. return STATE_CRITICAL;
  166. }
  167. else if (cmin >= 0 && procs < cmin) {
  168. printf (format, "CRITICAL", procs);
  169. return STATE_CRITICAL;
  170. }
  171. if (wmax >= 0 && wmin >= 0 && wmax < wmin) {
  172. if (procs > wmax && procs < wmin) {
  173. printf (format, "CRITICAL", procs);
  174. return STATE_CRITICAL;
  175. }
  176. }
  177. else if (wmax >= 0 && procs > wmax) {
  178. printf (format, "WARNING", procs);
  179. return max (result, STATE_WARNING);
  180. }
  181. else if (wmin >= 0 && procs < wmin) {
  182. printf (format, "WARNING", procs);
  183. return max (result, STATE_WARNING);
  184. }
  185. printf (format, "OK", procs);
  186. return max (result, STATE_OK);
  187. }
  188. /* process command-line arguments */
  189. int
  190. process_arguments (int argc, char **argv)
  191. {
  192. int c;
  193. if (argc < 2)
  194. return ERROR;
  195. for (c = 1; c < argc; c++)
  196. if (strcmp ("-to", argv[c]) == 0)
  197. strcpy (argv[c], "-t");
  198. c = 0;
  199. while (c += (call_getopt (argc - c, &argv[c]))) {
  200. if (argc <= c)
  201. break;
  202. if (wmax == -1)
  203. wmax = atoi (argv[c]);
  204. else if (cmax == -1)
  205. cmax = atoi (argv[c]);
  206. else if (statopts == NULL) {
  207. statopts = strscpy (statopts, argv[c]);
  208. format =
  209. strscat (format,
  210. ssprintf (NULL, "%sSTATE = %s", (options ? ", " : ""),
  211. statopts));
  212. options |= STAT;
  213. }
  214. }
  215. return validate_arguments ();
  216. }
  217. int
  218. call_getopt (int argc, char **argv)
  219. {
  220. int c, i = 1;
  221. char *user;
  222. struct passwd *pw;
  223. #ifdef HAVE_GETOPT_H
  224. int option_index = 0;
  225. static struct option long_options[] = {
  226. {"warning", required_argument, 0, 'w'},
  227. {"critical", required_argument, 0, 'c'},
  228. {"timeout", required_argument, 0, 't'},
  229. {"status", required_argument, 0, 's'},
  230. {"ppid", required_argument, 0, 'p'},
  231. {"command", required_argument, 0, 'C'},
  232. {"argument-array", required_argument, 0, 'a'},
  233. {"help", no_argument, 0, 'h'},
  234. {"version", no_argument, 0, 'V'},
  235. {"verbose", no_argument, 0, 'v'},
  236. {0, 0, 0, 0}
  237. };
  238. #endif
  239. while (1) {
  240. #ifdef HAVE_GETOPT_H
  241. c =
  242. getopt_long (argc, argv, "+Vvht:c:w:p:s:u:C:a:", long_options,
  243. &option_index);
  244. #else
  245. c = getopt (argc, argv, "+Vvht:c:w:p:s:u:C:a:");
  246. #endif
  247. if (c == EOF)
  248. break;
  249. i++;
  250. switch (c) {
  251. case 't':
  252. case 'c':
  253. case 'w':
  254. case 'p':
  255. case 's':
  256. case 'a':
  257. case 'u':
  258. case 'C':
  259. i++;
  260. }
  261. switch (c) {
  262. case '?': /* help */
  263. print_usage ();
  264. exit (STATE_UNKNOWN);
  265. case 'h': /* help */
  266. print_help (my_basename (argv[0]));
  267. exit (STATE_OK);
  268. case 'V': /* version */
  269. print_revision (my_basename (argv[0]), "$Revision$");
  270. exit (STATE_OK);
  271. case 't': /* timeout period */
  272. if (!is_integer (optarg)) {
  273. printf ("%s: Timeout Interval must be an integer!\n\n",
  274. my_basename (argv[0]));
  275. print_usage ();
  276. exit (STATE_UNKNOWN);
  277. }
  278. timeout_interval = atoi (optarg);
  279. break;
  280. case 'c': /* critical threshold */
  281. if (is_integer (optarg)) {
  282. cmax = atoi (optarg);
  283. break;
  284. }
  285. else if (sscanf (optarg, ":%d", &cmax) == 1) {
  286. break;
  287. }
  288. else if (sscanf (optarg, "%d:%d", &cmin, &cmax) == 2) {
  289. break;
  290. }
  291. else if (sscanf (optarg, "%d:", &cmin) == 1) {
  292. break;
  293. }
  294. else {
  295. printf ("%s: Critical Process Count must be an integer!\n\n",
  296. my_basename (argv[0]));
  297. print_usage ();
  298. exit (STATE_UNKNOWN);
  299. }
  300. case 'w': /* warning time threshold */
  301. if (is_integer (optarg)) {
  302. wmax = atoi (optarg);
  303. break;
  304. }
  305. else if (sscanf (optarg, ":%d", &wmax) == 1) {
  306. break;
  307. }
  308. else if (sscanf (optarg, "%d:%d", &wmin, &wmax) == 2) {
  309. break;
  310. }
  311. else if (sscanf (optarg, "%d:", &wmin) == 1) {
  312. break;
  313. }
  314. else {
  315. printf ("%s: Warning Process Count must be an integer!\n\n",
  316. my_basename (argv[0]));
  317. print_usage ();
  318. exit (STATE_UNKNOWN);
  319. }
  320. case 'p': /* process id */
  321. if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) {
  322. format =
  323. strscat (format,
  324. ssprintf (NULL, "%sPPID = %d", (options ? ", " : ""),
  325. ppid));
  326. options |= PPID;
  327. break;
  328. }
  329. printf ("%s: Parent Process ID must be an integer!\n\n",
  330. my_basename (argv[0]));
  331. print_usage ();
  332. exit (STATE_UNKNOWN);
  333. case 's': /* status */
  334. statopts = strscpy (statopts, optarg);
  335. format =
  336. strscat (format,
  337. ssprintf (NULL, "%sSTATE = %s", (options ? ", " : ""),
  338. statopts));
  339. options |= STAT;
  340. break;
  341. case 'u': /* user or user id */
  342. if (is_integer (optarg)) {
  343. uid = atoi (optarg);
  344. pw = getpwuid ((uid_t) uid);
  345. /* check to be sure user exists */
  346. if (pw == NULL) {
  347. printf ("UID %d was not found\n", uid);
  348. print_usage ();
  349. exit (STATE_UNKNOWN);
  350. }
  351. }
  352. else {
  353. pw = getpwnam (optarg);
  354. /* check to be sure user exists */
  355. if (pw == NULL) {
  356. printf ("User name %s was not found\n", optarg);
  357. print_usage ();
  358. exit (STATE_UNKNOWN);
  359. }
  360. /* then get uid */
  361. uid = pw->pw_uid;
  362. }
  363. user = pw->pw_name;
  364. format =
  365. strscat (format,
  366. ssprintf (NULL, "%sUID = %d (%s)", (options ? ", " : ""),
  367. uid, user));
  368. options |= USER;
  369. break;
  370. case 'C': /* command */
  371. prog = strscpy (prog, optarg);
  372. format =
  373. strscat (format,
  374. ssprintf (NULL, "%scommand name %s", (options ? ", " : ""),
  375. prog));
  376. options |= PROG;
  377. break;
  378. case 'a': /* args (full path name with args) */
  379. args = strscpy (args, optarg);
  380. format =
  381. strscat (format,
  382. ssprintf (NULL, "%sargs %s", (options ? ", " : ""), args));
  383. options |= ARGS;
  384. break;
  385. case 'v': /* command */
  386. verbose = TRUE;
  387. break;
  388. }
  389. }
  390. return i;
  391. }
  392. int
  393. validate_arguments ()
  394. {
  395. if (wmax >= 0 && wmin == -1)
  396. wmin = 0;
  397. if (cmax >= 0 && cmin == -1)
  398. cmin = 0;
  399. if (wmax >= wmin && cmax >= cmin) { /* standard ranges */
  400. if (wmax > cmax && cmax != -1) {
  401. printf ("wmax (%d) cannot be greater than cmax (%d)\n", wmax, cmax);
  402. return ERROR;
  403. }
  404. if (cmin > wmin && wmin != -1) {
  405. printf ("wmin (%d) cannot be less than cmin (%d)\n", wmin, cmin);
  406. return ERROR;
  407. }
  408. }
  409. if (wmax == -1 && cmax == -1 && wmin == -1 && cmin == -1) {
  410. printf ("At least one threshold must be set\n");
  411. return ERROR;
  412. }
  413. if (options == 0) {
  414. options = 1;
  415. format = ssprintf (format, "%%s - %%d processes running\n");
  416. }
  417. else {
  418. format =
  419. ssprintf (format, "%%s - %%d processes running with %s\n", format);
  420. }
  421. return options;
  422. }
  423. void
  424. print_help (char *cmd)
  425. {
  426. print_revision (cmd, "$Revision$");
  427. printf
  428. ("Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)\n\n"
  429. "This plugin checks the number of currently running processes and\n"
  430. "generates WARNING or CRITICAL states if the process count is outside\n"
  431. "the specified threshold ranges. The process count can be filtered by\n"
  432. "process owner, parent process PID, current state (e.g., 'Z'), or may\n"
  433. "be the total number of running processes\n\n");
  434. print_usage ();
  435. printf
  436. ("\nRequired Arguments:\n"
  437. " -w, --warning=RANGE\n"
  438. " generate warning state if process count is outside this range\n"
  439. " -c, --critical=RANGE\n"
  440. " generate critical state if process count is outside this range\n\n"
  441. "Optional Filters:\n"
  442. " -s, --state=STATUSFLAGS\n"
  443. " Only scan for processes that have, in the output of `ps`, one or\n"
  444. " more of the status flags you specify (for example R, Z, S, RS,\n"
  445. " RSZDT, plus others based on the output of your 'ps' command).\n"
  446. " -p, --ppid=PPID\n"
  447. " Only scan for children of the parent process ID indicated.\n"
  448. " -u, --user=USER\n"
  449. " Only scan for proceses with user name or ID indicated.\n"
  450. " -a, --argument-array=STRING\n"
  451. " Only scan for ARGS that match up to the length of the given STRING\n"
  452. " -C, --command=COMMAND\n"
  453. " Only scan for exact matches to the named COMMAND.\n\n"
  454. "RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n"
  455. "specified 'max:min', a warning status will be generated if the\n"
  456. "count is inside the specified range\n");}
  457. void
  458. print_usage (void)
  459. {
  460. printf
  461. ("Usage:\n"
  462. " check_procs -w <range> -c <range> [-s state] [-p ppid] [-u user]\n"
  463. " [-a argument-array] [-C command]\n"
  464. " check_procs --version\n" " check_procs --help\n");
  465. }