1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
3 #include "cli/daemonutility.hpp"
4 #include "base/utility.hpp"
5 #include "base/logger.hpp"
6 #include "base/application.hpp"
7 #include "base/scriptglobal.hpp"
8 #include "config/configcompiler.hpp"
9 #include "config/configcompilercontext.hpp"
10 #include "config/configitembuilder.hpp"
12 using namespace icinga;
14 static bool ExecuteExpression(Expression *expression)
20 ScriptFrame frame(true);
21 expression->Evaluate(frame);
22 } catch (const std::exception& ex) {
23 Log(LogCritical, "config", DiagnosticInformation(ex));
30 static void IncludeZoneDirRecursive(const String& path, const String& package, bool& success)
32 String zoneName = Utility::BaseName(path);
34 /* We don't have an activated zone object yet. We may forcefully guess from configitems
35 * to not include this specific synced zones directory.
37 if(!ConfigItem::GetByTypeAndName(Type::GetByName("Zone"), zoneName)) {
38 Log(LogWarning, "config")
39 << "Ignoring directory '" << path << "' for unknown zone '" << zoneName << "'.";
43 /* register this zone path for cluster config sync */
44 ConfigCompiler::RegisterZoneDir("_etc", path, zoneName);
46 std::vector<std::unique_ptr<Expression> > expressions;
47 Utility::GlobRecursive(path, "*.conf", std::bind(&ConfigCompiler::CollectIncludes, std::ref(expressions), _1, zoneName, package), GlobFile);
48 DictExpression expr(std::move(expressions));
49 if (!ExecuteExpression(&expr))
53 static void IncludeNonLocalZone(const String& zonePath, const String& package, bool& success)
55 /* Note: This include function must not call RegisterZoneDir().
56 * We do not need to copy it for cluster config sync. */
58 String zoneName = Utility::BaseName(zonePath);
60 /* We don't have an activated zone object yet. We may forcefully guess from configitems
61 * to not include this specific synced zones directory.
63 if(!ConfigItem::GetByTypeAndName(Type::GetByName("Zone"), zoneName)) {
64 Log(LogWarning, "config")
65 << "Ignoring directory '" << zonePath << "' for unknown zone '" << zoneName << "'.";
69 /* Check whether this node already has an authoritative config version
70 * from zones.d in etc or api package directory, or a local marker file)
72 if (ConfigCompiler::HasZoneConfigAuthority(zoneName) || Utility::PathExists(zonePath + "/.authoritative")) {
73 Log(LogNotice, "config")
74 << "Ignoring non local config include for zone '" << zoneName << "': We already have an authoritative copy included.";
78 std::vector<std::unique_ptr<Expression> > expressions;
79 Utility::GlobRecursive(zonePath, "*.conf", std::bind(&ConfigCompiler::CollectIncludes, std::ref(expressions), _1, zoneName, package), GlobFile);
80 DictExpression expr(std::move(expressions));
81 if (!ExecuteExpression(&expr))
85 static void IncludePackage(const String& packagePath, bool& success)
87 /* Note: Package includes will register their zones
88 * for config sync inside their generated config. */
89 String packageName = Utility::BaseName(packagePath);
91 if (Utility::PathExists(packagePath + "/include.conf")) {
92 std::unique_ptr<Expression> expr = ConfigCompiler::CompileFile(packagePath + "/include.conf",
93 String(), packageName);
95 if (!ExecuteExpression(&*expr))
100 bool DaemonUtility::ValidateConfigFiles(const std::vector<std::string>& configs, const String& objectsFile)
103 if (!objectsFile.IsEmpty())
104 ConfigCompilerContext::GetInstance()->OpenObjectsFile(objectsFile);
106 if (!configs.empty()) {
107 for (const String& configPath : configs) {
109 std::unique_ptr<Expression> expression = ConfigCompiler::CompileFile(configPath, String(), "_etc");
110 success = ExecuteExpression(&*expression);
113 } catch (const std::exception& ex) {
114 Log(LogCritical, "cli", "Could not compile config files: " + DiagnosticInformation(ex, false));
115 Application::Exit(1);
120 /* Load cluster config files from /etc/icinga2/zones.d.
121 * This should probably be in libremote but
122 * unfortunately moving it there is somewhat non-trivial. */
125 String zonesEtcDir = Configuration::ZonesDir;
126 if (!zonesEtcDir.IsEmpty() && Utility::PathExists(zonesEtcDir))
127 Utility::Glob(zonesEtcDir + "/*", std::bind(&IncludeZoneDirRecursive, _1, "_etc", std::ref(success)), GlobDirectory);
132 /* Load package config files - they may contain additional zones which
133 * are authoritative on this node and are checked in HasZoneConfigAuthority(). */
134 String packagesVarDir = Configuration::DataDir + "/api/packages";
135 if (Utility::PathExists(packagesVarDir))
136 Utility::Glob(packagesVarDir + "/*", std::bind(&IncludePackage, _1, std::ref(success)), GlobDirectory);
141 /* Load cluster synchronized configuration files */
142 String zonesVarDir = Configuration::DataDir + "/api/zones";
143 if (Utility::PathExists(zonesVarDir))
144 Utility::Glob(zonesVarDir + "/*", std::bind(&IncludeNonLocalZone, _1, "_cluster", std::ref(success)), GlobDirectory);
149 Namespace::Ptr systemNS = ScriptGlobal::Get("System");
152 /* This is initialized inside the IcingaApplication class. */
154 VERIFY(systemNS->Get("ApplicationType", &vAppType));
156 Type::Ptr appType = Type::GetByName(vAppType);
158 if (ConfigItem::GetItems(appType).empty()) {
159 ConfigItemBuilder builder;
160 builder.SetType(appType);
161 builder.SetName("app");
162 builder.AddExpression(new ImportDefaultTemplatesExpression());
163 ConfigItem::Ptr item = builder.Compile();
170 bool DaemonUtility::LoadConfigFiles(const std::vector<std::string>& configs,
171 std::vector<ConfigItem::Ptr>& newItems,
172 const String& objectsFile, const String& varsfile)
174 ActivationScope ascope;
176 if (!DaemonUtility::ValidateConfigFiles(configs, objectsFile)) {
177 ConfigCompilerContext::GetInstance()->CancelObjectsFile();
181 WorkQueue upq(25000, Configuration::Concurrency);
182 upq.SetName("DaemonUtility::LoadConfigFiles");
183 bool result = ConfigItem::CommitItems(ascope.GetContext(), upq, newItems);
186 ConfigCompilerContext::GetInstance()->CancelObjectsFile();
190 ConfigCompilerContext::GetInstance()->FinishObjectsFile();
193 ScriptGlobal::WriteToFile(varsfile);
194 } catch (const std::exception& ex) {
195 Log(LogCritical, "cli", "Could not write vars file: " + DiagnosticInformation(ex, false));
196 Application::Exit(1);