28#include <core/exceptions/software.h>
29#include <core/threading/mutex.h>
30#include <core/threading/mutex_locker.h>
31#include <logging/liblogger.h>
32#include <sys/socket.h>
34#include <utils/misc/string_split.h>
35#include <utils/system/fam_thread.h>
36#include <yaml-cpp/exceptions.h>
50#define YAML_FILE_REGEX "^[a-zA-Z0-9_-]+\\.yaml$"
65 current_ = nodes_.end();
72 std::map<std::string, std::shared_ptr<YamlConfigurationNode>> &nodes)
73: first_(true), nodes_(nodes)
75 current_ = nodes_.begin();
86 return (current_ != nodes_.end());
92 return (current_ != nodes_.end());
98 if (current_ == nodes_.end()) {
99 throw Exception(
"YamlValueIterator: cannot get path of invalid iterator");
101 return current_->first.c_str();
107 if (current_ == nodes_.end()) {
108 throw Exception(
"YamlValueIterator: cannot get type of invalid iterator");
110 return YamlConfigurationNode::Type::to_string(current_->second->get_type());
116 if (current_ == nodes_.end()) {
117 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
119 return (current_->second->is_type<
float>());
125 if (current_ == nodes_.end()) {
126 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
128 return (current_->second->is_type<
unsigned int>());
134 if (current_ == nodes_.end()) {
135 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
137 return (current_->second->is_type<
int>());
143 if (current_ == nodes_.end()) {
144 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
146 return (current_->second->is_type<
bool>());
152 if (current_ == nodes_.end()) {
153 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
155 return (current_->second->is_type<std::string>());
161 if (current_ == nodes_.end()) {
162 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
164 return current_->second->get_type() == YamlConfigurationNode::Type::SEQUENCE;
170 if (current_ == nodes_.end()) {
171 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
173 if (current_->second->get_type() != YamlConfigurationNode::Type::SEQUENCE) {
174 throw Exception(
"YamlValueIterator: cannot get list size of non-list value");
176 return current_->second->get_list_size();
182 if (current_ == nodes_.end()) {
183 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
185 return current_->second->get_value<
float>();
191 if (current_ == nodes_.end()) {
192 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
194 return current_->second->get_value<
unsigned int>();
200 if (current_ == nodes_.end()) {
201 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
203 return current_->second->get_value<
int>();
209 if (current_ == nodes_.end()) {
210 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
212 return current_->second->get_value<
bool>();
218 if (current_ == nodes_.end()) {
219 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
221 return current_->second->get_value<std::string>();
227 if (current_ == nodes_.end()) {
228 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
230 if (current_->second->get_type() == YamlConfigurationNode::Type::SEQUENCE) {
231 return current_->second->get_list_as_string();
233 return current_->second->get_value<std::string>();
240 if (current_ == nodes_.end()) {
241 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
243 return current_->second->get_list<
float>();
246std::vector<unsigned int>
249 if (current_ == nodes_.end()) {
250 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
252 return current_->second->get_list<
unsigned int>();
258 if (current_ == nodes_.end()) {
259 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
261 return current_->second->get_list<
int>();
267 if (current_ == nodes_.end()) {
268 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
270 return current_->second->get_list<
bool>();
273std::vector<std::string>
276 if (current_ == nodes_.end()) {
277 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
279 return current_->second->get_list<std::string>();
291 if (current_ == nodes_.end()) {
292 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
294 return current_->second->is_default();
307 write_pending_ =
false;
308 write_pending_mutex_ =
new Mutex();
327 write_pending_ =
false;
328 write_pending_mutex_ =
new Mutex();
330 sysconfdir_ = strdup(sysconfdir);
332 if (userconfdir != NULL) {
333 userconfdir_ = strdup(userconfdir);
335 const char *homedir = getenv(
"HOME");
336 if (homedir == NULL) {
337 userconfdir_ = strdup(sysconfdir);
339 if (asprintf(&userconfdir_,
"%s/%s", homedir, USERDIR) == -1) {
340 userconfdir_ = strdup(sysconfdir);
349 if (write_pending_) {
364 delete write_pending_mutex_;
370 if (file_path == NULL) {
371 file_path =
"config.yaml";
374 std::string filename;
375 if (file_path[0] ==
'/') {
376 filename = file_path;
378 const char *try_paths[] = {userconfdir_, sysconfdir_};
379 int try_paths_len = 2;
381 for (
int i = 0; i < try_paths_len; ++i) {
383 if (asprintf(&path,
"%s/%s", try_paths[i], file_path) != -1) {
384 if (access(path, R_OK) == 0) {
392 if (filename ==
"") {
393 throw Exception(
"YamlConfig: cannot find configuration file %s/%s or %s/%s",
401 config_file_ = filename;
404 std::list<std::string> files, dirs;
405 read_yaml_config(filename, host_file_, root_, host_root_, files, dirs);
410 fam->add_filter(
"^[^.].*\\.yaml$");
411 std::list<std::string>::iterator f;
412 for (f = files.begin(); f != files.end(); ++f) {
414 fam->watch_file(f->c_str());
416 for (f = dirs.begin(); f != dirs.end(); ++f) {
418 fam->watch_dir(f->c_str());
420 fam->add_listener(
this);
421 fam_thread_->
start();
427std::shared_ptr<YamlConfigurationNode>
428YamlConfiguration::read_yaml_file(std::string filename,
430 std::queue<LoadQueueEntry> &load_queue,
431 std::string & host_file)
433 if (access(filename.c_str(), R_OK) == -1) {
434 if (ignore_missing) {
437 throw Exception(errno,
"YamlConfig: cannot access file %s", filename.c_str());
440 std::vector<YAML::Node> docs;
441 bool have_doc1 =
false, have_doc2 =
false;
444 docs = YAML::LoadAllFromFile(filename);
445 have_doc1 = docs.size() > 0;
446 have_doc2 = docs.size() > 1;
447 }
catch (YAML::ParserException &e) {
448 throw CouldNotOpenConfigException(
"Failed to parse %s line %i column %i: %s",
455 std::shared_ptr<YamlConfigurationNode> sub_root;
460 }
else if (have_doc1 && have_doc2) {
462 read_meta_doc(docs[0], load_queue, host_file);
463 sub_root = read_config_doc(docs[1]);
467 sub_root = read_config_doc(docs[0]);
474YamlConfiguration::read_yaml_config(std::string filename,
475 std::string & host_file,
476 std::shared_ptr<YamlConfigurationNode> &root,
477 std::shared_ptr<YamlConfigurationNode> &host_root,
478 std::list<std::string> & files,
479 std::list<std::string> & dirs)
481 root = std::make_shared<YamlConfigurationNode>();
483 std::queue<LoadQueueEntry> load_queue;
484 load_queue.push(LoadQueueEntry(filename,
false));
486 while (!load_queue.empty()) {
487 LoadQueueEntry &qe = load_queue.front();
490 dirs.push_back(qe.filename);
496 std::shared_ptr<YamlConfigurationNode> sub_root =
497 read_yaml_file(qe.filename, qe.ignore_missing, load_queue, host_file);
500 files.push_back(qe.filename);
508 if (host_file !=
"") {
511 std::queue<LoadQueueEntry> host_load_queue;
512 host_root = read_yaml_file(host_file,
true, host_load_queue, host_file);
513 if (!host_load_queue.empty()) {
514 throw CouldNotOpenConfigException(
"YamlConfig: includes are not allowed "
519 files.push_back(host_file);
521 host_root = std::make_shared<YamlConfigurationNode>();
524 host_root = std::make_shared<YamlConfigurationNode>();
533 std::string host_file =
"";
534 std::list<std::string> files, dirs;
535 std::shared_ptr<YamlConfigurationNode> root, host_root;
536 read_yaml_config(config_file_, host_file, root, host_root, files, dirs);
538 std::list<std::string> changes = YamlConfigurationNode::diff(root_, root);
540 if (!changes.empty()) {
542 host_root_ = host_root;
543 host_file_ = host_file;
545 std::list<std::string>::iterator c;
546 for (c = changes.begin(); c != changes.end(); ++c) {
557 std::list<std::string>::iterator f;
558 for (f = files.begin(); f != files.end(); ++f) {
559 fam->watch_file(f->c_str());
561 for (f = dirs.begin(); f != dirs.end(); ++f) {
562 fam->watch_dir(f->c_str());
566 LibLogger::log_warn(
"YamlConfiguration",
"Failed to reload changed config, exception follows");
580 if (path[0] ==
'/') {
583 return std::string(CONFDIR) +
"/" + path;
594 const std::string to_replace =
"$host";
595 static char * hostname = NULL;
596 if (hostname == NULL) {
597 hostname =
new char[256];
598 gethostname(hostname, 256);
600 size_t repl_position = prelim.find(to_replace);
601 if (repl_position == std::string::npos) {
604 return prelim.replace(repl_position, to_replace.length(), std::string(hostname));
609YamlConfiguration::read_meta_doc(YAML::Node & doc,
610 std::queue<LoadQueueEntry> &load_queue,
611 std::string & host_file)
614 const YAML::Node &includes = doc[
"include"];
615 for (YAML::const_iterator it = includes.begin(); it != includes.end(); ++it) {
617 bool ignore_missing =
false;
618 if (it->Tag() ==
"tag:fawkesrobotics.org,cfg/ignore-missing") {
619 ignore_missing =
true;
622 if (it->Tag() ==
"tag:fawkesrobotics.org,cfg/host-specific") {
623 if (host_file !=
"") {
624 throw Exception(
"YamlConfig: Only one host-specific file can be specified");
631 if (include.empty()) {
632 throw Exception(
"YamlConfig: invalid empty include");
635 if (include[include.size() - 1] ==
'/') {
638 struct stat dir_stat;
639 if ((stat(dirname.c_str(), &dir_stat) != 0)) {
642 throw Exception(errno,
"YamlConfig: Failed to stat directory %s", dirname.c_str());
645 if (!S_ISDIR(dir_stat.st_mode)) {
646 throw Exception(
"YamlConfig: %s is not a directory", dirname.c_str());
649 DIR *d = opendir(dirname.c_str());
651 throw Exception(errno,
"YamlConfig: failed to open directory %s", dirname.c_str());
654 load_queue.push(LoadQueueEntry(dirname, ignore_missing,
true));
656 std::list<std::string> files;
658 std::regex yaml_regex{YAML_REGEX, std::regex_constants::extended};
661 while ((dent = readdir(d)) != NULL) {
662 if (regex_search(dent->d_name, yaml_regex)) {
663 std::string dn = dent->d_name;
664 files.push_back(dirname + dn);
670 for (std::list<std::string>::iterator f = files.begin(); f != files.end(); ++f) {
671 load_queue.push(LoadQueueEntry(*f, ignore_missing));
675 load_queue.push(LoadQueueEntry(
abs_cfg_path(include), ignore_missing));
678 }
catch (YAML::KeyNotFound &e) {
683std::shared_ptr<YamlConfigurationNode>
684YamlConfiguration::read_config_doc(
const YAML::Node &doc)
686 return YamlConfigurationNode::create(doc);
690YamlConfiguration::write_host_file()
692 if (host_file_ ==
"") {
693 throw Exception(
"YamlConfig: no host config file specified");
697 host_root_->emit(host_file_);
700 write_pending_mutex_->
unlock();
705 write_pending_mutex_->
lock();
706 write_pending_ =
true;
707 write_pending_mutex_->
unlock();
721 std::shared_ptr<YamlConfigurationNode> n = root_->find(path);
722 return !n->has_children();
731 std::shared_ptr<YamlConfigurationNode> n = root_->find(path);
732 if (n->has_children()) {
736 return YamlConfigurationNode::Type::to_string(n->get_type());
754get_value_as(std::shared_ptr<YamlConfigurationNode> root,
const char *path)
756 std::shared_ptr<YamlConfigurationNode> n = root->find(path);
757 if (n->has_children()) {
760 return n->get_value<T>();
771static inline std::vector<T>
772get_list(std::shared_ptr<YamlConfigurationNode> root,
const char *path)
774 std::shared_ptr<YamlConfigurationNode> n = root->find(path);
775 if (n->has_children()) {
778 return n->get_list<T>();
784 return get_value_as<float>(root_, path);
790 return get_value_as<unsigned int>(root_, path);
796 return get_value_as<int>(root_, path);
802 return get_value_as<bool>(root_, path);
808 return get_value_as<std::string>(root_, path);
814 return get_list<float>(root_, path);
817std::vector<unsigned int>
820 return get_list<unsigned int>(root_, path);
826 return get_list<int>(root_, path);
832 return get_list<bool>(root_, path);
835std::vector<std::string>
838 return get_list<std::string>(root_, path);
848is_type(std::shared_ptr<YamlConfigurationNode> root,
const char *path)
850 std::shared_ptr<YamlConfigurationNode> n = root->find(path);
851 if (n->has_children()) {
854 return n->is_type<T>();
860 return is_type<float>(root_, path);
866 std::shared_ptr<YamlConfigurationNode> n = root_->find(path);
867 if (n->has_children()) {
871 if (!n->is_type<
unsigned int>())
874 int v = n->get_value<
int>();
881 return is_type<int>(root_, path);
887 return is_type<bool>(root_, path);
893 return is_type<std::string>(root_, path);
899 std::shared_ptr<YamlConfigurationNode> n = root_->find(path);
900 if (n->has_children()) {
903 return (n->get_type() == YamlConfigurationNode::Type::SEQUENCE);
922 std::shared_ptr<YamlConfigurationNode> n = root_->find(path);
923 if (n->has_children()) {
926 std::map<std::string, std::shared_ptr<YamlConfigurationNode>> nodes;
937 root_->set_value(path, f);
938 host_root_->set_value(path, f);
946 root_->set_value(path, uint);
947 host_root_->set_value(path, uint);
955 root_->set_value(path, i);
956 host_root_->set_value(path, i);
964 root_->set_value(path, b);
965 host_root_->set_value(path, b);
973 root_->set_value(path, std::string(s));
974 host_root_->set_value(path, std::string(s));
988 root_->set_list(path, f);
989 host_root_->set_list(path, f);
997 root_->set_list(path, u);
998 host_root_->set_list(path, u);
1006 root_->set_list(path, i);
1007 host_root_->set_list(path, i);
1015 root_->set_list(path, b);
1016 host_root_->set_list(path, b);
1024 root_->set_list(path, s);
1025 host_root_->set_list(path, s);
1033 root_->set_list(path, s);
1034 host_root_->set_list(path, s);
1052 host_root_->erase(path);
1137 write_pending_mutex_->
lock();
1138 if (write_pending_) {
1139 host_root_->emit(host_file_);
1140 write_pending_ =
false;
1142 write_pending_mutex_->
unlock();
1154 std::map<std::string, std::shared_ptr<YamlConfigurationNode>> nodes;
1155 root_->enum_leafs(nodes);
1162 std::string tmp_path = path;
1163 std::string::size_type tl = tmp_path.length();
1164 if ((tl > 0) && (tmp_path[tl - 1] ==
'/')) {
1165 tmp_path.resize(tl - 1);
1168 std::shared_ptr<YamlConfigurationNode> n = root_->find(tmp_path.c_str());
1169 std::map<std::string, std::shared_ptr<YamlConfigurationNode>> nodes;
1170 n->enum_leafs(nodes, tmp_path);
1182std::shared_ptr<YamlConfigurationNode>
1183YamlConfiguration::query(
const char *path)
const
1186 return root_->find(pel_q);
Thrown if a config entry could not be found.
Iterator interface to iterate over config values.
Interface for configuration handling.
void notify_handlers(const char *path, bool comment_changed=false)
Notify handlers for given path.
Base class for exceptions in Fawkes.
FileAlterationMonitor thread wrapper.
RefPtr< FileAlterationMonitor > get_fam()
Get FileAlterationMonitor.
static void log_warn(const char *component, const char *format,...)
Log warning message.
Mutex mutual exclusion lock.
bool try_lock()
Tries to lock the mutex.
void lock()
Lock this mutex.
void unlock()
Unlock the mutex.
Called method has not been implemented.
RefPtr<> is a reference-counting shared smartpointer.
void reset()
Reset pointer.
void start(bool wait=true)
Call this method to start the thread.
void join()
Join the thread.
void cancel()
Cancel a thread.
Iterator for YAML config trees.
virtual int get_int() const
Get int value.
YamlValueIterator()
Constructor.
virtual std::string get_as_string() const
Get value as string.
virtual bool valid() const
Check if the current element is valid.
virtual std::vector< float > get_floats() const
Get list of values from configuration which is of type float.
virtual std::vector< int > get_ints() const
Get list of values from configuration which is of type int.
virtual bool is_uint() const
Check if current value is a unsigned int.
virtual std::vector< bool > get_bools() const
Get list of values from configuration which is of type bool.
virtual std::vector< std::string > get_strings() const
Get list of values from configuration which is of type string.
virtual bool get_bool() const
Get bool value.
virtual bool is_int() const
Check if current value is a int.
virtual bool is_default() const
Check if current value was read from the default config.
virtual std::string get_comment() const
Get comment of value.
virtual const char * path() const
Path of value.
virtual bool is_bool() const
Check if current value is a bool.
virtual bool next()
Check if there is another element and advance to this if possible.
virtual bool is_string() const
Check if current value is a string.
virtual bool is_float() const
Check if current value is a float.
virtual std::vector< unsigned int > get_uints() const
Get list of values from configuration which is of type unsigned int.
virtual const char * type() const
Type of value.
virtual float get_float() const
Get float value.
virtual unsigned int get_uint() const
Get unsigned int value.
virtual bool is_list() const
Check if a value is a list.
virtual size_t get_list_size() const
Get number of elements in list value.
virtual std::string get_string() const
Get string value.
virtual void set_floats(const char *path, std::vector< float > &f)
Set new value in configuration of type float.
virtual void erase(const char *path)
Erase the given value from the configuration.
virtual void set_strings(const char *path, std::vector< std::string > &s)
Set new value in configuration of type string.
virtual std::string get_type(const char *path)
Get type of value at given path.
virtual std::vector< bool > get_bools(const char *path)
Get list of values from configuration which is of type bool.
virtual void set_string(const char *path, std::string &s)
Set new value in configuration of type string.
virtual bool exists(const char *path)
Check if a given value exists.
virtual std::string get_comment(const char *path)
Get comment of value at given path.
virtual void set_uint(const char *path, unsigned int uint)
Set new value in configuration of type unsigned int.
virtual float get_float(const char *path)
Get value from configuration which is of type float.
virtual unsigned int get_uint(const char *path)
Get value from configuration which is of type unsigned int.
virtual void set_uints(const char *path, std::vector< unsigned int > &uint)
Set new value in configuration of type unsigned int.
YamlConfiguration()
Constructor.
ValueIterator * search(const char *path)
Iterator with search results.
virtual void set_ints(const char *path, std::vector< int > &i)
Set new value in configuration of type int.
virtual void fam_event(const char *filename, unsigned int mask)
Event has been raised.
virtual void set_bools(const char *path, std::vector< bool > &b)
Set new value in configuration of type bool.
virtual bool is_int(const char *path)
Check if a value is of type int.
virtual std::string get_string(const char *path)
Get value from configuration which is of type string.
virtual bool is_default(const char *path)
Check if a value was read from the default config.
virtual void try_dump()
Try to dump configuration.
virtual void set_float(const char *path, float f)
Set new value in configuration of type float.
bool try_lock()
Try to lock the config.
virtual bool is_uint(const char *path)
Check if a value is of type unsigned int.
virtual void set_default_comment(const char *path, const char *comment)
Set new default comment for existing default configuration value.
virtual bool is_bool(const char *path)
Check if a value is of type bool.
virtual bool is_list(const char *path)
Check if a value is a list.
void lock()
Lock the config.
virtual void set_int(const char *path, int i)
Set new value in configuration of type int.
virtual void set_default_int(const char *path, int i)
Set new default value in configuration of type int.
virtual void set_default_string(const char *path, std::string &s)
Set new default value in configuration of type string.
virtual ~YamlConfiguration()
Destructor.
virtual std::vector< unsigned int > get_uints(const char *path)
Get list of values from configuration which is of type unsigned int.
virtual int get_int(const char *path)
Get value from configuration which is of type int.
ValueIterator * iterator()
Iterator for all values.
virtual bool get_bool(const char *path)
Get value from configuration which is of type bool.
virtual std::vector< std::string > get_strings(const char *path)
Get list of values from configuration which is of type string.
virtual std::string get_default_comment(const char *path)
Get comment of value at given path.
virtual void load(const char *file_path)
Load configuration.
virtual void set_default_uint(const char *path, unsigned int uint)
Set new default value in configuration of type unsigned int.
virtual ValueIterator * get_value(const char *path)
Get value from configuration.
virtual void set_comment(const char *path, std::string &comment)
Set new comment for existing value.
void unlock()
Unlock the config.
virtual void copy(Configuration *copyconf)
Copies all values from the given configuration.
virtual void set_default_bool(const char *path, bool b)
Set new default value in configuration of type bool.
virtual std::vector< float > get_floats(const char *path)
Get list of values from configuration which is of type float.
virtual bool is_float(const char *path)
Check if a value is of type float.
virtual bool is_string(const char *path)
Check if a value is of type string.
virtual void set_bool(const char *path, bool b)
Set new value in configuration of type bool.
virtual void set_default_float(const char *path, float f)
Set new default value in configuration of type float.
virtual void erase_default(const char *path)
Erase the given default value from the configuration.
virtual std::vector< int > get_ints(const char *path)
Get list of values from configuration which is of type int.
Fawkes library namespace.
static std::string abs_cfg_path(const std::string &path)
Create absolute config path.
static T get_value_as(std::shared_ptr< YamlConfigurationNode > root, const char *path)
Retrieve value casted to given type T.
static std::vector< T > get_list(std::shared_ptr< YamlConfigurationNode > root, const char *path)
Retrieve value casted to given type T.
static std::queue< std::string > str_split_to_queue(const std::string &s, char delim='/')
Split string by delimiter.
static std::string insert_hostname(std::string prelim)
Replace $host in string with hostname.
static bool is_type(std::shared_ptr< YamlConfigurationNode > root, const char *path)
Check if value is of given type T.