[monitoring-plugins] check_by_ssh: fix child process leak on timeouts

Sven Nierlein git at monitoring-plugins.org
Tue Feb 19 21:50:15 CET 2019


 Module: monitoring-plugins
 Branch: master
 Commit: 7cafb0e84550035fe671662c293122be975065ca
 Author: Sven Nierlein <sven at nierlein.de>
   Date: Fri Feb 15 10:36:28 2019 +0100
    URL: https://www.monitoring-plugins.org/repositories/monitoring-plugins/commit/?id=7cafb0e

check_by_ssh: fix child process leak on timeouts

When check_by_ssh runs into a timeout it simply exits keeping all child processes running.
Simply adopting the kill loop from runcmd_timeout_alarm_handler() fixes this.

Signed-off-by: Sven Nierlein <sven at nierlein.de>

---

 lib/utils_base.c      | 19 +++++++++++++++++++
 lib/utils_base.h      |  5 +++++
 lib/utils_cmd.c       | 42 +++++++++++++++++-------------------------
 lib/utils_cmd.h       | 13 +++++++++++++
 plugins/check_dbi.c   |  1 +
 plugins/check_pgsql.c |  1 +
 plugins/common.h      | 14 ++++++++++++++
 plugins/popen.c       | 29 -----------------------------
 plugins/runcmd.c      | 13 -------------
 plugins/utils.c       | 31 -------------------------------
 plugins/utils.h       |  9 ---------
 11 files changed, 70 insertions(+), 107 deletions(-)

diff --git a/lib/utils_base.c b/lib/utils_base.c
index 19a531f..fd7058d 100644
--- a/lib/utils_base.c
+++ b/lib/utils_base.c
@@ -37,6 +37,9 @@
 
 monitoring_plugin *this_monitoring_plugin=NULL;
 
+unsigned int timeout_state = STATE_CRITICAL;
+unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT;
+
 int _np_state_read_file(FILE *);
 
 void np_init( char *plugin_name, int argc, char **argv ) {
@@ -359,6 +362,22 @@ char *np_extract_value(const char *varlist, const char *name, char sep) {
 	return value;
 }
 
+const char *
+state_text (int result)
+{
+	switch (result) {
+	case STATE_OK:
+		return "OK";
+	case STATE_WARNING:
+		return "WARNING";
+	case STATE_CRITICAL:
+		return "CRITICAL";
+	case STATE_DEPENDENT:
+		return "DEPENDENT";
+	default:
+		return "UNKNOWN";
+	}
+}
 
 /*
  * Read a string representing a state (ok, warning... or numeric: 0, 1) and
diff --git a/lib/utils_base.h b/lib/utils_base.h
index 42ae0c0..d7e7dff 100644
--- a/lib/utils_base.h
+++ b/lib/utils_base.h
@@ -61,6 +61,10 @@ void print_thresholds(const char *, thresholds *);
 int check_range(double, range *);
 int get_status(double, thresholds *);
 
+/* Handle timeouts */
+extern unsigned int timeout_state;
+extern unsigned int timeout_interval;
+
 /* All possible characters in a threshold range */
 #define NP_THRESHOLDS_CHARS "-0123456789.:@~"
 
@@ -107,5 +111,6 @@ void np_state_write_string(time_t, char *);
 void np_init(char *, int argc, char **argv);
 void np_set_args(int argc, char **argv);
 void np_cleanup();
+const char *state_text (int);
 
 #endif /* _UTILS_BASE_ */
diff --git a/lib/utils_cmd.c b/lib/utils_cmd.c
index 7eb9a3a..795840d 100644
--- a/lib/utils_cmd.c
+++ b/lib/utils_cmd.c
@@ -40,6 +40,7 @@
 
 /** includes **/
 #include "common.h"
+#include "utils.h"
 #include "utils_cmd.h"
 #include "utils_base.h"
 #include <fcntl.h>
@@ -65,31 +66,6 @@ extern char **environ;
 # define SIG_ERR ((Sigfunc *)-1)
 #endif
 
-/* This variable must be global, since there's no way the caller
- * can forcibly slay a dead or ungainly running program otherwise.
- * Multithreading apps and plugins can initialize it (via CMD_INIT)
- * in an async safe manner PRIOR to calling cmd_run() or cmd_run_array()
- * for the first time.
- *
- * The check for initialized values is atomic and can
- * occur in any number of threads simultaneously. */
-static pid_t *_cmd_pids = NULL;
-
-/* Try sysconf(_SC_OPEN_MAX) first, as it can be higher than OPEN_MAX.
- * If that fails and the macro isn't defined, we fall back to an educated
- * guess. There's no guarantee that our guess is adequate and the program
- * will die with SIGSEGV if it isn't and the upper boundary is breached. */
-#define DEFAULT_MAXFD  256   /* fallback value if no max open files value is set */
-#define MAXFD_LIMIT   8192   /* upper limit of open files */
-#ifdef _SC_OPEN_MAX
-static long maxfd = 0;
-#elif defined(OPEN_MAX)
-# define maxfd OPEN_MAX
-#else	/* sysconf macro unavailable, so guess (may be wildly inaccurate) */
-# define maxfd DEFAULT_MAXFD
-#endif
-
-
 /** prototypes **/
 static int _cmd_open (char *const *, int *, int *)
 	__attribute__ ((__nonnull__ (1, 2, 3)));
@@ -406,3 +382,19 @@ cmd_file_read ( char *filename, output *out, int flags)
 
 	return 0;
 }
+
+void
+timeout_alarm_handler (int signo)
+{
+	size_t i;
+	if (signo == SIGALRM) {
+		printf (_("%s - Plugin timed out after %d seconds\n"),
+						state_text(timeout_state), timeout_interval);
+
+		if(_cmd_pids) for(i = 0; i < maxfd; i++) {
+			if(_cmd_pids[i] != 0) kill(_cmd_pids[i], SIGKILL);
+		}
+
+		exit (timeout_state);
+	}
+}
diff --git a/lib/utils_cmd.h b/lib/utils_cmd.h
index ebaf15b..6f3aeb8 100644
--- a/lib/utils_cmd.h
+++ b/lib/utils_cmd.h
@@ -32,4 +32,17 @@ void cmd_init (void);
 #define CMD_NO_ARRAYS 0x01   /* don't populate arrays at all */
 #define CMD_NO_ASSOC 0x02    /* output.line won't point to buf */
 
+/* This variable must be global, since there's no way the caller
+ * can forcibly slay a dead or ungainly running program otherwise.
+ * Multithreading apps and plugins can initialize it (via CMD_INIT)
+ * in an async safe manner PRIOR to calling cmd_run() or cmd_run_array()
+ * for the first time.
+ *
+ * The check for initialized values is atomic and can
+ * occur in any number of threads simultaneously. */
+static pid_t *_cmd_pids = NULL;
+
+RETSIGTYPE timeout_alarm_handler (int);
+
+
 #endif /* _UTILS_CMD_ */
diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c
index 826eb8d..ced13d0 100644
--- a/plugins/check_dbi.c
+++ b/plugins/check_dbi.c
@@ -35,6 +35,7 @@ const char *email = "devel at monitoring-plugins.org";
 
 #include "common.h"
 #include "utils.h"
+#include "utils_cmd.h"
 
 #include "netutils.h"
 
diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c
index 5cd4709..11ce691 100644
--- a/plugins/check_pgsql.c
+++ b/plugins/check_pgsql.c
@@ -34,6 +34,7 @@ const char *email = "devel at monitoring-plugins.org";
 
 #include "common.h"
 #include "utils.h"
+#include "utils_cmd.h"
 
 #include "netutils.h"
 #include <libpq-fe.h>
diff --git a/plugins/common.h b/plugins/common.h
index 6bf4fca..0f08e2f 100644
--- a/plugins/common.h
+++ b/plugins/common.h
@@ -225,4 +225,18 @@ enum {
 # define __attribute__(x) /* do nothing */
 #endif
 
+/* Try sysconf(_SC_OPEN_MAX) first, as it can be higher than OPEN_MAX.
+ * If that fails and the macro isn't defined, we fall back to an educated
+ * guess. There's no guarantee that our guess is adequate and the program
+ * will die with SIGSEGV if it isn't and the upper boundary is breached. */
+#define DEFAULT_MAXFD  256   /* fallback value if no max open files value is set */
+#define MAXFD_LIMIT   8192   /* upper limit of open files */
+#ifdef _SC_OPEN_MAX
+static long maxfd = 0;
+#elif defined(OPEN_MAX)
+# define maxfd OPEN_MAX
+#else	/* sysconf macro unavailable, so guess (may be wildly inaccurate) */
+# define maxfd DEFAULT_MAXFD
+#endif
+
 #endif /* _COMMON_H_ */
diff --git a/plugins/popen.c b/plugins/popen.c
index 592263f..116d168 100644
--- a/plugins/popen.c
+++ b/plugins/popen.c
@@ -78,7 +78,6 @@ RETSIGTYPE popen_timeout_alarm_handler (int);
 
 #define	min(a,b)	((a) < (b) ? (a) : (b))
 #define	max(a,b)	((a) > (b) ? (a) : (b))
-int open_max (void);						/* {Prog openmax} */
 static void err_sys (const char *, ...) __attribute__((noreturn,format(printf, 1, 2)));
 char *rtrim (char *, const char *);
 
@@ -86,7 +85,6 @@ char *pname = NULL;							/* caller can set this from argv[0] */
 
 /*int *childerr = NULL;*//* ptr to array allocated at run-time */
 /*extern pid_t *childpid = NULL; *//* ptr to array allocated at run-time */
-static int maxfd;								/* from our open_max(), {Prog openmax} */
 
 #ifdef REDHAT_SPOPEN_ERROR
 static volatile int childtermd = 0;
@@ -187,13 +185,11 @@ spopen (const char *cmdstring)
 	argv[i] = NULL;
 
 	if (childpid == NULL) {				/* first time through */
-		maxfd = open_max ();				/* allocate zeroed out array for child pids */
 		if ((childpid = calloc ((size_t)maxfd, sizeof (pid_t))) == NULL)
 			return (NULL);
 	}
 
 	if (child_stderr_array == NULL) {	/* first time through */
-		maxfd = open_max ();				/* allocate zeroed out array for child pids */
 		if ((child_stderr_array = calloc ((size_t)maxfd, sizeof (int))) == NULL)
 			return (NULL);
 	}
@@ -273,15 +269,6 @@ spclose (FILE * fp)
 	return (1);
 }
 
-#ifdef	OPEN_MAX
-static int openmax = OPEN_MAX;
-#else
-static int openmax = 0;
-#endif
-
-#define	OPEN_MAX_GUESS	256			/* if OPEN_MAX is indeterminate */
-				/* no guarantee this is adequate */
-
 #ifdef REDHAT_SPOPEN_ERROR
 RETSIGTYPE
 popen_sigchld_handler (int signo)
@@ -311,22 +298,6 @@ popen_timeout_alarm_handler (int signo)
 }
 
 
-int
-open_max (void)
-{
-	if (openmax == 0) {						/* first time through */
-		errno = 0;
-		if ((openmax = sysconf (_SC_OPEN_MAX)) < 0) {
-			if (errno == 0)
-				openmax = OPEN_MAX_GUESS;	/* it's indeterminate */
-			else
-				err_sys (_("sysconf error for _SC_OPEN_MAX"));
-		}
-	}
-	return (openmax);
-}
-
-
 /* Fatal error related to a system call.
  * Print a message and die. */
 
diff --git a/plugins/runcmd.c b/plugins/runcmd.c
index 1a7c904..c382867 100644
--- a/plugins/runcmd.c
+++ b/plugins/runcmd.c
@@ -67,19 +67,6 @@
  * occur in any number of threads simultaneously. */
 static pid_t *np_pids = NULL;
 
-/* Try sysconf(_SC_OPEN_MAX) first, as it can be higher than OPEN_MAX.
- * If that fails and the macro isn't defined, we fall back to an educated
- * guess. There's no guarantee that our guess is adequate and the program
- * will die with SIGSEGV if it isn't and the upper boundary is breached. */
-#ifdef _SC_OPEN_MAX
-static long maxfd = 0;
-#elif defined(OPEN_MAX)
-# define maxfd OPEN_MAX
-#else /* sysconf macro unavailable, so guess (may be wildly inaccurate) */
-# define maxfd 256
-#endif
-
-
 /** prototypes **/
 static int np_runcmd_open(const char *, int *, int *)
 	__attribute__((__nonnull__(1, 2, 3)));
diff --git a/plugins/utils.c b/plugins/utils.c
index 231af92..ee62013 100644
--- a/plugins/utils.c
+++ b/plugins/utils.c
@@ -36,9 +36,6 @@ extern const char *progname;
 #define STRLEN 64
 #define TXTBLK 128
 
-unsigned int timeout_state = STATE_CRITICAL;
-unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT;
-
 time_t start_time, end_time;
 
 /* **************************************************************************
@@ -148,33 +145,6 @@ print_revision (const char *command_name, const char *revision)
 	         command_name, revision, PACKAGE, VERSION);
 }
 
-const char *
-state_text (int result)
-{
-	switch (result) {
-	case STATE_OK:
-		return "OK";
-	case STATE_WARNING:
-		return "WARNING";
-	case STATE_CRITICAL:
-		return "CRITICAL";
-	case STATE_DEPENDENT:
-		return "DEPENDENT";
-	default:
-		return "UNKNOWN";
-	}
-}
-
-void
-timeout_alarm_handler (int signo)
-{
-	if (signo == SIGALRM) {
-		printf (_("%s - Plugin timed out after %d seconds\n"),
-						state_text(timeout_state), timeout_interval);
-		exit (timeout_state);
-	}
-}
-
 int
 is_numeric (char *number)
 {
@@ -708,4 +678,3 @@ char *sperfdata_int (const char *label,
 
 	return data;
 }
-
diff --git a/plugins/utils.h b/plugins/utils.h
index a436e1c..6aa316f 100644
--- a/plugins/utils.h
+++ b/plugins/utils.h
@@ -29,13 +29,6 @@ suite of plugins. */
 void support (void);
 void print_revision (const char *, const char *);
 
-/* Handle timeouts */
-
-extern unsigned int timeout_state;
-extern unsigned int timeout_interval;
-
-RETSIGTYPE timeout_alarm_handler (int);
-
 extern time_t start_time, end_time;
 
 /* Test input types */
@@ -89,8 +82,6 @@ void usage4(const char *) __attribute__((noreturn));
 void usage5(void) __attribute__((noreturn));
 void usage_va(const char *fmt, ...) __attribute__((noreturn));
 
-const char *state_text (int);
-
 #define max(a,b) (((a)>(b))?(a):(b))
 #define min(a,b) (((a)<(b))?(a):(b))
 



More information about the Commits mailing list