Configuration.cpp 11.4 KB
Newer Older
1
2
3
4
/*
 * Configuration.cpp
 *
 *  Created on: 13.12.2017
5
 *      Author: Michael Ott (original), Micha Mueller
6
7
8
9
10
 */

#include "Configuration.h"
#include <string>
#include <unistd.h>
11
#include <dlfcn.h>
12
13
14
15
16
17
18

#include <boost/foreach.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <boost/algorithm/string.hpp>

using namespace std;

19
20
21
22
23
24
Configuration::Configuration(const std::string& cfgFilePath) :
		_cfgFilePath(cfgFilePath) {

	if (_cfgFilePath[_cfgFilePath.length()-1] != '/') {
		_cfgFilePath.append("/");
	}
25
26

	//set default values for global variables
27
	_global.validateConfig = false;
Alessio Netti's avatar
Alessio Netti committed
28
	_global.qosLevel = 1;
Alessio Netti's avatar
Alessio Netti committed
29
	_global.daemonize = false;
30
	_global.hierarchy = "";
31
32
33
	_global.brokerHost = "";
	_global.brokerPort = 1883;
	_global.threads	   = 1;
Alessio Netti's avatar
Alessio Netti committed
34
	_global.maxMsgNum  = 0;
35
36
	_global.maxInflightMsgNum = 20;
	_global.maxQueuedMsgNum = 0;
37
38
	_global.logLevelFile = boost::log::trivial::trace;
	_global.logLevelCmd  = boost::log::trivial::info;
39

40
	_global.pluginSettings.sensorPattern = "";
41
42
43
44
45
46
	_global.pluginSettings.mqttPrefix	= "";
	_global.pluginSettings.tempdir		= "./";
	_global.pluginSettings.cacheInterval = 900000;

	_global.restAPISettings.restHost	= "";
	_global.restAPISettings.restPort	= "8000";
47
48
49
}

Configuration::~Configuration() {
50
51
52
53
54
55
56
57
	//close plugins
	for (auto p : _plugins) {
		if (p.configurator) {
			p.destroy(p.configurator);
		}
		if (p.DL) {
			dlclose(p.DL);
		}
58
	}
59
60
}

61
62
bool Configuration::readGlobal() {
	//open file
63
	std::string globalConfig = _cfgFilePath;
Alessio Netti's avatar
Alessio Netti committed
64
	globalConfig.append("dcdbpusher.conf");
65
66

	boost::property_tree::iptree cfg;
67
	//parse to property_tree
68
69
70
	try {
		boost::property_tree::read_info(globalConfig, cfg);
	} catch (boost::property_tree::info_parser_error& e) {
Alessio Netti's avatar
Alessio Netti committed
71
		LOG(error) << "Error when reading dcdbpusher.conf: " << e.what();
72
73
		return false;
	}
74

75
	//read global struct
76
	BOOST_FOREACH(boost::property_tree::iptree::value_type &global, cfg.get_child("global")) {
77
		if (boost::iequals(global.first, "mqttBroker")) {
78
79
80
81
82
83
			_global.brokerHost = global.second.data();
			size_t pos = _global.brokerHost.find(":");
			if (pos != string::npos) {
				_global.brokerPort = stoi(_global.brokerHost.substr(pos+1));
				_global.brokerHost.erase(pos);
			}
84
		} else if (boost::iequals(global.first, "mqttprefix")) {
85
86
87
			_global.pluginSettings.mqttPrefix = global.second.data();
			if (_global.pluginSettings.mqttPrefix[_global.pluginSettings.mqttPrefix.length()-1] != '/') {
				_global.pluginSettings.mqttPrefix.append("/");
88
			}
89
90
		} else if (boost::iequals(global.first, "sensorpattern")) {
			_global.pluginSettings.sensorPattern = global.second.data();
91
92
		} else if (boost::iequals(global.first, "hierarchy")) {
			_global.hierarchy = global.second.data();
93
		} else if (boost::iequals(global.first, "tempdir")) {
94
95
96
			_global.pluginSettings.tempdir = global.second.data();
			if (_global.pluginSettings.tempdir[_global.pluginSettings.tempdir.length()-1] != '/') {
				_global.pluginSettings.tempdir.append("/");
97
			}
Alessio Netti's avatar
Alessio Netti committed
98
99
100
101
		} else if (boost::iequals(global.first, "qosLevel")) {
			_global.qosLevel = stoi(global.second.data());
			if(_global.qosLevel < 0 || _global.qosLevel > 2)
				_global.qosLevel = 1;
102
103
104
105
106
		} else if (boost::iequals(global.first, "maxInflightMsgNum")) {
			_global.maxInflightMsgNum = stoull(global.second.data());
		}else if (boost::iequals(global.first, "maxQueuedMsgNum")) {
			_global.maxQueuedMsgNum = stoull(global.second.data());
		}else if (boost::iequals(global.first, "threads")) {
107
			_global.threads = stoi(global.second.data());
108
		} else if (boost::iequals(global.first, "maxMsgNum")) {
Alessio Netti's avatar
Alessio Netti committed
109
			_global.maxMsgNum = stoi(global.second.data());
110
		} else if (boost::iequals(global.first, "daemonize")) {
Alessio Netti's avatar
Alessio Netti committed
111
		    _global.daemonize = to_bool(global.second.data());
112
		} else if (boost::iequals(global.first, "validateConfig")) {
Alessio Netti's avatar
Alessio Netti committed
113
            _global.validateConfig = to_bool(global.second.data());
114
		} else if (boost::iequals(global.first, "verbosity")) {
115
			_global.logLevelFile = translateLogLevel(stoi(global.second.data()));
116
		} else if (boost::iequals(global.first, "cacheInterval")) {
117
118
			_global.pluginSettings.cacheInterval = stoul(global.second.data());
			_global.pluginSettings.cacheInterval *= 1000;
119
		} else {
120
			LOG(warning) << "  Value \"" << global.first << "\" not recognized. Omitting";
121
122
123
		}
	}

124
125
126
127
128
129
130
131
132
	//read restAPI struct
	BOOST_FOREACH(boost::property_tree::iptree::value_type &global, cfg.get_child("restAPI")) {
		if (boost::iequals(global.first, "address")) {
			_global.restAPISettings.restHost = global.second.data();
			size_t pos = _global.restAPISettings.restHost.find(":");
			if (pos != string::npos) {
				_global.restAPISettings.restPort = _global.restAPISettings.restHost.substr(pos+1);
				_global.restAPISettings.restHost.erase(pos);
			}
133
134
135
136
137
138
		} else if (boost::iequals(global.first, "certificate")) {
			_global.restAPISettings.certificate = global.second.data();
		} else if (boost::iequals(global.first, "privateKey")) {
			_global.restAPISettings.privateKey = global.second.data();
		} else if (boost::iequals(global.first, "dhFile")) {
			_global.restAPISettings.dhFile = global.second.data();
139
140
		} else if (boost::iequals(global.first, "authkey")) {
			//Avoid unnecessary "Value not recognized" message
141
142
143
144
145
		} else {
			LOG(warning) << "  Value \"" << global.first << "\" not recognized. Omitting";
		}
	}

146
147
148
	return true;
}

149
150
151
bool Configuration::readAuthkeys(HttpsServer* server) {
	//open file
	std::string globalConfig = _cfgFilePath;
Alessio Netti's avatar
Alessio Netti committed
152
	globalConfig.append("dcdbpusher.conf");
153
154
155
156
157
158

	boost::property_tree::iptree cfg;
	//parse to property_tree
	try {
		boost::property_tree::read_info(globalConfig, cfg);
	} catch (boost::property_tree::info_parser_error& e) {
Alessio Netti's avatar
Alessio Netti committed
159
		LOG(error) << "Error when reading authkeys from dcdbpusher.conf: " << e.what();
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
		return false;
	}

	//read authkeys
	BOOST_FOREACH(boost::property_tree::iptree::value_type &global, cfg.get_child("restAPI")) {
		if (boost::iequals(global.first, "authkey")) {
#ifdef DEBUG
			LOG(info) << "Authentication token \"" << global.second.data() << "\"";
#endif
			std::bitset<NUM_PERMISSIONS> permissions;
			BOOST_FOREACH(boost::property_tree::iptree::value_type &perm, global.second) {
				if (boost::iequals(perm.first, "GETReq")) {
#ifdef DEBUG
					LOG(info) << "  Permission \"GETReq\"";
#endif
					permissions[GETReq] = true;
				} else if (boost::iequals(perm.first, "PUTReq")) {
#ifdef DEBUG
					LOG(info) << "  Permission \"PUTReq\"";
#endif
					permissions[PUTReq] = true;
				} else {
#ifdef DEBUG
					LOG(warning) << "Permission \"" << perm.first << "\" not recognized. Omitting";
#endif
				}
			}
			if (!server->addAuthkey(authkeyMap_t::value_type(global.second.data(), permissions))) {
#ifdef DEBUG
				LOG(warning) << "Authkey already present!";
#endif
			}
		} else {
			//
		}
	}
	return true;
}

bool Configuration::readPlugins() {
200
	std::string globalConfig = _cfgFilePath;
Alessio Netti's avatar
Alessio Netti committed
201
	globalConfig.append("dcdbpusher.conf");
202
203
204
205
206

	boost::property_tree::iptree cfg;
	try {
		boost::property_tree::read_info(globalConfig, cfg);
	} catch (boost::property_tree::info_parser_error& e) {
Alessio Netti's avatar
Alessio Netti committed
207
		LOG(error) << "Error when reading plugins from dcdbpusher.conf: " << e.what();
208
		return false;
209
210
	}

Alessio Netti's avatar
Alessio Netti committed
211
	MQTTChecker& mqttCheck = MQTTChecker::getInstance();
212
213
214
215
	//read plugins
	BOOST_FOREACH(boost::property_tree::iptree::value_type &plugin, cfg.get_child("plugins")) {
		if (boost::iequals(plugin.first, "plugin")) {
			if (!plugin.second.empty()) {
216
				LOG(info) << "Loading plugin " << plugin.second.data() << "...";
217
				std::string pluginConfig; //path to config file for plugin
218
219
220
221
222
223
				std::string pluginLib = "libdcdbplugin_" + plugin.second.data(); //TODO add version information? //path to the plugin-lib
#if __APPLE__
				pluginLib+= ".dylib";
#else
				pluginLib+= ".so";
#endif
224
225
226
227
228
229
230
231
232

				BOOST_FOREACH(boost::property_tree::iptree::value_type &val, plugin.second) {
					if (boost::iequals(val.first, "path")) {
						std::string path = val.second.data();
						//if path not specified we will look up in the default lib-directories (usr/lib and friends)
						if (path != "") {
							if (path[path.length()-1] != '/') {
								path.append("/");
							}
233
							pluginLib = path + pluginLib;
234
235
236
						}
					} else if (boost::iequals(val.first, "config")) {
						pluginConfig = val.second.data();
Alessio Netti's avatar
Alessio Netti committed
237
						//if config-path not specified we will look for pluginName.conf in the dcdbpusher.conf directory
238
239
240
241
						if (pluginConfig == "") {
							pluginConfig = _cfgFilePath + plugin.second.data() + ".conf";
						}
					} else {
242
						LOG(warning) << "  Value \"" << val.first << "\" not recognized. Omitting";
243
244
245
246
247
					}
				}
				//open plugin
				//dl-code based on http://tldp.org/HOWTO/C++-dlopen/thesolution.html

248
249
				if (FILE *file = fopen(pluginConfig.c_str(), "r")) {
					fclose(file);
250
					dl_t dynLib;
251
					dynLib.id = plugin.second.data();
252
253
254
255
					dynLib.DL = NULL;
					dynLib.configurator = NULL;

					//plugin.conf exists --> open libdcdbplugin_pluginName.so and read config
256
					LOG(info) << pluginConfig << " found";
257
					dynLib.DL = dlopen(pluginLib.c_str(), RTLD_NOW);
258
					if(!dynLib.DL) {
Micha Mueller's avatar
Micha Mueller committed
259
						LOG(error) << "Cannot load " << dynLib.id << "-library: " << dlerror();
260
261
262
263
264
265
266
267
268
						return false;
					}
					//reset errors
					dlerror();

					//set dynLib-struct
					dynLib.create = (create_t*) dlsym(dynLib.DL, "create");
					const char* dlsym_error = dlerror();
					if (dlsym_error) {
Micha Mueller's avatar
Micha Mueller committed
269
						LOG(error) << "Cannot load symbol create for " << dynLib.id << ": " << dlsym_error;
270
271
272
273
274
275
						return false;
					}

					dynLib.destroy = (destroy_t*) dlsym(dynLib.DL, "destroy");
					dlsym_error = dlerror();
					if (dlsym_error) {
Micha Mueller's avatar
Micha Mueller committed
276
						LOG(error) << "Cannot load symbol destroy for " << dynLib.id << ": " << dlsym_error;
277
278
279
280
						return false;
					}

					dynLib.configurator = dynLib.create();
281
					//set prefix to global prefix (may be overwritten)
282
					dynLib.configurator->setGlobalSettings(_global.pluginSettings);
283
					//read in config
284
					if (!(dynLib.configurator->readConfig(pluginConfig))) {
285
						LOG(error) << "Plugin " << dynLib.id << " could not read configuration!";
286
287
						return false;
					}
288

289
290
					// returning an empty vector may indicate problems with the config file
					if(dynLib.configurator->getSensorGroups().size() == 0) {
291
						LOG(warning) << "Plugin " << dynLib.id << " created no sensors!";
292
293
					}

294
					//check if an MQTT-suffix was assigned twice
Alessio Netti's avatar
Alessio Netti committed
295
					bool validTopics=true;
Alessio Netti's avatar
Alessio Netti committed
296
297
298
299
300
301
302
					for(const auto& g : dynLib.configurator->getSensorGroups()) {
						if (!mqttCheck.checkGroup(g->getGroupName()))
							validTopics = false;
						for (const auto &s : g->getSensors())
                            if (!mqttCheck.checkTopic(s->getMqtt()) || !mqttCheck.checkName(s->getName()))
                                validTopics = false;
					}
Alessio Netti's avatar
Alessio Netti committed
303
304

					if(!validTopics) {
Alessio Netti's avatar
Alessio Netti committed
305
						LOG(error) << "Problematic MQTT topics or sensor names, please check your config files!";
Alessio Netti's avatar
Alessio Netti committed
306
						return false;
307
308
309
310
					}

					//save dl-struct
					_plugins.push_back(dynLib);
311
					LOG(info) << "Plugin " << dynLib.id << " " << dynLib.configurator->getVersion() << " loaded!";
312
				} else {
313
					LOG(info) << pluginConfig << " not found. Omitting";
314
				}
315
316
			}
		}
317
	}
318
	return true;
319
320
}

321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
boost::log::trivial::severity_level Configuration::translateLogLevel(int logLevel) {
	switch (logLevel) {
		case 0:
			return boost::log::trivial::fatal;
			break;
		case 1:
			return boost::log::trivial::error;
			break;
		case 2:
			return boost::log::trivial::warning;
			break;
		case 3:
			return boost::log::trivial::info;
			break;
		case 4:
			return boost::log::trivial::debug;
			break;
		case 5:
			return boost::log::trivial::trace;
			break;
		default:
			return boost::log::trivial::info;
			break;
	}
}

347
global_t& Configuration::getGlobal() {
348
349
350
	return _global;
}

351
352
pluginVector_t& Configuration::getPlugins() {
	return _plugins;
353
}