]> granicus.if.org Git - icinga2/commitdiff
Implement global modified attributes for the IcingaApplication class
authorMichael Friedrich <michael.friedrich@netways.de>
Tue, 22 Sep 2015 16:18:29 +0000 (18:18 +0200)
committerMichael Friedrich <michael.friedrich@netways.de>
Wed, 23 Sep 2015 13:02:53 +0000 (15:02 +0200)
fixes #9859

20 files changed:
doc/20-language-reference.md
doc/6-object-types.md
etc/CMakeLists.txt
etc/icinga2/conf.d/app.conf [new file with mode: 0644]
lib/base/application.cpp
lib/base/application.hpp
lib/cli/daemonutility.cpp
lib/hello/CMakeLists.txt
lib/hello/hello-app.conf [deleted file]
lib/hello/helloapplication.cpp [moved from lib/hello/hello.cpp with 76% similarity]
lib/hello/helloapplication.hpp [moved from lib/hello/hello.hpp with 82% similarity]
lib/hello/helloapplication.ti [moved from lib/hello/hello.ti with 97% similarity]
lib/icinga/CMakeLists.txt
lib/icinga/customvarobject.cpp
lib/icinga/icinga-app.conf [deleted file]
lib/icinga/icingaapplication.cpp
lib/icinga/icingaapplication.hpp
lib/icinga/icingaapplication.ti
lib/icinga/macroprocessor.cpp
lib/icinga/macroprocessor.hpp

index 4dc265e59dbe3b97c958b02ea07d3d6fbf62115b..69fcab77d6be68d2cc15741408ed62e64b4a2cb0 100644 (file)
@@ -368,16 +368,10 @@ ObjectsPath         |**Read-write.** Contains the path of the Icinga 2 objects f
 PidPath             |**Read-write.** Contains the path of the Icinga 2 PID file. Defaults to RunDir + "/icinga2/icinga2.pid".
 Vars                |**Read-write.** Contains a dictionary with global custom attributes. Not set by default.
 NodeName            |**Read-write.** Contains the cluster node name. Set to the local hostname by default.
-EnableNotifications |**Read-write.** Whether notifications are globally enabled. Defaults to true.
-EnableEventHandlers |**Read-write.** Whether event handlers are globally enabled. Defaults to true.
-EnableFlapping      |**Read-write.** Whether flap detection is globally enabled. Defaults to true.
-EnableHostChecks    |**Read-write.** Whether active host checks are globally enabled. Defaults to true.
-EnableServiceChecks |**Read-write.** Whether active service checks are globally enabled. Defaults to true.
-EnablePerfdata      |**Read-write.** Whether performance data processing is globally enabled. Defaults to true.
 UseVfork            |**Read-write.** Whether to use vfork(). Only available on *NIX. Defaults to true.
 AttachDebugger      |**Read-write.** Whether to attach a debugger when Icinga 2 crashes. Defaults to false.
-RunAsUser              |**Read-write.** Defines the user the Icinga 2 daemon is running as. Used in the `init.conf` configuration file.
-RunAsGroup             |**Read-write.** Defines the group the Icinga 2 daemon is running as. Used in the `init.conf` configuration file.
+RunAsUser           |**Read-write.** Defines the user the Icinga 2 daemon is running as. Used in the `init.conf` configuration file.
+RunAsGroup         |**Read-write.** Defines the group the Icinga 2 daemon is running as. Used in the `init.conf` configuration file.
 
 ## <a id="apply"></a> Apply
 
index 7239e6ba1825c4567dcac17fd60b1d8a8f3a7b88..1eaff0659a4d682b393282c20186f0197ea6547e 100644 (file)
@@ -552,6 +552,30 @@ Configuration Attributes:
   display_name    |**Optional.** A short description of the host group.
   groups          |**Optional.** An array of nested group names.
 
+## <a id="objecttype-icingaapplication"></a> IcingaApplication
+
+The IcingaApplication object is required to start Icinga 2.
+The object name must be `app`. If the object configuration
+is missing Icinga 2 will automatically create an IcingaApplication
+object.
+
+Example:
+
+    object IcingaApplication "app" {
+      enable_perfdata = false
+    }
+
+Configuration Attributes:
+
+  Name                  |Description
+  ----------------------|--------------------------
+  enable_notifications  |**Optional.** Whether notifications are globally enabled. Defaults to true.
+  enable_event_handlers |**Optional.** Whether event handlers are globally enabled. Defaults to true.
+  enable_flapping       |**Optional.** Whether flap detection is globally enabled. Defaults to true.
+  enable_host_checks    |**Optional.** Whether active host checks are globally enabled. Defaults to true.
+  enable_service_checks |**Optional.** Whether active service checks are globally enabled. Defaults to true.
+  enable_perfdata       |**Optional.** Whether performance data processing is globally enabled. Defaults to true.
+  vars                  |**Optional.** A dictionary containing custom attributes that are available globally.
 
 ## <a id="objecttype-icingastatuswriter"></a> IcingaStatusWriter
 
index 14b1c8eb0be1a1b20db7feb6fa73141bae5e002a..1b0045c87755bf07dccb87e2edc043594cefb130 100644 (file)
@@ -37,6 +37,7 @@ else()
 endif()
 
 install_if_not_exists(icinga2/zones.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2)
+install_if_not_exists(icinga2/conf.d/app.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
 install_if_not_exists(icinga2/conf.d/commands.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
 install_if_not_exists(icinga2/conf.d/downtimes.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
 install_if_not_exists(icinga2/conf.d/groups.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
diff --git a/etc/icinga2/conf.d/app.conf b/etc/icinga2/conf.d/app.conf
new file mode 100644 (file)
index 0000000..3e4be0d
--- /dev/null
@@ -0,0 +1 @@
+object IcingaApplication "app" { }
index afa2e66a3a12f623b5f8093c7c02e630090a6512..05d7fb6d2ec365eb7c58458b458f8b4f41a39b00 100644 (file)
@@ -1449,3 +1449,11 @@ void Application::SetStartTime(double ts)
 {
        m_StartTime = ts;
 }
+
+void Application::ValidateName(const String& value, const ValidationUtils& utils)
+{
+       ObjectImpl<Application>::ValidateName(value, utils);
+
+       if (value != "app")
+               BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("name"), "Application object must be named 'app'."));
+}
index eeb8f9c3b6239a9a79978f9ea040845083766ae0..3906f2efbb4cc3957c7173b5bc6c79a641e39dbd 100644 (file)
@@ -146,6 +146,8 @@ protected:
 
        virtual void OnShutdown(void);
 
+       virtual void ValidateName(const String& value, const ValidationUtils& utils) override;
+
 private:
        static Application::Ptr m_Instance; /**< The application instance. */
 
index 912c70848603312aea1dc7c606c0faeb5051a7f3..bff3a91ecc1647b16ca734740ffc045630f6a165 100644 (file)
@@ -124,6 +124,16 @@ bool DaemonUtility::ValidateConfigFiles(const std::vector<std::string>& configs,
        if (!success)
                return false;
 
+       String appType = ScriptGlobal::Get("ApplicationType", &Empty);
+
+       if (ConfigItem::GetItems(appType).empty()) {
+               ConfigItemBuilder::Ptr builder = new ConfigItemBuilder();
+               builder->SetType(appType);
+               builder->SetName("app");
+               ConfigItem::Ptr item = builder->Compile();
+               item->Register();
+       }
+
        return true;
 }
 
index f8a1190ec976f2a4c080b29bd850c50e815f7f31..e6877613a801b386a4dbd178b9c75d2dde018eae 100644 (file)
 # along with this program; if not, write to the Free Software Foundation
 # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
-mkclass_target(hello.ti hello.tcpp hello.thpp)
-
-mkembedconfig_target(hello-app.conf hello-app.cpp)
+mkclass_target(helloapplication.ti helloapplication.tcpp helloapplication.thpp)
 
 set(hello_SOURCES
-  hello.cpp hello.thpp hello-app.cpp
+  helloapplication.cpp helloapplication.thpp
 )
 
 if(ICINGA2_UNITY_BUILD)
diff --git a/lib/hello/hello-app.conf b/lib/hello/hello-app.conf
deleted file mode 100644 (file)
index 5daf38a..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/******************************************************************************
- * Icinga 2                                                                   *
- * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)    *
- *                                                                            *
- * This program is free software; you can redistribute it and/or              *
- * modify it under the terms of the GNU General Public License                *
- * as published by the Free Software Foundation; either version 2             *
- * of the License, or (at your option) any later version.                     *
- *                                                                            *
- * This program is distributed in the hope that it will be useful,            *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
- * GNU General Public License for more details.                               *
- *                                                                            *
- * You should have received a copy of the GNU General Public License          *
- * along with this program; if not, write to the Free Software Foundation     *
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
- ******************************************************************************/
-
-object Hello "app" { }
similarity index 76%
rename from lib/hello/hello.cpp
rename to lib/hello/helloapplication.cpp
index b273d523cbac3d449b8aa5314d79f8e7772775c6..0e562f271b1b3b3d89a275fb674001ec279b4146 100644 (file)
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#include "hello/hello.hpp"
-#include "hello/hello.tcpp"
+#include "hello/helloapplication.hpp"
+#include "hello/helloapplication.tcpp"
+#include "base/initialize.hpp"
 #include "base/logger.hpp"
+#include "base/scriptglobal.hpp"
 
 using namespace icinga;
 
-REGISTER_TYPE(Hello);
+REGISTER_TYPE(HelloApplication);
+
+INITIALIZE_ONCE(&HelloApplication::StaticInitialize);
+
+void HelloApplication::StaticInitialize(void)
+{
+       ScriptGlobal::Set("ApplicationType", "HelloApplication");
+}
 
 /**
- * The entry point for the Hello application.
+ * The entry point for the hello application.
  *
  * @returns An exit status.
  */
-int Hello::Main(void)
+int HelloApplication::Main(void)
 {
-       Log(LogInformation, "Hello", "Hello World!");
+       Log(LogInformation, "HelloApplication", "Hello World!");
 
        return 0;
 }
similarity index 82%
rename from lib/hello/hello.hpp
rename to lib/hello/helloapplication.hpp
index cb3775748eaa94f09c883cdeb1661177b244923a..7081747eb491074948ba7d7b4758c9bffa4c000b 100644 (file)
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#ifndef HELLO_H
-#define HELLO_H
+#ifndef HELLOAPPLICATION_H
+#define HELLOAPPLICATION_H
 
-#include "hello/hello.thpp"
+#include "hello/helloapplication.thpp"
 
 namespace icinga
 {
 
 /**
- * The Hello application.
+ * The hello application.
  *
  * @ingroup hello
  */
-class Hello : public ObjectImpl<Hello>
+class HelloApplication : public ObjectImpl<HelloApplication>
 {
 public:
-       DECLARE_OBJECT(Hello);
-       DECLARE_OBJECTNAME(Hello);
+       DECLARE_OBJECT(HelloApplication);
+       DECLARE_OBJECTNAME(HelloApplication);
+
+       static void StaticInitialize(void);
 
        virtual int Main(void) override;
 };
 
 }
 
-#endif /* HELLO_H */
+#endif /* HELLOAPPLICATION_H */
similarity index 97%
rename from lib/hello/hello.ti
rename to lib/hello/helloapplication.ti
index 008b66d4d37eb8e741f1b59da35b843870a63bf4..1cb933a22884a9cf0950e6db7caf488e1537ce8f 100644 (file)
@@ -24,7 +24,7 @@ library hello;
 namespace icinga
 {
 
-class Hello : Application
+class HelloApplication : Application
 {
 };
 
index 57a8a3e287cd31124c2d828de964a5e6b42c94b5..7171cecab92afde5ab43d36563876d220b44fa13 100644 (file)
@@ -38,8 +38,6 @@ mkclass_target(timeperiod.ti timeperiod.tcpp timeperiod.thpp)
 mkclass_target(usergroup.ti usergroup.tcpp usergroup.thpp)
 mkclass_target(user.ti user.tcpp user.thpp)
 
-mkembedconfig_target(icinga-app.conf icinga-app.cpp)
-
 set(icinga_SOURCES
   api.cpp apiactions.cpp apievents.cpp checkable.cpp checkable.thpp checkable-dependency.cpp checkable-downtime.cpp checkable-event.cpp
   checkable-flapping.cpp checkcommand.cpp checkcommand.thpp checkresult.cpp checkresult.thpp
@@ -51,7 +49,7 @@ set(icinga_SOURCES
   notification-apply.cpp objectutils.cpp perfdatavalue.cpp perfdatavalue.thpp pluginutility.cpp scheduleddowntime.cpp scheduleddowntime.thpp
   scheduleddowntime-apply.cpp service-apply.cpp checkable-check.cpp checkable-comment.cpp
   service.cpp service.thpp servicegroup.cpp servicegroup.thpp checkable-notification.cpp timeperiod.cpp timeperiod.thpp
-  user.cpp user.thpp usergroup.cpp usergroup.thpp icinga-app.cpp
+  user.cpp user.thpp usergroup.cpp usergroup.thpp
 )
 
 if(ICINGA2_UNITY_BUILD)
index 802fd25890af7ead6b9430aa8bfc9ea1d3e5cdc3..a9deaa4f6ea30382ba2819fe3955acad57a9c697 100644 (file)
@@ -24,7 +24,6 @@
 #include "base/function.hpp"
 #include "base/exception.hpp"
 #include "base/objectlock.hpp"
-#include <boost/foreach.hpp>
 
 using namespace icinga;
 
@@ -49,47 +48,5 @@ bool CustomVarObject::IsVarOverridden(const String& name) const
 
 void CustomVarObject::ValidateVars(const Dictionary::Ptr& value, const ValidationUtils& utils)
 {
-       if (!value)
-               return;
-
-       /* string, array, dictionary */
-       ObjectLock olock(value);
-       BOOST_FOREACH(const Dictionary::Pair& kv, value) {
-               const Value& varval = kv.second;
-
-               if (varval.IsObjectType<Dictionary>()) {
-                       /* only one dictonary level */
-                       Dictionary::Ptr varval_dict = varval;
-
-                       ObjectLock xlock(varval_dict);
-                       BOOST_FOREACH(const Dictionary::Pair& kv_var, varval_dict) {
-                               if (kv_var.second.IsEmpty())
-                                       continue;
-
-                               if (!MacroProcessor::ValidateMacroString(kv_var.second))
-                                       BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of<String>("vars")(kv.first)(kv_var.first), "Closing $ not found in macro format string '" + kv_var.second + "'."));
-                       }
-               } else if (varval.IsObjectType<Array>()) {
-                       /* check all array entries */
-                       Array::Ptr varval_arr = varval;
-
-                       ObjectLock ylock (varval_arr);
-                       BOOST_FOREACH(const Value& arrval, varval_arr) {
-                               if (arrval.IsEmpty())
-                                       continue;
-
-                               if (!MacroProcessor::ValidateMacroString(arrval)) {
-                                       BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of<String>("vars")(kv.first), "Closing $ not found in macro format string '" + arrval + "'."));
-                               }
-                       }
-               } else {
-                       if (varval.IsEmpty())
-                               continue;
-
-                       String varstr = varval;
-
-                       if (!MacroProcessor::ValidateMacroString(varstr))
-                               BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of<String>("vars")(kv.first), "Closing $ not found in macro format string '" + varstr + "'."));
-               }
-       }
+       MacroProcessor::ValidateCustomVars(this, value);
 }
diff --git a/lib/icinga/icinga-app.conf b/lib/icinga/icinga-app.conf
deleted file mode 100644 (file)
index af0ba57..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/******************************************************************************
- * Icinga 2                                                                   *
- * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)    *
- *                                                                            *
- * This program is free software; you can redistribute it and/or              *
- * modify it under the terms of the GNU General Public License                *
- * as published by the Free Software Foundation; either version 2             *
- * of the License, or (at your option) any later version.                     *
- *                                                                            *
- * This program is distributed in the hope that it will be useful,            *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
- * GNU General Public License for more details.                               *
- *                                                                            *
- * You should have received a copy of the GNU General Public License          *
- * along with this program; if not, write to the Free Software Foundation     *
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
- ******************************************************************************/
-
-object IcingaApplication "app" { }
index bba902fbb98b288d9c727eef19c4dc79c0404a6b..743e12e7991eb5e88a32ea246ced5a175e454ed4 100644 (file)
@@ -20,6 +20,7 @@
 #include "icinga/icingaapplication.hpp"
 #include "icinga/icingaapplication.tcpp"
 #include "icinga/cib.hpp"
+#include "icinga/macroprocessor.hpp"
 #include "config/configwriter.hpp"
 #include "config/configcompiler.hpp"
 #include "base/configtype.hpp"
@@ -42,13 +43,6 @@ INITIALIZE_ONCE(&IcingaApplication::StaticInitialize);
 
 void IcingaApplication::StaticInitialize(void)
 {
-       ScriptGlobal::Set("EnableNotifications", true);
-       ScriptGlobal::Set("EnableEventHandlers", true);
-       ScriptGlobal::Set("EnableFlapping", true);
-       ScriptGlobal::Set("EnableHostChecks", true);
-       ScriptGlobal::Set("EnableServiceChecks", true);
-       ScriptGlobal::Set("EnablePerfdata", true);
-
        String node_name = Utility::GetFQDN();
 
        if (node_name.IsEmpty()) {
@@ -62,6 +56,8 @@ void IcingaApplication::StaticInitialize(void)
        }
 
        ScriptGlobal::Set("NodeName", node_name);
+
+       ScriptGlobal::Set("ApplicationType", "IcingaApplication");
 }
 
 REGISTER_STATSFUNCTION(IcingaApplicationStats, &IcingaApplication::StatsFunc);
@@ -191,16 +187,6 @@ IcingaApplication::Ptr IcingaApplication::GetInstance(void)
        return static_pointer_cast<IcingaApplication>(Application::GetInstance());
 }
 
-Dictionary::Ptr IcingaApplication::GetVars(void) const
-{
-       return ScriptGlobal::Get("Vars", &Empty);
-}
-
-String IcingaApplication::GetNodeName(void) const
-{
-       return ScriptGlobal::Get("NodeName");
-}
-
 bool IcingaApplication::ResolveMacro(const String& macro, const CheckResult::Ptr&, Value *result) const
 {
        double now = Utility::GetTime();
@@ -291,62 +277,12 @@ bool IcingaApplication::ResolveMacro(const String& macro, const CheckResult::Ptr
        return false;
 }
 
-bool IcingaApplication::GetEnableNotifications(void) const
-{
-       return ScriptGlobal::Get("EnableNotifications");
-}
-
-void IcingaApplication::SetEnableNotifications(bool enabled)
-{
-       ScriptGlobal::Set("EnableNotifications", enabled);
-}
-
-bool IcingaApplication::GetEnableEventHandlers(void) const
-{
-       return ScriptGlobal::Get("EnableEventHandlers");
-}
-
-void IcingaApplication::SetEnableEventHandlers(bool enabled)
-{
-       ScriptGlobal::Set("EnableEventHandlers", enabled);
-}
-
-bool IcingaApplication::GetEnableFlapping(void) const
-{
-       return ScriptGlobal::Get("EnableFlapping");
-}
-
-void IcingaApplication::SetEnableFlapping(bool enabled)
-{
-       ScriptGlobal::Set("EnableFlapping", enabled);
-}
-
-bool IcingaApplication::GetEnableHostChecks(void) const
-{
-       return ScriptGlobal::Get("EnableHostChecks");
-}
-
-void IcingaApplication::SetEnableHostChecks(bool enabled)
-{
-       ScriptGlobal::Set("EnableHostChecks", enabled);
-}
-
-bool IcingaApplication::GetEnableServiceChecks(void) const
-{
-       return ScriptGlobal::Get("EnableServiceChecks");
-}
-
-void IcingaApplication::SetEnableServiceChecks(bool enabled)
-{
-       ScriptGlobal::Set("EnableServiceChecks", enabled);
-}
-
-bool IcingaApplication::GetEnablePerfdata(void) const
+String IcingaApplication::GetNodeName(void) const
 {
-       return ScriptGlobal::Get("EnablePerfdata");
+       return ScriptGlobal::Get("NodeName");
 }
 
-void IcingaApplication::SetEnablePerfdata(bool enabled)
+void IcingaApplication::ValidateVars(const Dictionary::Ptr& value, const ValidationUtils& utils)
 {
-       ScriptGlobal::Set("EnablePerfdata", enabled);
+       MacroProcessor::ValidateCustomVars(this, value);
 }
index e1250e9885de1de987d9fd03def0474bfcc128a5..4e66ff86d51892a884cc97fd89a25da162026e87 100644 (file)
@@ -47,33 +47,12 @@ public:
        static IcingaApplication::Ptr GetInstance(void);
 
        String GetPidPath(void) const;
-       Dictionary::Ptr GetVars(void) const;
-       String GetNodeName(void) const;
 
        virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Value *result) const override;
 
-       bool GetEnableNotifications(void) const;
-       void SetEnableNotifications(bool enabled);
-       void ClearEnableNotifications(void);
-
-       bool GetEnableEventHandlers(void) const;
-       void SetEnableEventHandlers(bool enabled);
-       void ClearEnableEventHandlers(void);
-
-       bool GetEnableFlapping(void) const;
-       void SetEnableFlapping(bool enabled);
-       void ClearEnableFlapping(void);
-
-       bool GetEnableHostChecks(void) const;
-       void SetEnableHostChecks(bool enabled);
-       void ClearEnableHostChecks(void);
-       bool GetEnableServiceChecks(void) const;
-       void SetEnableServiceChecks(bool enabled);
-       void ClearEnableServiceChecks(void);
+       String GetNodeName(void) const;
 
-       bool GetEnablePerfdata(void) const;
-       void SetEnablePerfdata(bool enabled);
-       void ClearEnablePerfdata(void);
+       virtual void ValidateVars(const Dictionary::Ptr& value, const ValidationUtils& utils) override;
 
 private:
        void DumpProgramState(void);
index 447adf456ec4d39020cb5c5a1ad282736bfb3b6d..4f07ab53a526b6af5fd47cfcc2abe86c11355c47 100644 (file)
@@ -26,6 +26,25 @@ namespace icinga
 
 class IcingaApplication : Application
 {
+       [config] bool enable_notifications {
+               default {{{ return true; }}}
+       };
+       [config] bool enable_event_handlers {
+               default {{{ return true; }}}
+       };
+       [config] bool enable_flapping {
+               default {{{ return true; }}}
+       };
+       [config] bool enable_host_checks {
+               default {{{ return true; }}}
+       };
+       [config] bool enable_service_checks {
+               default {{{ return true; }}}
+       };
+       [config] bool enable_perfdata {
+               default {{{ return true; }}}
+       };
+       [config] Dictionary::Ptr vars;
 };
 
 }
index c33289a5ed06a34f2d70fab2dbea7f0d8693eb94..cfd0381d0bb0e90d8d832aa7bdd4645e1eb2ca20 100644 (file)
@@ -28,6 +28,7 @@
 #include "base/scriptframe.hpp"
 #include "base/convert.hpp"
 #include "base/exception.hpp"
+#include <boost/assign.hpp>
 #include <boost/foreach.hpp>
 #include <boost/algorithm/string/split.hpp>
 #include <boost/algorithm/string/join.hpp>
@@ -351,6 +352,53 @@ bool MacroProcessor::ValidateMacroString(const String& macro)
        return true;
 }
 
+void MacroProcessor::ValidateCustomVars(const ConfigObject::Ptr& object, const Dictionary::Ptr& value)
+{
+       if (!value)
+               return;
+
+       /* string, array, dictionary */
+       ObjectLock olock(value);
+       BOOST_FOREACH(const Dictionary::Pair& kv, value) {
+               const Value& varval = kv.second;
+
+               if (varval.IsObjectType<Dictionary>()) {
+                       /* only one dictonary level */
+                       Dictionary::Ptr varval_dict = varval;
+
+                       ObjectLock xlock(varval_dict);
+                       BOOST_FOREACH(const Dictionary::Pair& kv_var, varval_dict) {
+                               if (kv_var.second.IsEmpty())
+                                       continue;
+
+                               if (!ValidateMacroString(kv_var.second))
+                                       BOOST_THROW_EXCEPTION(ValidationError(object.get(), boost::assign::list_of<String>("vars")(kv.first)(kv_var.first), "Closing $ not found in macro format string '" + kv_var.second + "'."));
+                       }
+               } else if (varval.IsObjectType<Array>()) {
+                       /* check all array entries */
+                       Array::Ptr varval_arr = varval;
+
+                       ObjectLock ylock (varval_arr);
+                       BOOST_FOREACH(const Value& arrval, varval_arr) {
+                               if (arrval.IsEmpty())
+                                       continue;
+
+                               if (!ValidateMacroString(arrval)) {
+                                       BOOST_THROW_EXCEPTION(ValidationError(object.get(), boost::assign::list_of<String>("vars")(kv.first), "Closing $ not found in macro format string '" + arrval + "'."));
+                               }
+                       }
+               } else {
+                       if (varval.IsEmpty())
+                               continue;
+
+                       String varstr = varval;
+
+                       if (!ValidateMacroString(varstr))
+                               BOOST_THROW_EXCEPTION(ValidationError(object.get(), boost::assign::list_of<String>("vars")(kv.first), "Closing $ not found in macro format string '" + varstr + "'."));
+               }
+       }
+}
+
 void MacroProcessor::AddArgumentHelper(const Array::Ptr& args, const String& key, const String& value,
     bool add_key, bool add_value)
 {
index 60d579d23c743b02e77235e5a3519aae55c80073..01d5d6add63e182b45a4fc5c44eb9353004a2cbd 100644 (file)
@@ -52,6 +52,7 @@ public:
            const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros, int recursionLevel = 0);
 
        static bool ValidateMacroString(const String& macro);
+       static void ValidateCustomVars(const ConfigObject::Ptr& object, const Dictionary::Ptr& value);
 
 private:
        MacroProcessor(void);