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