]> granicus.if.org Git - icinga2/blob - lib/config/configcompiler.cpp
Merge pull request #7002 from Icinga/bugfix/check_network-percent-6155
[icinga2] / lib / config / configcompiler.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "config/configcompiler.hpp"
4 #include "config/configitem.hpp"
5 #include "base/logger.hpp"
6 #include "base/utility.hpp"
7 #include "base/loader.hpp"
8 #include "base/context.hpp"
9 #include "base/exception.hpp"
10 #include <fstream>
11
12 using namespace icinga;
13
14 std::vector<String> ConfigCompiler::m_IncludeSearchDirs;
15 boost::mutex ConfigCompiler::m_ZoneDirsMutex;
16 std::map<String, std::vector<ZoneFragment> > ConfigCompiler::m_ZoneDirs;
17
18 /**
19  * Constructor for the ConfigCompiler class.
20  *
21  * @param path The path of the configuration file (or another name that
22  *        identifies the source of the configuration text).
23  * @param input Input stream for the configuration file.
24  * @param zone The zone.
25  */
26 ConfigCompiler::ConfigCompiler(String path, std::istream *input,
27         String zone, String package)
28         : m_Path(std::move(path)), m_Input(input), m_Zone(std::move(zone)),
29         m_Package(std::move(package)), m_Eof(false), m_OpenBraces(0)
30 {
31         InitializeScanner();
32 }
33
34 /**
35  * Destructor for the ConfigCompiler class.
36  */
37 ConfigCompiler::~ConfigCompiler()
38 {
39         DestroyScanner();
40 }
41
42 /**
43  * Reads data from the input stream. Used internally by the lexer.
44  *
45  * @param buffer Where to store data.
46  * @param max_size The maximum number of bytes to read from the stream.
47  * @returns The actual number of bytes read.
48  */
49 size_t ConfigCompiler::ReadInput(char *buffer, size_t max_size)
50 {
51         m_Input->read(buffer, max_size);
52         return static_cast<size_t>(m_Input->gcount());
53 }
54
55 /**
56  * Retrieves the scanner object.
57  *
58  * @returns The scanner object.
59  */
60 void *ConfigCompiler::GetScanner() const
61 {
62         return m_Scanner;
63 }
64
65 /**
66  * Retrieves the path for the input file.
67  *
68  * @returns The path.
69  */
70 const char *ConfigCompiler::GetPath() const
71 {
72         return m_Path.CStr();
73 }
74
75 void ConfigCompiler::SetZone(const String& zone)
76 {
77         m_Zone = zone;
78 }
79
80 String ConfigCompiler::GetZone() const
81 {
82         return m_Zone;
83 }
84
85 void ConfigCompiler::SetPackage(const String& package)
86 {
87         m_Package = package;
88 }
89
90 String ConfigCompiler::GetPackage() const
91 {
92         return m_Package;
93 }
94
95 void ConfigCompiler::CollectIncludes(std::vector<std::unique_ptr<Expression> >& expressions,
96         const String& file, const String& zone, const String& package)
97 {
98         try {
99                 expressions.emplace_back(CompileFile(file, zone, package));
100         } catch (const std::exception& ex) {
101                 Log(LogWarning, "ConfigCompiler")
102                         << "Cannot compile file '"
103                         << file << "': " << DiagnosticInformation(ex);
104         }
105 }
106
107 /**
108  * Handles an include directive.
109  *
110  * @param relativeBath The path this include is relative to.
111  * @param path The path from the include directive.
112  * @param search Whether to search global include dirs.
113  * @param debuginfo Debug information.
114  */
115 std::unique_ptr<Expression> ConfigCompiler::HandleInclude(const String& relativeBase, const String& path,
116         bool search, const String& zone, const String& package, const DebugInfo& debuginfo)
117 {
118         String upath;
119
120         if (search || (IsAbsolutePath(path)))
121                 upath = path;
122         else
123                 upath = relativeBase + "/" + path;
124
125         String includePath = upath;
126
127         if (search) {
128                 for (const String& dir : m_IncludeSearchDirs) {
129                         String spath = dir + "/" + path;
130
131                         if (Utility::PathExists(spath)) {
132                                 includePath = spath;
133                                 break;
134                         }
135                 }
136         }
137
138         std::vector<std::unique_ptr<Expression> > expressions;
139
140         if (!Utility::Glob(includePath, std::bind(&ConfigCompiler::CollectIncludes, std::ref(expressions), _1, zone, package), GlobFile) && includePath.FindFirstOf("*?") == String::NPos) {
141                 std::ostringstream msgbuf;
142                 msgbuf << "Include file '" + path + "' does not exist";
143                 BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), debuginfo));
144         }
145
146         std::unique_ptr<DictExpression> expr{new DictExpression(std::move(expressions))};
147         expr->MakeInline();
148         return std::move(expr);
149 }
150
151 /**
152  * Handles recursive includes.
153  *
154  * @param relativeBase The path this include is relative to.
155  * @param path The directory path.
156  * @param pattern The file pattern.
157  * @param debuginfo Debug information.
158  */
159 std::unique_ptr<Expression> ConfigCompiler::HandleIncludeRecursive(const String& relativeBase, const String& path,
160         const String& pattern, const String& zone, const String& package, const DebugInfo&)
161 {
162         String ppath;
163
164         if (IsAbsolutePath(path))
165                 ppath = path;
166         else
167                 ppath = relativeBase + "/" + path;
168
169         std::vector<std::unique_ptr<Expression> > expressions;
170         Utility::GlobRecursive(ppath, pattern, std::bind(&ConfigCompiler::CollectIncludes, std::ref(expressions), _1, zone, package), GlobFile);
171
172         std::unique_ptr<DictExpression> dict{new DictExpression(std::move(expressions))};
173         dict->MakeInline();
174         return std::move(dict);
175 }
176
177 void ConfigCompiler::HandleIncludeZone(const String& relativeBase, const String& tag, const String& path, const String& pattern, const String& package, std::vector<std::unique_ptr<Expression> >& expressions)
178 {
179         String zoneName = Utility::BaseName(path);
180
181         String ppath;
182
183         if (IsAbsolutePath(path))
184                 ppath = path;
185         else
186                 ppath = relativeBase + "/" + path;
187
188         RegisterZoneDir(tag, ppath, zoneName);
189
190         Utility::GlobRecursive(ppath, pattern, std::bind(&ConfigCompiler::CollectIncludes, std::ref(expressions), _1, zoneName, package), GlobFile);
191 }
192
193 /**
194  * Handles zone includes.
195  *
196  * @param relativeBase The path this include is relative to.
197  * @param tag The tag name.
198  * @param path The directory path.
199  * @param pattern The file pattern.
200  * @param debuginfo Debug information.
201  */
202 std::unique_ptr<Expression> ConfigCompiler::HandleIncludeZones(const String& relativeBase, const String& tag,
203         const String& path, const String& pattern, const String& package, const DebugInfo&)
204 {
205         String ppath;
206         String newRelativeBase = relativeBase;
207
208         if (IsAbsolutePath(path))
209                 ppath = path;
210         else {
211                 ppath = relativeBase + "/" + path;
212                 newRelativeBase = ".";
213         }
214
215         std::vector<std::unique_ptr<Expression> > expressions;
216         Utility::Glob(ppath + "/*", std::bind(&ConfigCompiler::HandleIncludeZone, newRelativeBase, tag, _1, pattern, package, std::ref(expressions)), GlobDirectory);
217         return std::unique_ptr<Expression>(new DictExpression(std::move(expressions)));
218 }
219
220 /**
221  * Compiles a stream.
222  *
223  * @param path A name identifying the stream.
224  * @param stream The input stream.
225  * @returns Configuration items.
226  */
227 std::unique_ptr<Expression> ConfigCompiler::CompileStream(const String& path,
228         std::istream *stream, const String& zone, const String& package)
229 {
230         CONTEXT("Compiling configuration stream with name '" + path + "'");
231
232         stream->exceptions(std::istream::badbit);
233
234         ConfigCompiler ctx(path, stream, zone, package);
235
236         try {
237                 return ctx.Compile();
238         } catch (const ScriptError& ex) {
239                 return std::unique_ptr<Expression>(new ThrowExpression(MakeLiteral(ex.what()), ex.IsIncompleteExpression(), ex.GetDebugInfo()));
240         } catch (const std::exception& ex) {
241                 return std::unique_ptr<Expression>(new ThrowExpression(MakeLiteral(DiagnosticInformation(ex)), false));
242         }
243 }
244
245 /**
246  * Compiles a file.
247  *
248  * @param path The path.
249  * @returns Configuration items.
250  */
251 std::unique_ptr<Expression> ConfigCompiler::CompileFile(const String& path, const String& zone,
252         const String& package)
253 {
254         CONTEXT("Compiling configuration file '" + path + "'");
255
256         std::ifstream stream(path.CStr(), std::ifstream::in);
257
258         if (!stream)
259                 BOOST_THROW_EXCEPTION(posix_error()
260                         << boost::errinfo_api_function("std::ifstream::open")
261                         << boost::errinfo_errno(errno)
262                         << boost::errinfo_file_name(path));
263
264         Log(LogNotice, "ConfigCompiler")
265                 << "Compiling config file: " << path;
266
267         return CompileStream(path, &stream, zone, package);
268 }
269
270 /**
271  * Compiles a snippet of text.
272  *
273  * @param path A name identifying the text.
274  * @param text The text.
275  * @returns Configuration items.
276  */
277 std::unique_ptr<Expression> ConfigCompiler::CompileText(const String& path, const String& text,
278         const String& zone, const String& package)
279 {
280         std::stringstream stream(text);
281         return CompileStream(path, &stream, zone, package);
282 }
283
284 /**
285  * Adds a directory to the list of include search dirs.
286  *
287  * @param dir The new dir.
288  */
289 void ConfigCompiler::AddIncludeSearchDir(const String& dir)
290 {
291         Log(LogInformation, "ConfigCompiler")
292                 << "Adding include search dir: " << dir;
293
294         m_IncludeSearchDirs.push_back(dir);
295 }
296
297 std::vector<ZoneFragment> ConfigCompiler::GetZoneDirs(const String& zone)
298 {
299         boost::mutex::scoped_lock lock(m_ZoneDirsMutex);
300         auto it = m_ZoneDirs.find(zone);
301         if (it == m_ZoneDirs.end())
302                 return std::vector<ZoneFragment>();
303         else
304                 return it->second;
305 }
306
307 void ConfigCompiler::RegisterZoneDir(const String& tag, const String& ppath, const String& zoneName)
308 {
309         ZoneFragment zf;
310         zf.Tag = tag;
311         zf.Path = ppath;
312
313         boost::mutex::scoped_lock lock(m_ZoneDirsMutex);
314         m_ZoneDirs[zoneName].push_back(zf);
315 }
316
317 bool ConfigCompiler::HasZoneConfigAuthority(const String& zoneName)
318 {
319         std::vector<ZoneFragment> zoneDirs = m_ZoneDirs[zoneName];
320
321         bool empty = zoneDirs.empty();
322
323         if (!empty) {
324                 std::vector<String> paths;
325                 paths.reserve(zoneDirs.size());
326
327                 for (const ZoneFragment& zf : zoneDirs) {
328                         paths.push_back(zf.Path);
329                 }
330
331                 Log(LogNotice, "ConfigCompiler")
332                         << "Registered authoritative config directories for zone '" << zoneName << "': " << Utility::NaturalJoin(paths);
333         }
334
335         return !empty;
336 }
337
338
339 bool ConfigCompiler::IsAbsolutePath(const String& path)
340 {
341 #ifndef _WIN32
342         return (path.GetLength() > 0 && path[0] == '/');
343 #else /* _WIN32 */
344         return !PathIsRelative(path.CStr());
345 #endif /* _WIN32 */
346 }
347
348 void ConfigCompiler::AddImport(const std::shared_ptr<Expression>& import)
349 {
350         m_Imports.push_back(import);
351 }
352
353 std::vector<std::shared_ptr<Expression> > ConfigCompiler::GetImports() const
354 {
355         return m_Imports;
356 }