]> granicus.if.org Git - icinga2/commitdiff
Implemented environment-based macros.
authorGunnar Beutner <gunnar.beutner@netways.de>
Sat, 9 Feb 2013 22:02:33 +0000 (23:02 +0100)
committerGunnar Beutner <gunnar.beutner@netways.de>
Sat, 9 Feb 2013 22:02:33 +0000 (23:02 +0100)
configure.ac
lib/base/convert.cpp
lib/base/convert.h
lib/base/dictionary.cpp
lib/base/process.cpp
lib/base/process.h
lib/icinga/macroprocessor.cpp
lib/icinga/macroprocessor.h
lib/icinga/pluginchecktask.cpp
lib/icinga/pluginnotificationtask.cpp

index d39cf8a4d02c2cb2fca04ac05aa63c6cabc7c788..0f9cec07114e20f164d73bf747aca3c9805ed274 100644 (file)
@@ -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
index d3bcd1e1ce534900d6e9c3be7df8494002449ced..3a35326c653a03a33287fb2713475176fd3e9f16 100644 (file)
@@ -43,3 +43,7 @@ String Convert::ToString(long val)
        return cs.str();
 }
 
+String Convert::ToString(const Value& val)
+{
+       return static_cast<String>(val);
+}
index 358dee62e3f394f34e31318fe0a73feb855a7ac2..54b4357c08aa85a149e309d206a844349c3f4a4e 100644 (file)
@@ -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);
index 46595d5d437f19255167b1e312cea365b69b7c19..cb1082e9b5a5b9f5ba02f3cdf2caaa7313e3cffc 100644 (file)
@@ -255,4 +255,3 @@ cJSON *Dictionary::ToJson(void) const
 
        return json;
 }
-
index 263a889b0c43e227281a5fbefb09b0c2d61bf9c6..2ee52b542e512352523410e1bc2d7e159feb2132 100644 (file)
 
 #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;
 
index 10fa8d33d1ded50d30c62c5b8c606fe4dc214096..1c0c2f5694ef281bec32f59b19414b7396d99406 100644 (file)
@@ -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);
 };
 
 }
index 4561aab9768ef0e31eb3c9363432c8f5af652e33..e1490aa509b2284373ca2b61c4b2ad04149af31c 100644 (file)
@@ -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;
+}
index e015e9e8206c5ae0d05a932a439172c9867b2efe..41fccad14251e245d1f72146695b07d740b795e3 100644 (file)
@@ -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);
 };
 
 }
index cb477473310e14d501c1f50e665b412aabb09707..44ae3264ba13950c12e89c4cbfcb5ebe62a5d348 100644 (file)
@@ -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);
 
index 7901bf8713c79986d90c35822e9f1cdf3ac50298..7bea52468523d3a6d63179ab5ffb33285409b52d 100644 (file)
@@ -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);