26 #include "yaml_node.h" 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> 49 #define YAML_FILE_REGEX "^[a-zA-Z0-9_-]+\\.yaml$" 64 current_ = nodes_.end();
71 std::map<std::string, std::shared_ptr<YamlConfigurationNode>> &nodes)
72 : first_(true), nodes_(nodes)
74 current_ = nodes_.begin();
85 return (current_ != nodes_.end());
91 return (current_ != nodes_.end());
97 if (current_ == nodes_.end()) {
98 throw Exception(
"YamlValueIterator: cannot get path of invalid iterator");
100 return current_->first.c_str();
106 if (current_ == nodes_.end()) {
107 throw Exception(
"YamlValueIterator: cannot get type of invalid iterator");
109 return YamlConfigurationNode::Type::to_string(current_->second->get_type());
115 if (current_ == nodes_.end()) {
116 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
118 return (current_->second->is_type<
float>());
124 if (current_ == nodes_.end()) {
125 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
127 return (current_->second->is_type<
unsigned int>());
133 if (current_ == nodes_.end()) {
134 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
136 return (current_->second->is_type<
int>());
142 if (current_ == nodes_.end()) {
143 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
145 return (current_->second->is_type<
bool>());
151 if (current_ == nodes_.end()) {
152 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
154 return (current_->second->is_type<std::string>());
160 if (current_ == nodes_.end()) {
161 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
163 return current_->second->get_type() == YamlConfigurationNode::Type::SEQUENCE;
169 if (current_ == nodes_.end()) {
170 throw Exception(
"YamlValueIterator: cannot check type on invalid iterator");
172 if (current_->second->get_type() != YamlConfigurationNode::Type::SEQUENCE) {
173 throw Exception(
"YamlValueIterator: cannot get list size of non-list value");
175 return current_->second->get_list_size();
181 if (current_ == nodes_.end()) {
182 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
184 return current_->second->get_value<
float>();
190 if (current_ == nodes_.end()) {
191 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
193 return current_->second->get_value<
unsigned int>();
199 if (current_ == nodes_.end()) {
200 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
202 return current_->second->get_value<
int>();
208 if (current_ == nodes_.end()) {
209 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
211 return current_->second->get_value<
bool>();
217 if (current_ == nodes_.end()) {
218 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
220 return current_->second->get_value<std::string>();
226 if (current_ == nodes_.end()) {
227 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
229 if (current_->second->get_type() == YamlConfigurationNode::Type::SEQUENCE) {
230 return current_->second->get_list_as_string();
232 return current_->second->get_value<std::string>();
239 if (current_ == nodes_.end()) {
240 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
242 return current_->second->get_list<
float>();
245 std::vector<unsigned int>
248 if (current_ == nodes_.end()) {
249 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
251 return current_->second->get_list<
unsigned int>();
257 if (current_ == nodes_.end()) {
258 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
260 return current_->second->get_list<
int>();
266 if (current_ == nodes_.end()) {
267 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
269 return current_->second->get_list<
bool>();
272 std::vector<std::string>
275 if (current_ == nodes_.end()) {
276 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
278 return current_->second->get_list<std::string>();
290 if (current_ == nodes_.end()) {
291 throw Exception(
"YamlValueIterator: cannot get value of invalid iterator");
293 return current_->second->is_default();
306 write_pending_ =
false;
307 write_pending_mutex_ =
new Mutex();
326 write_pending_ =
false;
327 write_pending_mutex_ =
new Mutex();
329 sysconfdir_ = strdup(sysconfdir);
331 if (userconfdir != NULL) {
332 userconfdir_ = strdup(userconfdir);
334 const char *homedir = getenv(
"HOME");
335 if (homedir == NULL) {
336 userconfdir_ = strdup(sysconfdir);
338 if (asprintf(&userconfdir_,
"%s/%s", homedir, USERDIR) == -1) {
339 userconfdir_ = strdup(sysconfdir);
348 if (write_pending_) {
363 delete write_pending_mutex_;
369 if (file_path == NULL) {
370 file_path =
"config.yaml";
373 std::string filename;
374 if (file_path[0] ==
'/') {
375 filename = file_path;
377 const char *try_paths[] = {userconfdir_, sysconfdir_};
378 int try_paths_len = 2;
380 for (
int i = 0; i < try_paths_len; ++i) {
382 if (asprintf(&path,
"%s/%s", try_paths[i], file_path) != -1) {
383 if (access(path, R_OK) == 0) {
391 if (filename ==
"") {
392 throw Exception(
"YamlConfig: cannot find configuration file %s/%s or %s/%s",
400 config_file_ = filename;
403 std::list<std::string> files, dirs;
404 read_yaml_config(filename, host_file_, root_, host_root_, files, dirs);
409 fam->add_filter(
"^[^.].*\\.yaml$");
410 std::list<std::string>::iterator f;
411 for (f = files.begin(); f != files.end(); ++f) {
413 fam->watch_file(f->c_str());
415 for (f = dirs.begin(); f != dirs.end(); ++f) {
417 fam->watch_dir(f->c_str());
419 fam->add_listener(
this);
420 fam_thread_->
start();
426 std::shared_ptr<YamlConfigurationNode>
427 YamlConfiguration::read_yaml_file(std::string filename,
429 std::queue<LoadQueueEntry> &load_queue,
430 std::string & host_file)
432 if (access(filename.c_str(), R_OK) == -1) {
433 if (ignore_missing) {
436 throw Exception(errno,
"YamlConfig: cannot access file %s", filename.c_str());
439 std::vector<YAML::Node> docs;
440 bool have_doc1 =
false, have_doc2 =
false;
443 docs = YAML::LoadAllFromFile(filename);
444 have_doc1 = docs.size() > 0;
445 have_doc2 = docs.size() > 1;
446 }
catch (YAML::ParserException &e) {
447 throw CouldNotOpenConfigException(
"Failed to parse %s line %i column %i: %s",
454 std::shared_ptr<YamlConfigurationNode> sub_root;
459 }
else if (have_doc1 && have_doc2) {
461 read_meta_doc(docs[0], load_queue, host_file);
462 sub_root = read_config_doc(docs[1]);
466 sub_root = read_config_doc(docs[0]);
473 YamlConfiguration::read_yaml_config(std::string filename,
474 std::string & host_file,
475 std::shared_ptr<YamlConfigurationNode> &root,
476 std::shared_ptr<YamlConfigurationNode> &host_root,
477 std::list<std::string> & files,
478 std::list<std::string> & dirs)
480 root = std::make_shared<YamlConfigurationNode>();
482 std::queue<LoadQueueEntry> load_queue;
483 load_queue.push(LoadQueueEntry(filename,
false));
485 while (!load_queue.empty()) {
486 LoadQueueEntry &qe = load_queue.front();
489 dirs.push_back(qe.filename);
495 std::shared_ptr<YamlConfigurationNode> sub_root =
496 read_yaml_file(qe.filename, qe.ignore_missing, load_queue, host_file);
499 files.push_back(qe.filename);
507 if (host_file !=
"") {
510 std::queue<LoadQueueEntry> host_load_queue;
511 host_root = read_yaml_file(host_file,
true, host_load_queue, host_file);
512 if (!host_load_queue.empty()) {
513 throw CouldNotOpenConfigException(
"YamlConfig: includes are not allowed " 518 files.push_back(host_file);
520 host_root = std::make_shared<YamlConfigurationNode>();
523 host_root = std::make_shared<YamlConfigurationNode>();
532 std::string host_file =
"";
533 std::list<std::string> files, dirs;
534 std::shared_ptr<YamlConfigurationNode> root, host_root;
535 read_yaml_config(config_file_, host_file, root, host_root, files, dirs);
537 std::list<std::string> changes = YamlConfigurationNode::diff(root_, root);
539 if (!changes.empty()) {
541 host_root_ = host_root;
542 host_file_ = host_file;
544 std::list<std::string>::iterator c;
545 for (c = changes.begin(); c != changes.end(); ++c) {
556 std::list<std::string>::iterator f;
557 for (f = files.begin(); f != files.end(); ++f) {
558 fam->watch_file(f->c_str());
560 for (f = dirs.begin(); f != dirs.end(); ++f) {
561 fam->watch_dir(f->c_str());
565 LibLogger::log_warn(
"YamlConfiguration",
"Failed to reload changed config, exception follows");
579 if (path[0] ==
'/') {
582 return std::string(CONFDIR) +
"/" + path;
587 YamlConfiguration::read_meta_doc(YAML::Node & doc,
588 std::queue<LoadQueueEntry> &load_queue,
589 std::string & host_file)
592 const YAML::Node &includes = doc[
"include"];
593 for (YAML::const_iterator it = includes.begin(); it != includes.end(); ++it) {
594 std::string include = it->as<std::string>();
595 bool ignore_missing =
false;
596 if (it->Tag() ==
"tag:fawkesrobotics.org,cfg/ignore-missing") {
597 ignore_missing =
true;
600 if (it->Tag() ==
"tag:fawkesrobotics.org,cfg/host-specific") {
601 if (host_file !=
"") {
602 throw Exception(
"YamlConfig: Only one host-specific file can be specified");
608 if (include.empty()) {
609 throw Exception(
"YamlConfig: invalid empty include");
612 if (include[include.size() - 1] ==
'/') {
615 struct stat dir_stat;
616 if ((stat(dirname.c_str(), &dir_stat) != 0)) {
619 throw Exception(errno,
"YamlConfig: Failed to stat directory %s", dirname.c_str());
622 if (!S_ISDIR(dir_stat.st_mode)) {
623 throw Exception(
"YamlConfig: %s is not a directory", dirname.c_str());
626 DIR *d = opendir(dirname.c_str());
628 throw Exception(errno,
"YamlConfig: failed to open directory %s", dirname.c_str());
631 load_queue.push(LoadQueueEntry(dirname, ignore_missing,
true));
633 std::list<std::string> files;
635 std::regex yaml_regex{YAML_REGEX, std::regex_constants::extended};
638 while ((dent = readdir(d)) != NULL) {
639 if (regex_search(dent->d_name, yaml_regex)) {
640 std::string dn = dent->d_name;
641 files.push_back(dirname + dn);
647 for (std::list<std::string>::iterator f = files.begin(); f != files.end(); ++f) {
648 load_queue.push(LoadQueueEntry(*f, ignore_missing));
652 load_queue.push(LoadQueueEntry(
abs_cfg_path(include), ignore_missing));
655 }
catch (YAML::KeyNotFound &e) {
660 std::shared_ptr<YamlConfigurationNode>
661 YamlConfiguration::read_config_doc(
const YAML::Node &doc)
663 return YamlConfigurationNode::create(doc);
667 YamlConfiguration::write_host_file()
669 if (host_file_ ==
"") {
670 throw Exception(
"YamlConfig: no host config file specified");
674 host_root_->emit(host_file_);
677 write_pending_mutex_->
unlock();
682 write_pending_mutex_->
lock();
683 write_pending_ =
true;
684 write_pending_mutex_->
unlock();
698 std::shared_ptr<YamlConfigurationNode> n = root_->find(path);
699 return !n->has_children();
708 std::shared_ptr<YamlConfigurationNode> n = root_->find(path);
709 if (n->has_children()) {
713 return YamlConfigurationNode::Type::to_string(n->get_type());
729 template <
typename T>
731 get_value_as(std::shared_ptr<YamlConfigurationNode> root,
const char *path)
733 std::shared_ptr<YamlConfigurationNode> n = root->find(path);
734 if (n->has_children()) {
737 return n->get_value<T>();
747 template <
typename T>
748 static inline std::vector<T>
749 get_list(std::shared_ptr<YamlConfigurationNode> root,
const char *path)
751 std::shared_ptr<YamlConfigurationNode> n = root->find(path);
752 if (n->has_children()) {
755 return n->get_list<T>();
761 return get_value_as<float>(root_, path);
767 return get_value_as<unsigned int>(root_, path);
773 return get_value_as<int>(root_, path);
779 return get_value_as<bool>(root_, path);
785 return get_value_as<std::string>(root_, path);
791 return get_list<float>(root_, path);
794 std::vector<unsigned int>
797 return get_list<unsigned int>(root_, path);
803 return get_list<int>(root_, path);
809 return get_list<bool>(root_, path);
812 std::vector<std::string>
815 return get_list<std::string>(root_, path);
823 template <
typename T>
825 is_type(std::shared_ptr<YamlConfigurationNode> root,
const char *path)
827 std::shared_ptr<YamlConfigurationNode> n = root->find(path);
828 if (n->has_children()) {
831 return n->is_type<T>();
837 return is_type<float>(root_, path);
843 std::shared_ptr<YamlConfigurationNode> n = root_->find(path);
844 if (n->has_children()) {
848 if (!n->is_type<
unsigned int>())
851 int v = n->get_value<
int>();
858 return is_type<int>(root_, path);
864 return is_type<bool>(root_, path);
870 return is_type<std::string>(root_, path);
876 std::shared_ptr<YamlConfigurationNode> n = root_->find(path);
877 if (n->has_children()) {
880 return (n->get_type() == YamlConfigurationNode::Type::SEQUENCE);
899 std::shared_ptr<YamlConfigurationNode> n = root_->find(path);
900 if (n->has_children()) {
903 std::map<std::string, std::shared_ptr<YamlConfigurationNode>> nodes;
914 root_->set_value(path, f);
915 host_root_->set_value(path, f);
923 root_->set_value(path, uint);
924 host_root_->set_value(path, uint);
932 root_->set_value(path, i);
933 host_root_->set_value(path, i);
941 root_->set_value(path, b);
942 host_root_->set_value(path, b);
950 root_->set_value(path, std::string(s));
951 host_root_->set_value(path, std::string(s));
965 root_->set_list(path, f);
966 host_root_->set_list(path, f);
974 root_->set_list(path, u);
975 host_root_->set_list(path, u);
983 root_->set_list(path, i);
984 host_root_->set_list(path, i);
992 root_->set_list(path, b);
993 host_root_->set_list(path, b);
1001 root_->set_list(path, s);
1002 host_root_->set_list(path, s);
1010 root_->set_list(path, s);
1011 host_root_->set_list(path, s);
1029 host_root_->erase(path);
1114 write_pending_mutex_->
lock();
1115 if (write_pending_) {
1116 host_root_->emit(host_file_);
1117 write_pending_ =
false;
1119 write_pending_mutex_->
unlock();
1131 std::map<std::string, std::shared_ptr<YamlConfigurationNode>> nodes;
1132 root_->enum_leafs(nodes);
1139 std::string tmp_path = path;
1140 std::string::size_type tl = tmp_path.length();
1141 if ((tl > 0) && (tmp_path[tl - 1] ==
'/')) {
1142 tmp_path.resize(tl - 1);
1145 std::shared_ptr<YamlConfigurationNode> n = root_->find(tmp_path.c_str());
1146 std::map<std::string, std::shared_ptr<YamlConfigurationNode>> nodes;
1147 n->enum_leafs(nodes, tmp_path);
1159 std::shared_ptr<YamlConfigurationNode>
1160 YamlConfiguration::query(
const char *path)
const 1163 return root_->find(pel_q);
virtual void set_int(const char *path, int i)
Set new value in configuration of type int.
virtual std::vector< int > get_ints() const
Get list of values from configuration which is of type int.
virtual bool is_float() const
Check if current value is a float.
virtual bool is_list() const
Check if a value is a list.
virtual std::string get_string() const
Get string value.
ValueIterator * search(const char *path)
Iterator with search results.
virtual bool is_uint(const char *path)
Check if a value is of type unsigned int.
virtual int get_int() const
Get int value.
virtual float get_float() const
Get float value.
ValueIterator * iterator()
Iterator for all values.
virtual void set_comment(const char *path, std::string &comment)
Set new comment for existing value.
virtual std::vector< float > get_floats() const
Get list of values from configuration which is of type float.
YamlConfiguration()
Constructor.
virtual void erase_default(const char *path)
Erase the given default value from the configuration.
virtual void set_uint(const char *path, unsigned int uint)
Set new value in configuration of type unsigned int.
virtual bool is_list(const char *path)
Check if a value is a list.
static std::vector< T > get_list(std::shared_ptr< YamlConfigurationNode > root, const char *path)
Retrieve value casted to given type T.
virtual std::vector< unsigned int > get_uints(const char *path)
Get list of values from configuration which is of type unsigned int.
Fawkes library namespace.
void unlock()
Unlock the mutex.
virtual void set_strings(const char *path, std::vector< std::string > &s)
Set new value in configuration of type string.
Called method has not been implemented.
Thrown if a config entry could not be found.
virtual bool valid() const
Check if the current element is valid.
virtual void set_string(const char *path, std::string &s)
Set new value in configuration of type string.
void lock()
Lock the config.
virtual bool is_bool(const char *path)
Check if a value is of type bool.
virtual bool is_uint() const
Check if current value is a unsigned int.
virtual void set_default_comment(const char *path, const char *comment)
Set new default comment for existing default configuration value.
static bool is_type(std::shared_ptr< YamlConfigurationNode > root, const char *path)
Check if value is of given type T.
virtual bool is_int(const char *path)
Check if a value is of type int.
RefPtr< FileAlterationMonitor > get_fam()
Get FileAlterationMonitor.
virtual std::vector< std::string > get_strings(const char *path)
Get list of values 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 bool is_float(const char *path)
Check if a value is of type float.
virtual bool is_bool() const
Check if current value is a bool.
virtual std::vector< unsigned int > get_uints() const
Get list of values from configuration which is of type unsigned int.
static std::queue< std::string > str_split_to_queue(const std::string &s, char delim='/')
Split string by delimiter.
static T get_value_as(std::shared_ptr< YamlConfigurationNode > root, const char *path)
Retrieve value casted to given type T.
virtual std::string get_comment() const
Get comment of value.
virtual ValueIterator * get_value(const char *path)
Get value from configuration.
Iterator for YAML config trees.
virtual int get_int(const char *path)
Get value from configuration which is of type int.
virtual void load(const char *file_path)
Load configuration.
virtual std::vector< int > get_ints(const char *path)
Get list of values from configuration which is of type int.
static std::string abs_cfg_path(const std::string &path)
Create absolute config path.
YamlValueIterator()
Constructor.
void reset()
Reset pointer.
virtual std::vector< bool > get_bools() const
Get list of values from configuration which is of type bool.
virtual std::string get_string(const char *path)
Get value from configuration which is of type string.
virtual unsigned int get_uint(const char *path)
Get value from configuration which is of type unsigned int.
virtual const char * path() const
Path of value.
virtual std::string get_type(const char *path)
Get type of value at given path.
Base class for exceptions in Fawkes.
virtual std::string get_as_string() const
Get value as string.
virtual size_t get_list_size() const
Get number of elements in list value.
virtual void set_bool(const char *path, bool b)
Set new value in configuration of type bool.
virtual void set_default_uint(const char *path, unsigned int uint)
Set new default value in configuration of type unsigned int.
void unlock()
Unlock the config.
virtual bool get_bool() const
Get bool value.
virtual void set_bools(const char *path, std::vector< bool > &b)
Set new value in configuration of type bool.
virtual ~YamlConfiguration()
Destructor.
static void log_warn(const char *component, const char *format,...)
Log warning message.
bool try_lock()
Tries to lock the mutex.
virtual void set_float(const char *path, float f)
Set new value in configuration of type float.
virtual unsigned int get_uint() const
Get unsigned int value.
void cancel()
Cancel a thread.
virtual void try_dump()
Try to dump configuration.
bool try_lock()
Try to lock the config.
virtual void set_floats(const char *path, std::vector< float > &f)
Set new value in configuration of type float.
virtual bool next()
Check if there is another element and advance to this if possible.
void notify_handlers(const char *path, bool comment_changed=false)
Notify handlers for given path.
RefPtr<> is a reference-counting shared smartpointer.
virtual std::string get_comment(const char *path)
Get comment of value at given path.
virtual bool is_string(const char *path)
Check if a value is of type string.
virtual bool is_default() const
Check if current value was read from the default config.
virtual bool exists(const char *path)
Check if a given value exists.
virtual void set_default_string(const char *path, std::string &s)
Set new default value in configuration of type string.
virtual void set_ints(const char *path, std::vector< int > &i)
Set new value in configuration of type int.
Iterator interface to iterate over config values.
virtual std::vector< std::string > get_strings() const
Get list of values from configuration which is of type string.
virtual bool get_bool(const char *path)
Get value from configuration which is of type bool.
virtual void set_uints(const char *path, std::vector< unsigned int > &uint)
Set new value in configuration of type unsigned int.
virtual void erase(const char *path)
Erase the given value from the configuration.
virtual void set_default_bool(const char *path, bool b)
Set new default 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.
void join()
Join the thread.
virtual void set_default_int(const char *path, int i)
Set new default value in configuration of type int.
virtual std::string get_default_comment(const char *path)
Get comment of value at given path.
virtual std::vector< float > get_floats(const char *path)
Get list of values from configuration which is of type float.
FileAlterationMonitor thread wrapper.
void lock()
Lock this mutex.
virtual bool is_string() const
Check if current value is a string.
virtual void fam_event(const char *filename, unsigned int mask)
Event has been raised.
virtual float get_float(const char *path)
Get value from configuration which is of type float.
virtual bool is_int() const
Check if current value is a int.
Mutex mutual exclusion lock.
virtual void copy(Configuration *copyconf)
Copies all values from the given configuration.
virtual std::vector< bool > get_bools(const char *path)
Get list of values from configuration which is of type bool.
Interface for configuration handling.
virtual const char * type() const
Type of value.
void start(bool wait=true)
Call this method to start the thread.