]> granicus.if.org Git - icinga2/blob - lib/config/configcompiler.cpp
Fix that relative paths do not work with include_zones
[icinga2] / lib / config / configcompiler.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2016 Icinga Development Team (https://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         String newRelativeBase = relativeBase;
216
217         if (path.GetLength() > 0 && path[0] == '/')
218                 ppath = path;
219         else {
220                 ppath = relativeBase + "/" + path;
221                 newRelativeBase = ".";
222         }
223
224         std::vector<Expression *> expressions;
225         Utility::Glob(ppath + "/*", boost::bind(&ConfigCompiler::HandleIncludeZone, newRelativeBase, tag, _1, pattern, package, boost::ref(expressions)), GlobDirectory);
226         return new DictExpression(expressions);
227 }
228
229 /**
230  * Compiles a stream.
231  *
232  * @param path A name identifying the stream.
233  * @param stream The input stream.
234  * @returns Configuration items.
235  */
236 Expression *ConfigCompiler::CompileStream(const String& path,
237     std::istream *stream, const String& zone, const String& package)
238 {
239         CONTEXT("Compiling configuration stream with name '" + path + "'");
240
241         stream->exceptions(std::istream::badbit);
242
243         ConfigCompiler ctx(path, stream, zone, package);
244
245         try {
246                 return ctx.Compile();
247         } catch (const ScriptError& ex) {
248                 return new ThrowExpression(MakeLiteral(ex.what()), ex.IsIncompleteExpression(), ex.GetDebugInfo());
249         } catch (const std::exception& ex) {
250                 return new ThrowExpression(MakeLiteral(DiagnosticInformation(ex)), false);
251         }
252 }
253
254 /**
255  * Compiles a file.
256  *
257  * @param path The path.
258  * @returns Configuration items.
259  */
260 Expression *ConfigCompiler::CompileFile(const String& path, const String& zone,
261     const String& package)
262 {
263         CONTEXT("Compiling configuration file '" + path + "'");
264
265         std::ifstream stream(path.CStr(), std::ifstream::in);
266
267         if (!stream)
268                 BOOST_THROW_EXCEPTION(posix_error()
269                         << boost::errinfo_api_function("std::ifstream::open")
270                         << boost::errinfo_errno(errno)
271                         << boost::errinfo_file_name(path));
272
273         Log(LogInformation, "ConfigCompiler")
274             << "Compiling config file: " << path;
275
276         return CompileStream(path, &stream, zone, package);
277 }
278
279 /**
280  * Compiles a snippet of text.
281  *
282  * @param path A name identifying the text.
283  * @param text The text.
284  * @returns Configuration items.
285  */
286 Expression *ConfigCompiler::CompileText(const String& path, const String& text,
287     const String& zone, const String& package)
288 {
289         std::stringstream stream(text);
290         return CompileStream(path, &stream, zone, package);
291 }
292
293 /**
294  * Adds a directory to the list of include search dirs.
295  *
296  * @param dir The new dir.
297  */
298 void ConfigCompiler::AddIncludeSearchDir(const String& dir)
299 {
300         Log(LogInformation, "ConfigCompiler")
301             << "Adding include search dir: " << dir;
302
303         m_IncludeSearchDirs.push_back(dir);
304 }
305
306 std::vector<ZoneFragment> ConfigCompiler::GetZoneDirs(const String& zone)
307 {
308         boost::mutex::scoped_lock lock(m_ZoneDirsMutex);
309         std::map<String, std::vector<ZoneFragment> >::const_iterator it = m_ZoneDirs.find(zone);
310         if (it == m_ZoneDirs.end())
311                 return std::vector<ZoneFragment>();
312         else
313                 return it->second;
314 }
315
316 void ConfigCompiler::RegisterZoneDir(const String& tag, const String& ppath, const String& zoneName)
317 {
318         ZoneFragment zf;
319         zf.Tag = tag;
320         zf.Path = ppath;
321
322         boost::mutex::scoped_lock lock(m_ZoneDirsMutex);
323         m_ZoneDirs[zoneName].push_back(zf);
324 }
325