[icinga-checkins] icinga.org: icinga2/master: Implemented environment-based macros.

git at icinga.org git at icinga.org
Sun Feb 10 01:36:14 CET 2013


Module: icinga2
Branch: master
Commit: ee37e0cace686733ad14e1e2acf39726947dc11d
URL:    https://git.icinga.org/?p=icinga2.git;a=commit;h=ee37e0cace686733ad14e1e2acf39726947dc11d

Author: Gunnar Beutner <gunnar.beutner at netways.de>
Date:   Sat Feb  9 23:02:33 2013 +0100

Implemented environment-based macros.

---

 configure.ac                          |    2 +
 lib/base/convert.cpp                  |    4 +
 lib/base/convert.h                    |    1 +
 lib/base/dictionary.cpp               |    1 -
 lib/base/process.cpp                  |  144 ++++++++++++++++++++++++++-------
 lib/base/process.h                    |   15 +++-
 lib/icinga/macroprocessor.cpp         |   18 ++++
 lib/icinga/macroprocessor.h           |    1 +
 lib/icinga/pluginchecktask.cpp        |    2 +-
 lib/icinga/pluginnotificationtask.cpp |    2 +-
 10 files changed, 153 insertions(+), 37 deletions(-)

diff --git a/configure.ac b/configure.ac
index d39cf8a..0f9cec0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -32,6 +32,8 @@ AC_LANG_CPLUSPLUS
 AC_PROG_CC
 AC_LANG_C
 
+AC_FUNC_VFORK
+
 LT_INIT([dlopen disable-static win32-dll])
 LT_CONFIG_LTDL_DIR([third-party/ltdl])
 LTDL_INIT
diff --git a/lib/base/convert.cpp b/lib/base/convert.cpp
index d3bcd1e..3a35326 100644
--- a/lib/base/convert.cpp
+++ b/lib/base/convert.cpp
@@ -43,3 +43,7 @@ String Convert::ToString(long val)
 	return cs.str();
 }
 
+String Convert::ToString(const Value& val)
+{
+	return static_cast<String>(val);
+}
diff --git a/lib/base/convert.h b/lib/base/convert.h
index 358dee6..54b4357 100644
--- a/lib/base/convert.h
+++ b/lib/base/convert.h
@@ -35,6 +35,7 @@ public:
 	static double ToDouble(const String& val);
 	static bool ToBool(const String& val);
 	static String ToString(long val);
+	static String ToString(const Value& val);
 
 private:
 	Convert(void);
diff --git a/lib/base/dictionary.cpp b/lib/base/dictionary.cpp
index 46595d5..cb1082e 100644
--- a/lib/base/dictionary.cpp
+++ b/lib/base/dictionary.cpp
@@ -255,4 +255,3 @@ cJSON *Dictionary::ToJson(void) const
 
 	return json;
 }
-
diff --git a/lib/base/process.cpp b/lib/base/process.cpp
index 263a889..2ee52b5 100644
--- a/lib/base/process.cpp
+++ b/lib/base/process.cpp
@@ -19,10 +19,6 @@
 
 #include "i2-base.h"
 
-#ifndef _MSC_VER
-#	include "popen_noshell.h"
-#endif /* _MSC_VER */
-
 using namespace icinga;
 
 bool Process::m_ThreadCreated = false;
@@ -30,8 +26,9 @@ boost::mutex Process::m_Mutex;
 deque<Process::Ptr> Process::m_Tasks;
 condition_variable Process::m_TasksCV;
 
-Process::Process(const String& command)
-	: AsyncTask<Process, ProcessResult>(), m_Command(command), m_UsePopen(false)
+Process::Process(const String& command, const Dictionary::Ptr& environment)
+	: AsyncTask<Process, ProcessResult>(), m_Command(command),
+	  m_Environment(environment), m_FP(NULL)
 {
 	assert(Application::IsMainThread());
 
@@ -130,29 +127,127 @@ void Process::WorkerThreadProc(void)
 	}
 }
 
-void Process::InitTask(void)
+void Process::Spawn(const String& command, const Dictionary::Ptr& env)
 {
-	m_Result.ExecutionStart = Utility::GetTime();
+	vector<String> args;
+	args.push_back("sh");
+	args.push_back("-c");
+	args.push_back(command);
+
+	return Spawn(args, env);
+}
+
+void Process::Spawn(const vector<String>& args, const Dictionary::Ptr& extraEnv)
+{
+	assert(m_FP == NULL);
 
 #ifdef _MSC_VER
-	m_FP = _popen(m_Command.CStr(), "r");
+#error "TODO: implement"
 #else /* _MSC_VER */
-	if (!m_UsePopen) {
-		m_PCloseArg = new popen_noshell_pass_to_pclose;
+	int fds[2];
+
+	if (pipe(fds) < 0)
+		BOOST_THROW_EXCEPTION(PosixException("pipe() failed.", errno));
+
+	char **argv = new char *[args.size() + 1];
+
+	for (int i = 0; i < args.size(); i++)
+		argv[i] = strdup(args[i].CStr());
+
+	argv[args.size()] = NULL;
+
+	int envc = 0;
+
+	/* count existing environment variables */
+	while (environ[envc] != NULL)
+		envc++;
+
+	char **envp = new char *[envc + (extraEnv ? extraEnv->GetLength() : 0) + 1];
+
+	for (int i = 0; i < envc; i++)
+		envp[i] = environ[i];
+
+	if (extraEnv) {
+		String key;
+		Value value;
+		int index = envc;
+		BOOST_FOREACH(tie(key, value), extraEnv) {
+			String kv = key + "=" + Convert::ToString(value);
+			envp[index] = strdup(kv.CStr());
+			index++;
+		}
+	}
+
+	envp[envc + (extraEnv ? extraEnv->GetLength() : 0)] = NULL;
+
+#ifdef HAVE_VFORK
+	m_Pid = vfork();
+#else /* HAVE_VFORK */
+	m_Pid = fork();
+#endif /* HAVE_VFORK */
+
+	if (m_Pid < 0)
+		BOOST_THROW_EXCEPTION(PosixException("fork() failed.", errno));
+
+	if (m_Pid == 0) {
+		/* child */
+		if (dup2(fds[1], STDOUT_FILENO) < 0 || dup2(fds[1], STDERR_FILENO) < 0) {
+			perror("dup2() failed.");
+			_exit(128);
+		}
+
+		(void) close(fds[1]);
+
+		if (execvpe(argv[0], argv, envp) < 0) {
+			perror("execvpe() failed.");
+			_exit(128);
+		}
+
+		_exit(128);
+	} else {
+		for (int i = 0; i < args.size(); i++)
+			free(argv[i]);
+
+		delete [] argv;
+
+		if (extraEnv) {
+			for (int i = envc; i < envc + extraEnv->GetLength(); i++)
+				free(envp[i]);
+		}
+
+		delete [] envp;
 
-		m_FP = popen_noshell_compat(m_Command.CStr(), "r",
-		    (popen_noshell_pass_to_pclose *)m_PCloseArg);
+		/* parent */
+		(void) close(fds[1]);
+
+		m_FP = fdopen(fds[0], "r");
 
 		if (m_FP == NULL)
-			m_UsePopen = true;
+			BOOST_THROW_EXCEPTION(PosixException("fdopen() failed.", errno));
 	}
+#endif /* _MSC_VER */
+}
+
+int Process::WaitPid(void)
+{
+#ifdef _MSC_VER
+	return _pclose(m_FP);
+#else /* _MSC_VER */
+	fclose(m_FP);
 
-	if (m_UsePopen)
-		m_FP = popen(m_Command.CStr(), "r");
+	int status;
+	if (waitpid(m_Pid, &status, 0) != m_Pid)
+		BOOST_THROW_EXCEPTION(PosixException("waitpid() failed.", errno));
+
+	return status;
 #endif /* _MSC_VER */
+}
 
-	if (m_FP == NULL)
-		BOOST_THROW_EXCEPTION(runtime_error("Could not create process."));
+void Process::InitTask(void)
+{
+	m_Result.ExecutionStart = Utility::GetTime();
+
+	Spawn(m_Command, m_Environment);
 }
 
 bool Process::RunTask(void)
@@ -169,18 +264,8 @@ bool Process::RunTask(void)
 	String output = m_OutputStream.str();
 
 	int status, exitcode;
-#ifdef _MSC_VER
-	status = _pclose(m_FP);
-#else /* _MSC_VER */
-	if (m_UsePopen) {
-		status = pclose(m_FP);
-	} else {
-		status = pclose_noshell((popen_noshell_pass_to_pclose *)m_PCloseArg);
-		delete (popen_noshell_pass_to_pclose *)m_PCloseArg;
-	}
-#endif /* _MSC_VER */
 
-	m_Result.ExecutionEnd = Utility::GetTime();
+	status = WaitPid();
 
 #ifndef _MSC_VER
 	if (WIFEXITED(status)) {
@@ -206,6 +291,7 @@ bool Process::RunTask(void)
 	}
 #endif /* _MSC_VER */
 
+	m_Result.ExecutionEnd = Utility::GetTime();
 	m_Result.ExitStatus = exitcode;
 	m_Result.Output = output;
 
diff --git a/lib/base/process.h b/lib/base/process.h
index 10fa8d3..1c0c2f5 100644
--- a/lib/base/process.h
+++ b/lib/base/process.h
@@ -50,19 +50,20 @@ public:
 
 	static const deque<Process::Ptr>::size_type MaxTasksPerThread = 512;
 
-	Process(const String& command);
+	Process(const String& command, const Dictionary::Ptr& environment = Dictionary::Ptr());
 
 private:
 	static bool m_ThreadCreated;
 
 	String m_Command;
+	Dictionary::Ptr m_Environment;
 
+#ifndef _WIN32
+	pid_t m_Pid;
+#endif /* _WIN32 */
 	FILE *m_FP;
+
 	stringstream m_OutputStream;
-	bool m_UsePopen;
-#ifndef _MSC_VER
-	void *m_PCloseArg;
-#endif /* _MSC_VER */
 
 	ProcessResult m_Result;
 
@@ -78,6 +79,10 @@ private:
 	bool RunTask(void);
 
 	int GetFD(void) const;
+
+	void Spawn(const String& command, const Dictionary::Ptr& extraEnv);
+	void Spawn(const vector<String>& args, const Dictionary::Ptr& extraEnv);
+	int WaitPid(void);
 };
 
 }
diff --git a/lib/icinga/macroprocessor.cpp b/lib/icinga/macroprocessor.cpp
index 4561aab..e1490aa 100644
--- a/lib/icinga/macroprocessor.cpp
+++ b/lib/icinga/macroprocessor.cpp
@@ -56,3 +56,21 @@ String MacroProcessor::ResolveMacros(const String& str, const vector<Dictionary:
 
 	return result;
 }
+
+Dictionary::Ptr MacroProcessor::MakeEnvironment(const vector<Dictionary::Ptr>& dicts)
+{
+	Dictionary::Ptr result = boost::make_shared<Dictionary>();
+
+	BOOST_REVERSE_FOREACH(const Dictionary::Ptr& dict, dicts) {
+		String key;
+		Value value;
+		BOOST_FOREACH(tie(key, value), dict) {
+			if (!value.IsScalar())
+				continue;
+
+			result->Set(key, value);
+		}
+	}
+
+	return result;
+}
diff --git a/lib/icinga/macroprocessor.h b/lib/icinga/macroprocessor.h
index e015e9e..41fccad 100644
--- a/lib/icinga/macroprocessor.h
+++ b/lib/icinga/macroprocessor.h
@@ -32,6 +32,7 @@ class I2_ICINGA_API MacroProcessor
 {
 public:
 	static String ResolveMacros(const String& str, const vector<Dictionary::Ptr>& macroDicts);
+	static Dictionary::Ptr MakeEnvironment(const vector<Dictionary::Ptr>& macroDicts);
 };
 
 }
diff --git a/lib/icinga/pluginchecktask.cpp b/lib/icinga/pluginchecktask.cpp
index cb47747..44ae326 100644
--- a/lib/icinga/pluginchecktask.cpp
+++ b/lib/icinga/pluginchecktask.cpp
@@ -48,7 +48,7 @@ void PluginCheckTask::ScriptFunc(const ScriptTask::Ptr& task, const vector<Value
 	macroDicts.push_back(IcingaApplication::GetInstance()->GetMacros());
 	String command = MacroProcessor::ResolveMacros(checkCommand, macroDicts);
 
-	Process::Ptr process = boost::make_shared<Process>(command);
+	Process::Ptr process = boost::make_shared<Process>(command, MacroProcessor::MakeEnvironment(macroDicts));
 
 	PluginCheckTask ct(task, process);
 
diff --git a/lib/icinga/pluginnotificationtask.cpp b/lib/icinga/pluginnotificationtask.cpp
index 7901bf8..7bea524 100644
--- a/lib/icinga/pluginnotificationtask.cpp
+++ b/lib/icinga/pluginnotificationtask.cpp
@@ -53,7 +53,7 @@ void PluginNotificationTask::ScriptFunc(const ScriptTask::Ptr& task, const vecto
 	macroDicts.push_back(IcingaApplication::GetInstance()->GetMacros());
 	String command = MacroProcessor::ResolveMacros(notificationCommand, macroDicts);
 
-	Process::Ptr process = boost::make_shared<Process>(command);
+	Process::Ptr process = boost::make_shared<Process>(command, MacroProcessor::MakeEnvironment(macroDicts));
 
 	PluginNotificationTask ct(task, process, notification->GetService()->GetName(), command);
 





More information about the icinga-checkins mailing list