42#include <unordered_map>
47#include "TheBESKeys.h"
50#include "BESInternalError.h"
52#define BES_INCLUDE_KEY "BES.Include"
57#define prolog std::string("TheBESKeys::").append(__func__).append("() - ")
66static string get_the_config_filename() {
68 if (config_file.empty()) {
73 vector<string> try_ini{
"/usr/local/etc/bes/bes.conf",
"/etc/bes/bes.conf",
"/usr/etc/bes/bes.conf"};
74 for (
const auto &ini : try_ini) {
75 if (access(ini.c_str(), R_OK) == 0) {
86 static TheBESKeys instance(get_the_config_filename());
106TheBESKeys::TheBESKeys(
string keys_file_name) : d_keys_file_name(std::move(keys_file_name))
108 if (!d_keys_file_name.empty()) {
109 kvp::load_keys(d_keys_file_name, d_ingested_key_files, d_the_keys);
110#if DYNAMIC_CONFIG_ENABLED
111 *d_the_original_keys = d_the_keys;
115 BESDEBUG(MODULE, prolog <<
" d_keys_file_name: " << d_keys_file_name << endl);
116 BESDEBUG(MODULE, prolog <<
" d_the_keys.size(): " << d_the_keys.size() << endl);
117#if DYNAMIC_CONFIG_ENABLED
118 BESDEBUG(MODULE, prolog <<
"d_the_original_keys.size(): " << d_the_original_keys->size() << endl);
129 d_keys_file_name = keys_file_name;
140 d_ingested_key_files.clear();
141 kvp::load_keys(d_keys_file_name, d_ingested_key_files, d_the_keys);
162 auto i = d_the_keys.find(key);
163 if (i == d_the_keys.end()) {
165 d_the_keys[key] = vals;
167 if (!addto) d_the_keys[key].clear();
169 d_the_keys[key].push_back(val);
192 auto i = d_the_keys.find(key);
193 if (i == d_the_keys.end()) {
195 d_the_keys[key] = vals;
197 if (!addto) d_the_keys[key].clear();
199 for(
const auto &value: values) {
200 if (!value.empty()) {
201 d_the_keys[key].push_back(value);
225 const unordered_map<string, string> &values,
226 const bool case_insensitive_map_keys,
229 auto i = d_the_keys.find(key);
230 if (i == d_the_keys.end()) {
232 d_the_keys[key] = vals;
235 d_the_keys[key].clear();
238 for(
auto &p : values){
239 string map_key = p.first;
240 if(map_key.empty() ){
241 BESDEBUG(MODULE, prolog <<
"The map_key is empty. SKIPPING." << endl);
244 if(case_insensitive_map_keys){
247 string map_record=map_key+
":"+p.second;
248 d_the_keys[key].push_back(map_record);
270 kvp::break_pair(pair.c_str(), key, val, addto);
291 const auto &i = d_the_keys.find(key);
292 if (i != d_the_keys.end()) {
294 if (i->second.size() > 1) {
295 string err = string(
"Multiple values for the key ") + key +
" found, there should only be one.";
298 else if (i->second.size() == 1) {
321 const auto &i = d_the_keys.find(key);
322 if (i != d_the_keys.end()) {
324 for (
const auto &value: i->second) {
325 vals.push_back(value);
350 return (value ==
"true" || value ==
"yes"|| value ==
"on");
353 return default_value;
380 return default_value;
401 std::istringstream iss(value);
404 if (!iss.eof() || iss.bad() || iss.fail())
405 return default_value;
410 return default_value;
431 std::istringstream iss(value);
434 if (!iss.eof() || iss.bad() || iss.fail())
435 return default_value;
440 return default_value;
463 std::istringstream iss(value);
466 if (!iss.eof() || iss.bad() || iss.fail())
467 return default_value;
472 return default_value;
491 map<string, vector<std::string>, std::less<> > sorted_keys;
492 for(
const auto &key_entry: d_the_keys){
493 sorted_keys.insert( pair<
string, vector<string> >(key_entry.first,( vector<string>(key_entry.second) )));
497 ss <<
"# TheBESKeys::get_as_config()" << endl;
498 if (!sorted_keys.empty()) {
499 for (
const auto &key_entry : sorted_keys) {
500 string name = key_entry.first;
501 vector<string> values = key_entry.second;
503 for(
const string &value: values){
504 ss << name << (first?
"=":
"+=") << value << endl;
510 ss <<
"# TheBESKeys are empty()" << endl;
515constexpr auto MAP_SEPARATOR =
':';
517bool parse_map_record(
const string &map_record,
const bool &case_insensitive_map_keys,
string &key,
string &value) {
518 size_t primary_index = map_record.find(MAP_SEPARATOR);
519 if (primary_index > 0) {
520 key = map_record.substr(0, primary_index);
521 if (case_insensitive_map_keys)
523 value = map_record.substr(primary_index + 1);
524 BESDEBUG(MODULE, prolog <<
"key: '" << key <<
"' value: " << value << endl);
538 const std::string &key,
539 std::unordered_map<std::string,std::string> &map_values,
540 bool case_insensitive_map_keys,
543 vector<string> values;
549 for (
const auto &value: values) {
552 if (parse_map_record(value, case_insensitive_map_keys, map_key, map_value)) {
553 map_values.insert( std::pair<string,string>(map_key, map_value));
556 BESDEBUG(MODULE, prolog <<
string(
"The configuration entry for the ") << key <<
" was not " <<
557 "formatted as a map record. The offending entry: " << value <<
" HAS BEEN SKIPPED." << endl);
570 const std::string &key,
571 std::unordered_map< std::string, std::unordered_map<std::string,std::vector<std::string> > > &primary_map,
572 bool case_insensitive_map_keys,
575 BESDEBUG(MODULE, prolog <<
"BEGIN" << endl);
576 vector<string> values;
582 for (
const auto &map_record: values) {
583 string primary_map_key;
584 string primary_map_value;
585 if (parse_map_record(map_record, case_insensitive_map_keys, primary_map_key, primary_map_value)) {
586 string secondary_key;
587 string secondary_value;
588 if (parse_map_record(primary_map_value, case_insensitive_map_keys, secondary_key, secondary_value)) {
589 const auto &pit = primary_map.find(primary_map_key);
590 if (pit != primary_map.end()) {
591 const auto &sit = pit->second.find(secondary_key);
592 if (sit != pit->second.end()) {
593 sit->second.push_back(secondary_value);
597 vector<string> secondary_map_entry_values;
598 secondary_map_entry_values.push_back(secondary_value);
599 pit->second.insert(pair<
string, vector<string>>(secondary_key, secondary_map_entry_values));
604 unordered_map< string, vector<string> > secondary_map_entry;
605 vector< string > secondary_map_entry_values;
606 secondary_map_entry_values.push_back(secondary_value);
607 secondary_map_entry.insert(pair<
string, vector<string> >(secondary_key, secondary_map_entry_values));
608 primary_map.insert(pair<
string, unordered_map<
string, vector<string> > >(primary_map_key, secondary_map_entry));
614 BESDEBUG(MODULE, prolog <<
string(
"The configuration entry for the ") << key <<
" was not " <<
615 "formatted as a map record. The offending entry: " << map_record <<
616 " HAS BEEN SKIPPED." << endl);
620 BESDEBUG(MODULE, prolog <<
string(
"The configuration entry for the ") << key <<
" was not " <<
621 "formatted as a map record. The offending entry: " << map_record <<
622 " HAS BEEN SKIPPED." << endl);
625 BESDEBUG(MODULE, prolog <<
"END" << endl);
635#if DYNAMIC_CONFIG_ENABLED
636 BESDEBUG(MODULE, prolog <<
"BEGIN" << endl);
640 if( d_dynamic_config_in_use ){
641 BESDEBUG(MODULE, prolog <<
"Unloading DynamicConfig." << endl);
643 d_the_keys = *d_the_original_keys;
644 d_dynamic_config_in_use =
false;
647 map<string, map<string, vector<string>>> dynamic_confg;
649 get_values(DYNAMIC_CONFIG_KEY, dynamic_confg,
true, found);
651 BESDEBUG(MODULE, prolog <<
"Unable to locate " << DYNAMIC_CONFIG_KEY
652 <<
" in the configuration keys." << endl);
655 BESDEBUG(MODULE, prolog <<
"Found a " << DYNAMIC_CONFIG_KEY <<
" in TheBESKeys." << endl);
657 string best_matching_config_name;
658 long longest_match=0;
659 auto best_matching_config = dynamic_confg.end();
661 for(
auto dcit = dynamic_confg.begin(); dcit != dynamic_confg.end(); dcit++){
662 BESDEBUG(MODULE, prolog <<
"Processing " << DYNAMIC_CONFIG_KEY <<
"["<<dcit->first<<
"]" << endl);
664 auto rit = dcit->second.find(DC_REGEX_KEY);
665 if(rit==dcit->second.end()){
666 BESDEBUG(MODULE, prolog <<
"Could not find a " << DC_REGEX_KEY <<
" (regular expression) for the "
667 << DYNAMIC_CONFIG_KEY <<
" named: " << dcit->first <<
" SKIPPING!" << endl);
670 BESDEBUG(MODULE, prolog <<
"Found " << DC_REGEX_KEY <<
" vector for "
671 << DYNAMIC_CONFIG_KEY <<
"["<< dcit->first <<
"]" << endl);
672 for(
auto vit = rit->second.begin(); vit != rit->second.end(); vit ++){
673 BESDEBUG(MODULE, prolog <<
"Processing " << DC_REGEX_KEY <<
" value '" << *vit <<
"'" << endl);
675 long match_length = regex.
match(name.c_str(),name.size(),0);
677 BESDEBUG(MODULE, prolog <<
"The name '"<< name << (match_length<0?
"' does not match ":
"' matches ")
678 <<
"the regular expression: '"<< *vit <<
"' (match_length: " << match_length <<
")" << endl);
679 if(match_length>longest_match){
680 BESDEBUG(MODULE, prolog <<
"match_length of " << match_length
681 <<
" is larger than the current longest_match of "<< longest_match << endl);
683 auto cit = dcit->second.find(DC_CONFIG_KEY);
684 if(cit==dcit->second.end() || cit->second.empty()){
685 BESDEBUG(MODULE, prolog <<
"There were no " << DC_CONFIG_KEY
686 <<
" (configuration) values for the " << DYNAMIC_CONFIG_KEY <<
" named: "
687 << dcit->first <<
" SKIPPING!" << endl);
691 best_matching_config = dcit;
692 longest_match = match_length;
693 best_matching_config_name = dcit->first;
694 BESDEBUG(MODULE, prolog <<
"Found new best " << DYNAMIC_CONFIG_KEY <<
" match for '" << name
695 <<
"' " << DYNAMIC_CONFIG_KEY <<
": " << best_matching_config_name << endl);
702 if( longest_match==0 || best_matching_config==dynamic_confg.end() ){
703 BESDEBUG(MODULE, prolog <<
"None of the " << DYNAMIC_CONFIG_KEY
704 <<
" regex patterns matched the name: " << name << endl);
710 msg << prolog <<
"Using " << DYNAMIC_CONFIG_KEY <<
":" << best_matching_config_name <<
" for: " << name << endl;
711 BESDEBUG(MODULE, msg.str());
712 INFO_LOG( msg.str());
716 auto cit = best_matching_config->second.find(DC_CONFIG_KEY);
717 for(
auto vit=cit->second.begin(); vit != cit->second.end(); vit++){
720 BESDEBUG(MODULE, prolog <<
"Adding dynamic configuration BES Key: " << *vit << endl);
723 d_dynamic_config_in_use =
true;
725 BESDEBUG(MODULE, prolog <<
"END" << endl);
728 BESDEBUG(
"bes:keys",
dump());
750 ss << BESIndent::LMarg <<
"BESKeys::dump - (" << (
void *)
this <<
")" << endl;
752 ss << BESIndent::LMarg <<
"key file:" << d_keys_file_name << endl;
754 if (!d_the_keys.empty()) {
755 ss << BESIndent::LMarg <<
" keys:" << endl;
757 for (
auto &p: d_the_keys) {
758 ss << BESIndent::LMarg << p.first <<
": ";
759 for (
auto &value: p.second) {
764 BESIndent::UnIndent();
767 ss << BESIndent::LMarg <<
"keys: none" << endl;
769 BESIndent::UnIndent();
exception thrown if internal error encountered
Regular expression matching.
int match(const char *s, int len, int pos=0) const
Does the pattern match.
static std::string lowercase(const std::string &s)
static void trim_if_trailing_slash(std::string &value)
If the string ends in a slash, remove it This function works for empty strings (doing nothing)....
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
std::string get_as_config() const
static TheBESKeys * TheKeys()
Access to the singleton.
void set_key(const std::string &key, const std::string &val, bool addto=false)
allows the user to set key/value pairs from within the application.
static int read_int_key(const std::string &key, int default_value)
Read an integer-valued key from the bes.conf file.
void get_values(const std::string &s, std::vector< std::string > &vals, bool &found)
Retrieve the values of a given key, if set.
virtual std::string dump() const
dumps information about this object
static std::string ConfigFile
static bool read_bool_key(const std::string &key, bool default_value)
Read a boolean-valued key from the bes.conf file.
static std::string read_string_key(const std::string &key, const std::string &default_value)
Read a string-valued key from the bes.conf file.
static unsigned long read_ulong_key(const std::string &key, unsigned long default_value)
Read an integer-valued key from the bes.conf file.
void reload_keys()
Reload the keys. Erase the existing keys and reload them from the file. This uses the name of the key...
void set_keys(const std::string &key, const std::vector< std::string > &values, bool addto)
allows the user to set key/value pairs from within the application.
static uint64_t read_uint64_key(const std::string &key, uint64_t default_value)
Read an integer-valued key from the bes.conf file.
void load_dynamic_config(const std::string &name)
Loads the the applicable dynamic configuration or nothing if no configuration is applicable.