From ee37e0cace686733ad14e1e2acf39726947dc11d Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Sat, 9 Feb 2013 23:02:33 +0100 Subject: [PATCH] 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 d39cf8a4d..0f9cec071 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 d3bcd1e1c..3a35326c6 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(val); +} diff --git a/lib/base/convert.h b/lib/base/convert.h index 358dee62e..54b4357c0 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 46595d5d4..cb1082e9b 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 263a889b0..2ee52b542 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::m_Tasks; condition_variable Process::m_TasksCV; -Process::Process(const String& command) - : AsyncTask(), m_Command(command), m_UsePopen(false) +Process::Process(const String& command, const Dictionary::Ptr& environment) + : AsyncTask(), 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 args; + args.push_back("sh"); + args.push_back("-c"); + args.push_back(command); + + return Spawn(args, env); +} + +void Process::Spawn(const vector& 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 10fa8d33d..1c0c2f569 100644 --- a/lib/base/process.h +++ b/lib/base/process.h @@ -50,19 +50,20 @@ public: static const deque::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& args, const Dictionary::Ptr& extraEnv); + int WaitPid(void); }; } diff --git a/lib/icinga/macroprocessor.cpp b/lib/icinga/macroprocessor.cpp index 4561aab97..e1490aa50 100644 --- a/lib/icinga/macroprocessor.cpp +++ b/lib/icinga/macroprocessor.cpp @@ -56,3 +56,21 @@ String MacroProcessor::ResolveMacros(const String& str, const vector& dicts) +{ + Dictionary::Ptr result = boost::make_shared(); + + 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 e015e9e82..41fccad14 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& macroDicts); + static Dictionary::Ptr MakeEnvironment(const vector& macroDicts); }; } diff --git a/lib/icinga/pluginchecktask.cpp b/lib/icinga/pluginchecktask.cpp index cb4774733..44ae3264b 100644 --- a/lib/icinga/pluginchecktask.cpp +++ b/lib/icinga/pluginchecktask.cpp @@ -48,7 +48,7 @@ void PluginCheckTask::ScriptFunc(const ScriptTask::Ptr& task, const vectorGetMacros()); String command = MacroProcessor::ResolveMacros(checkCommand, macroDicts); - Process::Ptr process = boost::make_shared(command); + Process::Ptr process = boost::make_shared(command, MacroProcessor::MakeEnvironment(macroDicts)); PluginCheckTask ct(task, process); diff --git a/lib/icinga/pluginnotificationtask.cpp b/lib/icinga/pluginnotificationtask.cpp index 7901bf871..7bea52468 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(command); + Process::Ptr process = boost::make_shared(command, MacroProcessor::MakeEnvironment(macroDicts)); PluginNotificationTask ct(task, process, notification->GetService()->GetName(), command); -- 2.40.0