Skip to content

Commit ad4722c

Browse files
committed
Add support in config for including files from dir
1 parent 404964a commit ad4722c

9 files changed

+158
-7
lines changed

CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ add_executable(flashmq
7676
acksender.h
7777
bridgeconfig.h
7878
dnsresolver.h
79+
globber.h
7980

8081

8182
mainapp.cpp
@@ -130,6 +131,7 @@ add_executable(flashmq
130131
bridgeconfig.cpp
131132
dnsresolver.cpp
132133
bridgeinfodb.h bridgeinfodb.cpp
134+
globber.cpp
133135

134136
)
135137

FlashMQTests/FlashMQTests.pro

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ SOURCES += tst_maintests.cpp \
6565
../bridgeconfig.cpp \
6666
../dnsresolver.cpp \
6767
../bridgeinfodb.cpp \
68+
../globber.cpp \
6869
conffiletemp.cpp \
6970
dnstests.cpp \
7071
flashmqtempdir.cpp \
@@ -129,6 +130,7 @@ HEADERS += \
129130
../bridgeconfig.h \
130131
../dnsresolver.h \
131132
../bridgeinfodb.h \
133+
../globber.h \
132134
conffiletemp.h \
133135
flashmqtempdir.h \
134136
mainappthread.h \

configfileparser.cpp

+58-7
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ See LICENSE for license details.
1616
#include <fstream>
1717
#include <regex>
1818
#include <sys/stat.h>
19+
#include <filesystem>
1920

2021
#include <openssl/ssl.h>
2122
#include <openssl/err.h>
2223

2324
#include "exceptions.h"
2425
#include "utils.h"
26+
#include "globber.h"
2527

2628

2729
/**
@@ -148,6 +150,7 @@ ConfigFileParser::ConfigFileParser(const std::string &path) :
148150
validKeys.insert("max_outgoing_topic_alias_value");
149151
validKeys.insert("client_max_write_buffer_size");
150152
validKeys.insert("retained_messages_delivery_limit");
153+
validKeys.insert("include_dir");
151154

152155
validListenKeys.insert("port");
153156
validListenKeys.insert("protocol");
@@ -182,10 +185,12 @@ ConfigFileParser::ConfigFileParser(const std::string &path) :
182185
validBridgeKeys.insert("max_incoming_topic_aliases");
183186
}
184187

185-
void ConfigFileParser::loadFile(bool test)
188+
std::list<std::string> ConfigFileParser::readFileRecursively(const std::string &path) const
186189
{
190+
std::list<std::string> lines;
191+
187192
if (path.empty())
188-
return;
193+
return lines;
189194

190195
checkFileExistsAndReadable("application config file", path, 1024*1024*10);
191196

@@ -198,18 +203,64 @@ void ConfigFileParser::loadFile(bool test)
198203
throw ConfigFileException(oss.str());
199204
}
200205

201-
std::list<std::string> lines;
206+
for(std::string line; getline(infile, line ); )
207+
{
208+
lines.push_back(line);
209+
210+
if (strContains(line, "include_dir"))
211+
{
212+
std::smatch matches;
213+
214+
if (std::regex_match(line, matches, key_value_regex))
215+
{
216+
const std::string key = matches[1].str();
217+
const std::string value = matches[2].str();
218+
219+
if (key == "include_dir")
220+
{
221+
Logger *logger = Logger::getInstance();
222+
223+
if (!std::filesystem::is_directory(value))
224+
{
225+
std::ostringstream oss; oss << "Include_dir '" << value << "' is not there or is not a directory";
226+
throw ConfigFileException(oss.str());
227+
}
228+
229+
Globber globber;
230+
std::vector<std::string> files = globber.getGlob(value + "/*.conf");
202231

203-
const std::regex key_value_regex("^([a-zA-Z0-9_\\-]+) +([a-zA-Z0-9_\\-/.:#+]+) *([a-zA-Z0-9_\\-/.:#+]+)?$");
204-
const std::regex block_regex_start("^([a-zA-Z0-9_\\-]+) *\\{$");
205-
const std::regex block_regex_end("^\\}$");
232+
if (files.empty())
233+
{
234+
logger->logf(LOG_WARNING, "Including '%s' yielded 0 files.", value.c_str());
235+
}
236+
237+
for(const std::string &path_from_glob : files)
238+
{
239+
std::list<std::string> newLines = readFileRecursively(path_from_glob);
240+
lines.insert(lines.cend(), newLines.begin(), newLines.end());
241+
}
242+
}
243+
}
244+
}
245+
}
246+
247+
return lines;
248+
}
249+
250+
void ConfigFileParser::loadFile(bool test)
251+
{
252+
if (path.empty())
253+
return;
254+
255+
const std::list<std::string> unprocessed_lines = readFileRecursively(path);
256+
std::list<std::string> lines;
206257

207258
bool inBlock = false;
208259
std::ostringstream oss;
209260
int linenr = 0;
210261

211262
// First parse the file and keep the valid lines.
212-
for(std::string line; getline(infile, line ); )
263+
for (std::string line : unprocessed_lines)
213264
{
214265
trim(line);
215266
linenr++;

configfileparser.h

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ See LICENSE for license details.
1818
#include <memory>
1919
#include <list>
2020
#include <limits>
21+
#include <regex>
2122

2223
#include "settings.h"
2324

@@ -35,6 +36,10 @@ class ConfigFileParser
3536
std::set<std::string> validListenKeys;
3637
std::set<std::string> validBridgeKeys;
3738

39+
const std::regex key_value_regex = std::regex("^([a-zA-Z0-9_\\-]+) +([a-zA-Z0-9_\\-/.:#+]+) *([a-zA-Z0-9_\\-/.:#+]+)?$");
40+
const std::regex block_regex_start = std::regex("^([a-zA-Z0-9_\\-]+) *\\{$");
41+
const std::regex block_regex_end = std::regex("^\\}$");
42+
3843
Settings settings;
3944

4045
bool testKeyValidity(const std::string &key, const std::string &matchKey, const std::set<std::string> &validKeys) const;
@@ -45,6 +50,7 @@ class ConfigFileParser
4550
public:
4651
ConfigFileParser(const std::string &path);
4752
void loadFile(bool test);
53+
std::list<std::string> readFileRecursively(const std::string &path) const;
4854

4955
const Settings &getSettings();
5056
};

globber.cpp

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include "globber.h"
2+
3+
#include <cstring>
4+
#include <stdexcept>
5+
6+
GlobT::GlobT()
7+
{
8+
std::memset(&result, 0, sizeof (glob_t));
9+
}
10+
11+
GlobT::~GlobT()
12+
{
13+
globfree(&result);
14+
}
15+
16+
std::vector<std::string> Globber::getGlob(const std::string &pattern)
17+
{
18+
GlobT result;
19+
20+
const int rc = glob(pattern.c_str(), 0, nullptr, &result.result);
21+
22+
if (!(rc == 0 || rc == GLOB_NOMATCH))
23+
{
24+
throw std::runtime_error("Glob failed");
25+
}
26+
27+
std::vector<std::string> filenames;
28+
29+
for(size_t i = 0; i < result.result.gl_pathc; ++i)
30+
{
31+
filenames.push_back(std::string(result.result.gl_pathv[i]));
32+
}
33+
34+
return filenames;
35+
}

globber.h

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#ifndef GLOBBER_H
2+
#define GLOBBER_H
3+
4+
#include <glob.h>
5+
#include <string>
6+
#include <vector>
7+
8+
class GlobT
9+
{
10+
friend class Globber;
11+
12+
glob_t result;
13+
14+
public:
15+
GlobT();
16+
~GlobT();
17+
};
18+
19+
class Globber
20+
{
21+
public:
22+
23+
std::vector<std::string> getGlob(const std::string &pattern);
24+
};
25+
26+
#endif // GLOBBER_H

man/flashmq.conf.5

+5
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ When having multiple subscribers on a shared subscription (like '$share/myshare/
220220
\fIsender_hash\fR. Selects a receiver deterministically based on the hash of the client ID of the sender. The selected subscriber will depend on how many subscribers there are, so if some disconnect, the distribution will change. Moreover, the selection may also change when FlashMQ cleans up empty spaces in the list of shared subscribers.
221221

222222
Default: \fIround_robin\fR
223+
.TP
224+
\*(T<\fBinclude_dir\fR\*(T> \fI/path/to/dir\fR
225+
Load *.conf files from the specified directory, to merge with the main configuration file.
226+
227+
An error is generated when the directory is not there. This is to protect against running incorrect configurations by accident, when the dir has been renamed, for example.
223228
.SH "LISTEN PARAMETERS"
224229
Listen parameters can only be used within \*(T<listen { }\*(T> blocks.
225230
.TP

man/flashmq.conf.5.dbk5

+12
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,18 @@
451451
</listitem>
452452
</varlistentry>
453453

454+
<varlistentry xml:id="include_dir">
455+
<term><option>include_dir</option> <replaceable>/path/to/dir</replaceable></term>
456+
<listitem>
457+
<para>
458+
Load *.conf files from the specified directory, to merge with the main configuration file.
459+
</para>
460+
<para>
461+
An error is generated when the directory is not there. This is to protect against running incorrect configurations by accident, when the dir has been renamed, for example.
462+
</para>
463+
</listitem>
464+
</varlistentry>
465+
454466
</variablelist>
455467
</refsect1>
456468

man/flashmq.conf.5.html

+12
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,18 @@
506506
</dd>
507507

508508

509+
510+
<dt id="include_dir"><code class="option">include_dir</code> <code class="replaceable">/path/to/dir</code><a class="hash-anchor" href="#include_dir">#</a></dt>
511+
<dd>
512+
<p>
513+
Load *.conf files from the specified directory, to merge with the main configuration file.
514+
</p>
515+
<p>
516+
An error is generated when the directory is not there. This is to protect against running incorrect configurations by accident, when the dir has been renamed, for example.
517+
</p>
518+
</dd>
519+
520+
509521
</dl>
510522
</section><section class="refsect1">
511523
<header><h2>Listen parameters</h2></header>

0 commit comments

Comments
 (0)