bes  Updated for version 3.20.10
TheBESKeys.cc
1 // TheBESKeys.cc
2 
3 // This file is part of bes, A C++ back-end server implementation framework
4 // for the OPeNDAP Data Access Protocol.
5 
6 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
7 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 // You can contact University Corporation for Atmospheric Research at
24 // 3080 Center Green Drive, Boulder, CO 80301
25 
26 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
28 //
29 // Authors:
30 // pwest Patrick West <pwest@ucar.edu>
31 // jgarcia Jose Garcia <jgarcia@ucar.edu>
32 
33 #include "config.h"
34 
35 #if HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 
39 #include <cerrno>
40 #include <cstring>
41 
42 #include <string>
43 #include <vector>
44 #include <map>
45 #include <sstream>
46 
47 #include "BESDebug.h"
48 #include "TheBESKeys.h"
49 #include "kvp_utils.h"
50 #include "BESUtil.h"
51 #include "BESRegex.h"
52 #include "BESFSDir.h"
53 #include "BESFSFile.h"
54 #include "BESInternalFatalError.h"
55 #include "BESInternalError.h"
56 #include "BESSyntaxUserError.h"
57 #include "BESLog.h"
58 
59 #define BES_INCLUDE_KEY "BES.Include"
60 
61 using namespace std;
62 
63 #define MODULE "bes"
64 #define prolog std::string("TheBESKeys::").append(__func__).append("() - ")
65 
66 set<string> TheBESKeys::d_ingested_key_files;
67 
68 TheBESKeys *TheBESKeys::d_instance = 0;
69 string TheBESKeys::ConfigFile = "";
70 
72 {
73  if (d_instance) return d_instance;
74 
75  if (!TheBESKeys::ConfigFile.empty()) {
76  d_instance = new TheBESKeys(TheBESKeys::ConfigFile);
77  return d_instance;
78  }
79 
80  // d_instance is a nullptr and TheBESKeys::ConfigFile is ""
81  // so lets try some obvious places...
82 
83  string try_ini = "/usr/local/etc/bes/bes.conf";
84  if (access(try_ini.c_str(), R_OK) == 0) {
85  TheBESKeys::ConfigFile = try_ini;
86  d_instance = new TheBESKeys(TheBESKeys::ConfigFile);
87  return d_instance;
88  }
89 
90  try_ini = "/etc/bes/bes.conf";
91  if (access(try_ini.c_str(), R_OK) == 0) {
92  TheBESKeys::ConfigFile = try_ini;
93  d_instance = new TheBESKeys(TheBESKeys::ConfigFile);
94  return d_instance;
95  }
96 
97  try_ini = "/usr/etc/bes/bes.conf";
98  if (access(try_ini.c_str(), R_OK) == 0) {
99  TheBESKeys::ConfigFile = try_ini;
100  d_instance = new TheBESKeys(TheBESKeys::ConfigFile);
101  return d_instance;
102 }
103  throw BESInternalFatalError("Unable to locate a BES configuration file.", __FILE__, __LINE__);
104 }
105 
122 TheBESKeys::TheBESKeys(const string &keys_file_name) :
123  d_keys_file_name(keys_file_name), d_the_keys(0), d_the_original_keys(0), d_dynamic_config_in_use(false), d_own_keys(true)
124 {
125  d_the_keys = new map<string, vector<string> >;
126  d_the_original_keys = new map<string, vector<string> >;
127  initialize_keys();
128 }
129 
130 #if 0
131 TheBESKeys::TheBESKeys(const string &keys_file_name, map<string, vector<string> > *keys) :
132  d_keys_file_name(keys_file_name), d_the_keys(keys), d_the_original_keys(0), d_dynamic_config_in_use(false), d_own_keys(false)
133 {
134  initialize_keys();
135 }
136 #endif
137 
141 {
142  clean();
143 }
144 
145 void TheBESKeys::initialize_keys()
146 {
147  kvp::load_keys(d_keys_file_name, d_ingested_key_files, *d_the_keys);
148  *d_the_original_keys = *d_the_keys;
149  BESDEBUG(MODULE, prolog << " d_keys_file_name: " << d_keys_file_name << endl);
150  BESDEBUG(MODULE, prolog << " d_the_keys.size(): " << d_the_keys->size() << endl);
151  BESDEBUG(MODULE, prolog << "d_the_original_keys.size(): " << d_the_original_keys->size() << endl);
152 }
153 
154 void TheBESKeys::clean()
155 {
156 
157  if (d_the_keys && d_own_keys) {
158  delete d_the_keys;
159  d_the_keys = 0;
160  }
161  if(d_the_original_keys){
162  delete d_the_original_keys;
163  d_the_original_keys = 0;
164  }
165 }
166 
174 bool TheBESKeys::LoadedKeys(const string &key_file)
175 {
176 #if 0
177  vector<string>::const_iterator i = TheBESKeys::d_ingested_key_files.begin();
178  vector<string>::const_iterator e = TheBESKeys::d_ingested_key_files.end();
179  for (; i != e; i++) {
180  if ((*i) == key_file) {
181  return true;
182  }
183  }
184 #endif
185  set<string>::iterator it = d_ingested_key_files.find(key_file);
186 
187  return it != d_ingested_key_files.end();
188 }
189 
206 void TheBESKeys::set_key(const string &key, const string &val, bool addto)
207 {
208  map<string, vector<string> >::iterator i;
209  i = d_the_keys->find(key);
210  if (i == d_the_keys->end()) {
211  vector<string> vals;
212  (*d_the_keys)[key] = vals;
213  }
214  if (!addto) (*d_the_keys)[key].clear();
215  if (!val.empty()) {
216  (*d_the_keys)[key].push_back(val);
217  }
218 }
219 
236 void TheBESKeys::set_keys(const string &key, const vector<string> &values, bool addto)
237 {
238  map<string, vector<string> >::iterator i;
239  i = d_the_keys->find(key);
240  if (i == d_the_keys->end()) {
241  vector<string> vals;
242  (*d_the_keys)[key] = vals;
243  }
244  if (!addto) (*d_the_keys)[key].clear();
245 
246  size_t j;
247  for(j = 0; j!=values.size(); j++){
248  if (!values[j].empty()) {
249  (*d_the_keys)[key].push_back(values[j]);
250  }
251  }
252 }
253 
254 
273  const string &key,
274  const map<string, string> &values,
275  const bool case_insensitive_map_keys,
276  bool addto)
277 {
278  map<string, vector<string> >::iterator i;
279  i = d_the_keys->find(key);
280  if (i == d_the_keys->end()) {
281  vector<string> vals;
282  (*d_the_keys)[key] = vals;
283  }
284  if (!addto) {
285  (*d_the_keys)[key].clear();
286  }
287 
288  map<string, string>::const_iterator mit;
289  for(mit = values.begin(); mit!=values.end(); mit++){
290  string map_key = mit->first;
291  if(map_key.empty() ){
292  BESDEBUG(MODULE, prolog << "The map_key is empty. SKIPPING." << endl);
293  }
294  else {
295  if(case_insensitive_map_keys){
296  map_key = BESUtil::lowercase(map_key);
297  }
298  string map_record=map_key+":"+mit->second;
299  (*d_the_keys)[key].push_back(map_record);
300  }
301  }
302 }
303 
304 
305 
317 void TheBESKeys::set_key(const string &pair)
318 {
319  string key;
320  string val;
321  bool addto = false;
322  kvp::break_pair(pair.c_str(), key, val, addto);
323  set_key(key, val, addto);
324 }
325 
340 void TheBESKeys::get_value(const string &s, string &val, bool &found)
341 {
342  found = false;
343  map<string, vector<string> >::iterator i;
344  i = d_the_keys->find(s);
345  if (i != d_the_keys->end()) {
346  found = true;
347  if ((*i).second.size() > 1) {
348  string err = string("Multiple values for the key ") + s + " found, should only be one.";
349  throw BESInternalError(err, __FILE__, __LINE__);
350  }
351  if ((*i).second.size() == 1) {
352  val = (*i).second[0];
353  }
354  else {
355  val = "";
356  }
357  }
358 }
359 
371 void TheBESKeys::get_values(const string& s, vector<string> &vals, bool &found)
372 {
373  found = false;
374  map<string, vector<string> >::iterator i;
375  i = d_the_keys->find(s);
376  if (i != d_the_keys->end()) {
377  found = true;
378  vector<string>::iterator j;
379  for(j=(*i).second.begin(); j!=(*i).second.end(); j++){
380  vals.push_back(*j);
381  }
382  // vals = (*i).second; // BUT WHY NOT?
383  }
384 }
385 
398 bool TheBESKeys::read_bool_key(const string &key, bool default_value)
399 {
400  bool found = false;
401  string value;
402  TheBESKeys::TheKeys()->get_value(key, value, found);
403  // 'key' holds the string value at this point if key_found is true
404  if (found) {
405  value = BESUtil::lowercase(value);
406  return (value == "true" || value == "yes"|| value == "on");
407  }
408  else {
409  return default_value;
410  }
411  }
412 
423 string TheBESKeys::read_string_key(const string &key, const string &default_value)
424 {
425  bool found = false;
426  string value;
427  TheBESKeys::TheKeys()->get_value(key, value, found);
428  // 'value' holds the string value at this point if found is true
429  if (found) {
430  if (value[value.length() - 1] == '/') value.erase(value.length() - 1);
431  return value;
432  }
433  else {
434  return default_value;
435  }
436 }
437 
448 int TheBESKeys::read_int_key(const string &key, int default_value)
449 {
450  bool found = false;
451  string value;
452  TheBESKeys::TheKeys()->get_value(key, value, found);
453  // 'key' holds the string value at this point if found is true
454  if (found) {
455  std::istringstream iss(value);
456  int int_val;
457  iss >> int_val;
458  if (iss.eof() || iss.bad() || iss.fail())
459  return default_value;
460  else
461  return int_val;
462  }
463  else {
464  return default_value;
465  }
466 }
467 
474 void TheBESKeys::dump(ostream &strm) const
475 {
476  strm << dump();
477 }
478 
484 string TheBESKeys::dump() const
485 {
486  stringstream ss;
487  ss << BESIndent::LMarg << "BESKeys::dump - (" << (void *) this << ")" << endl;
488  BESIndent::Indent();
489  ss << BESIndent::LMarg << "key file:" << d_keys_file_name << endl;
490 
491 #if 0
492  if (_keys_file && *_keys_file) {
493  strm << BESIndent::LMarg << "key file is valid" << endl;
494  }
495  else {
496  strm << BESIndent::LMarg << "key file is NOT valid" << endl;
497  }
498 #endif
499 
500  if (d_the_keys && d_the_keys->size()) {
501  ss << BESIndent::LMarg << " keys:" << endl;
502  BESIndent::Indent();
503  Keys_citer i = d_the_keys->begin();
504  Keys_citer ie = d_the_keys->end();
505  for (; i != ie; i++) {
506  ss << BESIndent::LMarg << (*i).first << ": " /*<< endl*/;
507  // BESIndent::Indent();
508  vector<string>::const_iterator v = (*i).second.begin();
509  vector<string>::const_iterator ve = (*i).second.end();
510  for (; v != ve; v++) {
511  ss << (*v) << " "; //endl;
512  }
513  ss << endl;
514  //BESIndent::UnIndent();
515  }
516  BESIndent::UnIndent();
517  }
518  else {
519  ss << BESIndent::LMarg << "keys: none" << endl;
520  }
521  BESIndent::UnIndent();
522  return ss.str();
523 }
524 
525 
526 string TheBESKeys::get_as_config() const
527 {
528  stringstream ss;
529  ss << endl;
530  ss << "# TheBESKeys::get_as_config()" << endl;
531  if (d_the_keys && d_the_keys->size()) {
532  Keys_citer i = d_the_keys->begin();
533  Keys_citer ie = d_the_keys->end();
534  for (; i != ie; i++) {
535  string name = (*i).first;
536  vector<string> values = (*i).second;
537  bool first = true;
538  for(string value: values){
539  ss << name << (first?"=":"+=") << value << endl;
540  first = false;
541  }
542  }
543  }
544  else {
545  ss << "# TheBESKeys are empty()" << endl;
546  }
547  return ss.str();
548 }
549 
550 
551 #define MAP_SEPARATOR ":"
552 
553 bool parse_map_record(const string &map_record, const bool &case_insensitive_map_keys, string &key, string &value) {
554  int primary_index = map_record.find(MAP_SEPARATOR);
555  if (primary_index > 0) {
556  key = map_record.substr(0, primary_index);
557  if (case_insensitive_map_keys)
558  key = BESUtil::lowercase(key);
559  value = map_record.substr(primary_index + 1);
560  BESDEBUG(MODULE, prolog << "key: '" << key << "' value: " << value << endl);
561  return true;
562  }
563  return false;
564 }
565 
566 
575  const std::string &key,
576  std::map<std::string,std::string> &map_values,
577  const bool &case_insensitive_map_keys,
578  bool &found){
579 
580  vector<string> values;
581  get_values(key, values, found);
582  if(!found){
583  return;
584  }
585 
586  vector<string>::iterator it;
587  for(it=values.begin(); it!=values.end(); it++){
588  string map_key;
589  string map_value;
590  if(parse_map_record(*it,case_insensitive_map_keys,map_key,map_value)){
591  map_values.insert( std::pair<string,string>(map_key,map_value));
592  }
593  else {
594  BESDEBUG(MODULE, prolog << string("The configuration entry for the ") << key << " was not " <<
595  "formatted as a map record. The offending entry: " << *it << " HAS BEEN SKIPPED." << endl);
596  }
597  }
598 
599 }
600 
601 
610  const std::string &key,
611  std::map< std::string, std::map<std::string,std::vector<std::string> > > &primary_map,
612  const bool &case_insensitive_map_keys,
613  bool &found){
614 
615  BESDEBUG(MODULE, prolog << "BEGIN" << endl);
616  vector<string> values;
617  get_values(key, values, found);
618  if(!found){
619  return;
620  }
621 
622  vector<string>::iterator it;
623  for(it=values.begin(); it!=values.end(); it++){
624  string map_record = *it;
625  string primary_map_key;
626  string primary_map_value;
627  if(parse_map_record(map_record,case_insensitive_map_keys,primary_map_key,primary_map_value)){
628  string secondary_key;
629  string secondary_value;
630  if(parse_map_record(primary_map_value,case_insensitive_map_keys,secondary_key,secondary_value)){
631  map<string, map<string,vector<string>>>::iterator pit;
632  pit = primary_map.find(primary_map_key);
633  if(pit!=primary_map.end()){
634  map<string,vector<string>>::iterator sit;
635  sit = pit->second.find(secondary_key);
636  if(sit!=pit->second.end()){
637  sit->second.push_back(secondary_value);
638  }
639  else {
640  // How to make a vector<string>> and poke in to the secondary_map??
641  vector<string> secondary_map_entry_values;
642  secondary_map_entry_values.push_back(secondary_value);
643  pit->second.insert(pair<string,vector<string>>(secondary_key,secondary_map_entry_values));
644  }
645  }
646  else {
647  // How to make a map<string,vector<string>> and poke in to the primary_map??
648  map<string,vector<string>> secondary_map_entry;
649  vector<string> secondary_map_entry_values;
650  secondary_map_entry_values.push_back(secondary_value);
651  secondary_map_entry.insert(pair<string,vector<string>>(secondary_key,secondary_map_entry_values));
652  primary_map.insert(pair<string, map<string,vector<string>>>(primary_map_key,secondary_map_entry));
653  }
654  }
655  else {
656  // Map entry improperly formatted.
657  BESDEBUG(MODULE, prolog << string("The configuration entry for the ") << key << " was not " <<
658  "formatted as a map record. The offending entry: " << map_record <<
659  " HAS BEEN SKIPPED." << endl);
660  }
661  }
662  else {
663  BESDEBUG(MODULE, prolog << string("The configuration entry for the ") << key << " was not " <<
664  "formatted as a map record. The offending entry: " << map_record <<
665  " HAS BEEN SKIPPED." << endl);
666  }
667  }
668  BESDEBUG(MODULE, prolog << "END" << endl);
669 
670 }
671 
672 bool TheBESKeys::using_dynamic_config(){
673  return d_dynamic_config_in_use;
674 }
675 
676 
681 void TheBESKeys::load_dynamic_config(const string name)
682 {
683 #if DYNAMIC_CONFIG_ENABLED
684 
685  BESDEBUG(MODULE, prolog << "BEGIN" << endl);
686 
687  // Clear the active keys and copy the original keys into
688  // the active keys (resets the keys to 'as read from config files')
689  if( d_dynamic_config_in_use ){
690  BESDEBUG(MODULE, prolog << "Unloading DynamicConfig." << endl);
691  d_the_keys->clear();
692  *d_the_keys = *d_the_original_keys;
693  d_dynamic_config_in_use = false;
694  }
695 
696  map<string, map<string, vector<string>>> dynamic_confg;
697  bool found;
698  get_values(DYNAMIC_CONFIG_KEY, dynamic_confg, true, found);
699  if(!found){
700  BESDEBUG(MODULE, prolog << "Unable to locate " << DYNAMIC_CONFIG_KEY
701  << " in the configuration keys." << endl);
702  return;
703  }
704  BESDEBUG(MODULE, prolog << "Found a " << DYNAMIC_CONFIG_KEY << " in TheBESKeys." << endl);
705 
706  string best_matching_config_name;
707  long longest_match=0;
708  map<string, map<string, vector<string>>>::iterator best_matching_config=dynamic_confg.end();
709 
710  map<string, map<string, vector<string>>>::iterator dcit;
711  for(dcit = dynamic_confg.begin(); dcit != dynamic_confg.end(); dcit++){
712  BESDEBUG(MODULE, prolog << "Processing " << DYNAMIC_CONFIG_KEY << "["<<dcit->first<< "]" << endl);
713 
714  map<string, vector<string>>::iterator rit;
715  rit = dcit->second.find(DC_REGEX_KEY);
716  if(rit==dcit->second.end()){
717  BESDEBUG(MODULE, prolog << "Could not find a " << DC_REGEX_KEY << " (regular expression) for the "
718  << DYNAMIC_CONFIG_KEY << " named: " << dcit->first << " SKIPPING!" << endl);
719  }
720  else {
721  BESDEBUG(MODULE, prolog << "Found " << DC_REGEX_KEY << " vector for "
722  << DYNAMIC_CONFIG_KEY << "["<< dcit->first << "]" << endl);
723  vector<string>::iterator vit;
724  for(vit = rit->second.begin(); vit != rit->second.end(); vit ++){ // For all the regex expressions
725  BESDEBUG(MODULE, prolog << "Processing " << DC_REGEX_KEY << " value '" << *vit << "'" << endl);
726  BESRegex regex((*vit).c_str()); // make BESRegex
727  long match_length = regex.match(name.c_str(),name.size(),0); // Eval match
728 
729  BESDEBUG(MODULE, prolog << "The name '"<< name << (match_length<0?"' does not match ":"' matches ")
730  << "the regular expression: '"<< *vit << "' (match_length: " << match_length << ")" << endl);
731  if(match_length>longest_match){ // Is is a better match?
732  BESDEBUG(MODULE, prolog << "match_length of " << match_length
733  << " is larger than the current longest_match of "<< longest_match << endl);
734 
735  map<string, vector<string>>::iterator cit;
736  cit = dcit->second.find(DC_CONFIG_KEY);
737  if(cit==dcit->second.end() || cit->second.empty()){ // does it have a config?
738  BESDEBUG(MODULE, prolog << "There were no " << DC_CONFIG_KEY
739  << " (configuration) values for the " << DYNAMIC_CONFIG_KEY << " named: "
740  << dcit->first << " SKIPPING!" << endl);
741  }
742  else {
743 
744  best_matching_config = dcit;
745  longest_match = match_length;
746  best_matching_config_name = dcit->first;
747  BESDEBUG(MODULE, prolog << "Found new best " << DYNAMIC_CONFIG_KEY << " match for '" << name
748  << "' " << DYNAMIC_CONFIG_KEY << ": " << best_matching_config_name << endl);
749  }
750  }
751  }
752  }
753  }
754 
755  if( longest_match==0 || best_matching_config==dynamic_confg.end() ){
756  BESDEBUG(MODULE, prolog << "None of the " << DYNAMIC_CONFIG_KEY
757  << " regex patterns matched the name: " << name << endl);
758  return;
759  }
760 
761  {
762  stringstream msg;
763  msg << prolog << "Using " << DYNAMIC_CONFIG_KEY << ":" << best_matching_config_name << " for: " << name << endl;
764  BESDEBUG(MODULE, msg.str());
765  INFO_LOG( msg.str());
766  }
767 
768  // Now load the specific keys from the dynamic config;
769  map<string, vector<string>>::iterator cit;
770  cit = best_matching_config->second.find(DC_CONFIG_KEY);
771  vector<string>::iterator vit;
772  for(vit=cit->second.begin(); vit != cit->second.end(); vit++){
773  // Each value of this vector should be a regular BESKeys kvp. i.e. "BES.LogName=./opendap.log"
774  // Which we just feed into the keys, since we just backed them up...
775  BESDEBUG(MODULE, prolog << "Adding dynamic configuration BES Key: " << *vit << endl);
776  set_key(*vit);
777  }
778  d_dynamic_config_in_use = true;
779 
780  BESDEBUG(MODULE, prolog << "END" << endl);
781 #endif
782 
783  BESDEBUG("bes:keys",dump());
784 
785 }
786 
exception thrown if internal error encountered
exception thrown if an internal error is found and is fatal to the BES
Regular expression matching.
Definition: BESRegex.h:53
int match(const char *s, int len, int pos=0) const
Does the pattern match.
Definition: BESRegex.cc:127
static std::string lowercase(const std::string &s)
Definition: BESUtil.cc:206
mapping of key/value pairs defining different behaviors of an application.
Definition: TheBESKeys.h:85
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:340
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:71
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.
Definition: TheBESKeys.cc:206
int read_int_key(const std::string &key, int default_value)
Read an integer-valued key from the bes.conf file.
Definition: TheBESKeys.cc:448
void get_values(const std::string &s, std::vector< std::string > &vals, bool &found)
Retrieve the values of a given key, if set.
Definition: TheBESKeys.cc:371
void load_dynamic_config(std::string name)
Loads the the applicable dynamic configuration or nothing if no configuration is applicable.
Definition: TheBESKeys.cc:681
virtual std::string dump() const
dumps information about this object
Definition: TheBESKeys.cc:484
static std::string ConfigFile
Definition: TheBESKeys.h:185
bool read_bool_key(const std::string &key, bool default_value)
Read a boolean-valued key from the bes.conf file.
Definition: TheBESKeys.cc:398
std::string read_string_key(const std::string &key, const std::string &default_value)
Read a string-valued key from the bes.conf file.
Definition: TheBESKeys.cc:423
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.
Definition: TheBESKeys.cc:236
virtual ~TheBESKeys()
cleans up the key/value pair mapping
Definition: TheBESKeys.cc:140