]> granicus.if.org Git - icinga2/blob - lib/config/configcompiler.cpp
b678b735d79a93ffe7232754f43630c065d090c4
[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 std::map<String, std::vector<ZoneFragment> > ConfigCompiler::m_ZoneDirs;
34
35 /**
36  * Constructor for the ConfigCompiler class.
37  *
38  * @param path The path of the configuration file (or another name that
39  *             identifies the source of the configuration text).
40  * @param input Input stream for the configuration file.
41  * @param zone The zone.
42  */
43 ConfigCompiler::ConfigCompiler(const String& path, std::istream *input,
44     const String& zone, const String& module)
45         : m_Path(path), m_Input(input), m_Zone(zone), m_Module(module),
46           m_Eof(false), m_OpenBraces(0), m_IgnoreNewlines(0)
47 {
48         InitializeScanner();
49 }
50
51 /**
52  * Destructor for the ConfigCompiler class.
53  */
54 ConfigCompiler::~ConfigCompiler(void)
55 {
56         DestroyScanner();
57         delete m_Input;
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::SetModule(const String& module)
104 {
105         m_Module = module;
106 }
107
108 String ConfigCompiler::GetModule(void) const
109 {
110         return m_Module;
111 }
112
113 void ConfigCompiler::CollectIncludes(std::vector<Expression *>& expressions,
114     const String& file, const String& zone, const String& module)
115 {
116         expressions.push_back(CompileFile(file, true, zone, module));
117 }
118
119 /**
120  * Handles an include directive.
121  *
122  * @param include The path from the include directive.
123  * @param search Whether to search global include dirs.
124  * @param debuginfo Debug information.
125  */
126 Expression *ConfigCompiler::HandleInclude(const String& include, bool search, const DebugInfo& debuginfo)
127 {
128         String path;
129
130         if (search || (include.GetLength() > 0 && include[0] == '/'))
131                 path = include;
132         else
133                 path = Utility::DirName(GetPath()) + "/" + include;
134
135         String includePath = path;
136
137         if (search) {
138                 BOOST_FOREACH(const String& dir, m_IncludeSearchDirs) {
139                         String spath = dir + "/" + include;
140
141                         if (Utility::PathExists(spath)) {
142                                 includePath = spath;
143                                 break;
144                         }
145                 }
146         }
147
148         std::vector<Expression *> expressions;
149
150         if (!Utility::Glob(includePath, boost::bind(&ConfigCompiler::CollectIncludes, boost::ref(expressions), _1, m_Zone, m_Module), GlobFile) && includePath.FindFirstOf("*?") == String::NPos) {
151                 std::ostringstream msgbuf;
152                 msgbuf << "Include file '" + include + "' does not exist";
153                 BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), debuginfo));
154         }
155
156         DictExpression *expr = new DictExpression(expressions);
157         expr->MakeInline();
158         return expr;
159 }
160
161 /**
162  * Handles recursive includes.
163  *
164  * @param path The directory path.
165  * @param pattern The file pattern.
166  * @param debuginfo Debug information.
167  */
168 Expression *ConfigCompiler::HandleIncludeRecursive(const String& path, const String& pattern, const DebugInfo&)
169 {
170         String ppath;
171
172         if (path.GetLength() > 0 && path[0] == '/')
173                 ppath = path;
174         else
175                 ppath = Utility::DirName(GetPath()) + "/" + path;
176
177         std::vector<Expression *> expressions;
178         Utility::GlobRecursive(ppath, pattern, boost::bind(&ConfigCompiler::CollectIncludes, boost::ref(expressions), _1, m_Zone, m_Module), GlobFile);
179         return new DictExpression(expressions);
180 }
181
182 void ConfigCompiler::HandleIncludeZone(const String& tag, const String& path, const String& pattern, std::vector<Expression *>& expressions)
183 {
184         String zoneName = Utility::BaseName(path);
185
186         String ppath;
187
188         if (path.GetLength() > 0 && path[0] == '/')
189                 ppath = path;
190         else
191                 ppath = Utility::DirName(GetPath()) + "/" + path;
192
193         RegisterZoneDir(tag, ppath, zoneName);
194
195         Utility::GlobRecursive(ppath, pattern, boost::bind(&ConfigCompiler::CollectIncludes, boost::ref(expressions), _1, zoneName, m_Module), GlobFile);
196 }
197
198 /**
199  * Handles zone includes.
200  *
201  * @param tag The tag name.
202  * @param path The directory path.
203  * @param pattern The file pattern.
204  * @param debuginfo Debug information.
205  */
206 Expression *ConfigCompiler::HandleIncludeZones(const String& tag, const String& path, const String& pattern, const DebugInfo&)
207 {
208         String ppath;
209
210         if (path.GetLength() > 0 && path[0] == '/')
211                 ppath = path;
212         else
213                 ppath = Utility::DirName(GetPath()) + "/" + path;
214
215         std::vector<Expression *> expressions;
216         Utility::Glob(ppath + "/*", boost::bind(&ConfigCompiler::HandleIncludeZone, this, tag, _1, pattern, boost::ref(expressions)), GlobDirectory);
217         return new DictExpression(expressions);
218 }
219
220 /**
221  * Handles the library directive.
222  *
223  * @param library The name of the library.
224  */
225 void ConfigCompiler::HandleLibrary(const String& library)
226 {
227         Loader::LoadExtensionLibrary(library);
228 }
229
230 void ConfigCompiler::CompileHelper(void)
231 {
232         try {
233                 m_Promise.set_value(boost::shared_ptr<Expression>(Compile()));
234         } catch (...) {
235                 m_Promise.set_exception(boost::current_exception());
236         }
237
238         delete this;
239 }
240
241 /**
242  * Compiles a stream.
243  *
244  * @param path A name identifying the stream.
245  * @param stream The input stream.
246  * @returns Configuration items.
247  */
248 Expression *ConfigCompiler::CompileStream(const String& path,
249     std::istream *stream, bool async, const String& zone, const String& module)
250 {
251         CONTEXT("Compiling configuration stream with name '" + path + "'");
252
253         stream->exceptions(std::istream::badbit);
254
255         ConfigCompiler* ctx = new ConfigCompiler(path, stream, zone, module);
256
257         if (async) {
258                 boost::shared_future<boost::shared_ptr<Expression> > ftr = boost::shared_future<boost::shared_ptr<Expression> >(ctx->m_Promise.get_future());
259
260                 Utility::QueueAsyncCallback(boost::bind(&ConfigCompiler::CompileHelper, ctx));
261                 return new FutureExpression(ftr);
262         } else {
263                 Expression *expr;
264
265                 try {
266                         expr = ctx->Compile();
267                 } catch (...) {
268                         delete ctx;
269                         throw;
270                 }
271
272                 delete ctx;
273                 return expr;
274         }
275 }
276
277 /**
278  * Compiles a file.
279  *
280  * @param path The path.
281  * @returns Configuration items.
282  */
283 Expression *ConfigCompiler::CompileFile(const String& path, bool async,
284     const String& zone, const String& module)
285 {
286         CONTEXT("Compiling configuration file '" + path + "'");
287
288         std::ifstream *stream = new std::ifstream();
289         stream->open(path.CStr(), std::ifstream::in);
290
291         if (!*stream)
292                 BOOST_THROW_EXCEPTION(posix_error()
293                         << boost::errinfo_api_function("std::ifstream::open")
294                         << boost::errinfo_errno(errno)
295                         << boost::errinfo_file_name(path));
296
297         Log(LogInformation, "ConfigCompiler")
298             << "Compiling config file: " << path;
299
300         return CompileStream(path, stream, async, zone, module);
301 }
302
303 /**
304  * Compiles a snippet of text.
305  *
306  * @param path A name identifying the text.
307  * @param text The text.
308  * @returns Configuration items.
309  */
310 Expression *ConfigCompiler::CompileText(const String& path, const String& text,
311     bool async, const String& zone, const String& module)
312 {
313         std::stringstream *stream = new std::stringstream(text);
314         return CompileStream(path, stream, async, zone, module);
315 }
316
317 /**
318  * Adds a directory to the list of include search dirs.
319  *
320  * @param dir The new dir.
321  */
322 void ConfigCompiler::AddIncludeSearchDir(const String& dir)
323 {
324         Log(LogInformation, "ConfigCompiler")
325             << "Adding include search dir: " << dir;
326
327         m_IncludeSearchDirs.push_back(dir);
328 }
329
330 std::vector<ZoneFragment> ConfigCompiler::GetZoneDirs(const String& zone)
331 {
332         std::map<String, std::vector<ZoneFragment> >::const_iterator it;
333         it = m_ZoneDirs.find(zone);
334         if (it == m_ZoneDirs.end())
335                 return std::vector<ZoneFragment>();
336         else
337                 return it->second;
338 }
339
340 void ConfigCompiler::RegisterZoneDir(const String& tag, const String& ppath, const String& zoneName)
341 {
342         ZoneFragment zf;
343         zf.Tag = tag;
344         zf.Path = ppath;
345         m_ZoneDirs[zoneName].push_back(zf);
346 }
347
348 const std::vector<String>& ConfigCompiler::GetKeywords(void)
349 {
350         static std::vector<String> keywords;
351
352         if (keywords.empty()) {
353                 keywords.push_back("object");
354                 keywords.push_back("template");
355                 keywords.push_back("include");
356                 keywords.push_back("include_recursive");
357                 keywords.push_back("library");
358                 keywords.push_back("null");
359                 keywords.push_back("true");
360                 keywords.push_back("false");
361                 keywords.push_back("const");
362                 keywords.push_back("var");
363                 keywords.push_back("this");
364                 keywords.push_back("globals");
365                 keywords.push_back("locals");
366                 keywords.push_back("use");
367                 keywords.push_back("apply");
368                 keywords.push_back("to");
369                 keywords.push_back("where");
370                 keywords.push_back("import");
371                 keywords.push_back("assign");
372                 keywords.push_back("ignore");
373                 keywords.push_back("function");
374                 keywords.push_back("return");
375                 keywords.push_back("break");
376                 keywords.push_back("continue");
377                 keywords.push_back("for");
378                 keywords.push_back("if");
379                 keywords.push_back("else");
380                 keywords.push_back("while");
381         }
382
383         return keywords;
384 }