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 std::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 */
ConfigCompiler(String path,std::istream * input,String zone,String package)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 */
~ConfigCompiler()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 */
ReadInput(char * buffer,size_t max_size)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 */
GetScanner() const60 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 */
GetPath() const70 const char *ConfigCompiler::GetPath() const
71 {
72 return m_Path.CStr();
73 }
74
SetZone(const String & zone)75 void ConfigCompiler::SetZone(const String& zone)
76 {
77 m_Zone = zone;
78 }
79
GetZone() const80 String ConfigCompiler::GetZone() const
81 {
82 return m_Zone;
83 }
84
SetPackage(const String & package)85 void ConfigCompiler::SetPackage(const String& package)
86 {
87 m_Package = package;
88 }
89
GetPackage() const90 String ConfigCompiler::GetPackage() const
91 {
92 return m_Package;
93 }
94
CollectIncludes(std::vector<std::unique_ptr<Expression>> & expressions,const String & file,const String & zone,const String & package)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 */
HandleInclude(const String & relativeBase,const String & path,bool search,const String & zone,const String & package,const DebugInfo & debuginfo)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 auto funcCallback = [&expressions, zone, package](const String& file) { CollectIncludes(expressions, file, zone, package); };
140
141 if (!Utility::Glob(includePath, funcCallback, GlobFile) && includePath.FindFirstOf("*?") == String::NPos) {
142 std::ostringstream msgbuf;
143 msgbuf << "Include file '" + path + "' does not exist";
144 BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), debuginfo));
145 }
146
147 std::unique_ptr<DictExpression> expr{new DictExpression(std::move(expressions))};
148 expr->MakeInline();
149 return std::move(expr);
150 }
151
152 /**
153 * Handles recursive includes.
154 *
155 * @param relativeBase The path this include is relative to.
156 * @param path The directory path.
157 * @param pattern The file pattern.
158 * @param debuginfo Debug information.
159 */
HandleIncludeRecursive(const String & relativeBase,const String & path,const String & pattern,const String & zone,const String & package,const DebugInfo &)160 std::unique_ptr<Expression> ConfigCompiler::HandleIncludeRecursive(const String& relativeBase, const String& path,
161 const String& pattern, const String& zone, const String& package, const DebugInfo&)
162 {
163 String ppath;
164
165 if (IsAbsolutePath(path))
166 ppath = path;
167 else
168 ppath = relativeBase + "/" + path;
169
170 std::vector<std::unique_ptr<Expression> > expressions;
171 Utility::GlobRecursive(ppath, pattern, [&expressions, zone, package](const String& file) {
172 CollectIncludes(expressions, file, zone, package);
173 }, GlobFile);
174
175 std::unique_ptr<DictExpression> dict{new DictExpression(std::move(expressions))};
176 dict->MakeInline();
177 return std::move(dict);
178 }
179
HandleIncludeZone(const String & relativeBase,const String & tag,const String & path,const String & pattern,const String & package,std::vector<std::unique_ptr<Expression>> & expressions)180 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)
181 {
182 String zoneName = Utility::BaseName(path);
183
184 String ppath;
185
186 if (IsAbsolutePath(path))
187 ppath = path;
188 else
189 ppath = relativeBase + "/" + path;
190
191 RegisterZoneDir(tag, ppath, zoneName);
192
193 Utility::GlobRecursive(ppath, pattern, [&expressions, zoneName, package](const String& file) {
194 CollectIncludes(expressions, file, zoneName, package);
195 }, GlobFile);
196 }
197
198 /**
199 * Handles zone includes.
200 *
201 * @param relativeBase The path this include is relative to.
202 * @param tag The tag name.
203 * @param path The directory path.
204 * @param pattern The file pattern.
205 * @param debuginfo Debug information.
206 */
HandleIncludeZones(const String & relativeBase,const String & tag,const String & path,const String & pattern,const String & package,const DebugInfo &)207 std::unique_ptr<Expression> ConfigCompiler::HandleIncludeZones(const String& relativeBase, const String& tag,
208 const String& path, const String& pattern, const String& package, const DebugInfo&)
209 {
210 String ppath;
211 String newRelativeBase = relativeBase;
212
213 if (IsAbsolutePath(path))
214 ppath = path;
215 else {
216 ppath = relativeBase + "/" + path;
217 newRelativeBase = ".";
218 }
219
220 std::vector<std::unique_ptr<Expression> > expressions;
221 Utility::Glob(ppath + "/*", [newRelativeBase, tag, pattern, package, &expressions](const String& path) {
222 HandleIncludeZone(newRelativeBase, tag, path, pattern, package, expressions);
223 }, GlobDirectory);
224
225 return std::unique_ptr<Expression>(new DictExpression(std::move(expressions)));
226 }
227
228 /**
229 * Compiles a stream.
230 *
231 * @param path A name identifying the stream.
232 * @param stream The input stream.
233 * @returns Configuration items.
234 */
CompileStream(const String & path,std::istream * stream,const String & zone,const String & package)235 std::unique_ptr<Expression> ConfigCompiler::CompileStream(const String& path,
236 std::istream *stream, const String& zone, const String& package)
237 {
238 CONTEXT("Compiling configuration stream with name '" + path + "'");
239
240 stream->exceptions(std::istream::badbit);
241
242 ConfigCompiler ctx(path, stream, zone, package);
243
244 try {
245 return ctx.Compile();
246 } catch (const ScriptError& ex) {
247 return std::unique_ptr<Expression>(new ThrowExpression(MakeLiteral(ex.what()), ex.IsIncompleteExpression(), ex.GetDebugInfo()));
248 } catch (const std::exception& ex) {
249 return std::unique_ptr<Expression>(new ThrowExpression(MakeLiteral(DiagnosticInformation(ex)), false));
250 }
251 }
252
253 /**
254 * Compiles a file.
255 *
256 * @param path The path.
257 * @returns Configuration items.
258 */
CompileFile(const String & path,const String & zone,const String & package)259 std::unique_ptr<Expression> ConfigCompiler::CompileFile(const String& path, const String& zone,
260 const String& package)
261 {
262 CONTEXT("Compiling configuration file '" + path + "'");
263
264 std::ifstream stream(path.CStr(), std::ifstream::in);
265
266 if (!stream)
267 BOOST_THROW_EXCEPTION(posix_error()
268 << boost::errinfo_api_function("std::ifstream::open")
269 << boost::errinfo_errno(errno)
270 << boost::errinfo_file_name(path));
271
272 Log(LogNotice, "ConfigCompiler")
273 << "Compiling config file: " << path;
274
275 return CompileStream(path, &stream, zone, package);
276 }
277
278 /**
279 * Compiles a snippet of text.
280 *
281 * @param path A name identifying the text.
282 * @param text The text.
283 * @returns Configuration items.
284 */
CompileText(const String & path,const String & text,const String & zone,const String & package)285 std::unique_ptr<Expression> ConfigCompiler::CompileText(const String& path, const String& text,
286 const String& zone, const String& package)
287 {
288 std::stringstream stream(text);
289 return CompileStream(path, &stream, zone, package);
290 }
291
292 /**
293 * Adds a directory to the list of include search dirs.
294 *
295 * @param dir The new dir.
296 */
AddIncludeSearchDir(const String & dir)297 void ConfigCompiler::AddIncludeSearchDir(const String& dir)
298 {
299 Log(LogInformation, "ConfigCompiler")
300 << "Adding include search dir: " << dir;
301
302 m_IncludeSearchDirs.push_back(dir);
303 }
304
GetZoneDirs(const String & zone)305 std::vector<ZoneFragment> ConfigCompiler::GetZoneDirs(const String& zone)
306 {
307 std::unique_lock<std::mutex> lock(m_ZoneDirsMutex);
308 auto it = m_ZoneDirs.find(zone);
309 if (it == m_ZoneDirs.end())
310 return std::vector<ZoneFragment>();
311 else
312 return it->second;
313 }
314
RegisterZoneDir(const String & tag,const String & ppath,const String & zoneName)315 void ConfigCompiler::RegisterZoneDir(const String& tag, const String& ppath, const String& zoneName)
316 {
317 ZoneFragment zf;
318 zf.Tag = tag;
319 zf.Path = ppath;
320
321 std::unique_lock<std::mutex> lock(m_ZoneDirsMutex);
322 m_ZoneDirs[zoneName].push_back(zf);
323 }
324
HasZoneConfigAuthority(const String & zoneName)325 bool ConfigCompiler::HasZoneConfigAuthority(const String& zoneName)
326 {
327 std::vector<ZoneFragment> zoneDirs = m_ZoneDirs[zoneName];
328
329 bool empty = zoneDirs.empty();
330
331 if (!empty) {
332 std::vector<String> paths;
333 paths.reserve(zoneDirs.size());
334
335 for (const ZoneFragment& zf : zoneDirs) {
336 paths.push_back(zf.Path);
337 }
338
339 Log(LogNotice, "ConfigCompiler")
340 << "Registered authoritative config directories for zone '" << zoneName << "': " << Utility::NaturalJoin(paths);
341 }
342
343 return !empty;
344 }
345
346
IsAbsolutePath(const String & path)347 bool ConfigCompiler::IsAbsolutePath(const String& path)
348 {
349 #ifndef _WIN32
350 return (path.GetLength() > 0 && path[0] == '/');
351 #else /* _WIN32 */
352 return !PathIsRelative(path.CStr());
353 #endif /* _WIN32 */
354 }
355
AddImport(const Expression::Ptr & import)356 void ConfigCompiler::AddImport(const Expression::Ptr& import)
357 {
358 m_Imports.push_back(import);
359 }
360
GetImports() const361 std::vector<Expression::Ptr> ConfigCompiler::GetImports() const
362 {
363 return m_Imports;
364 }
365