bes  Updated for version 3.20.13
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 
104  throw BESInternalFatalError("Unable to locate a BES configuration file.", __FILE__, __LINE__);
105 }
106 
123 TheBESKeys::TheBESKeys(const string &keys_file_name) :
124  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)
125 {
126  d_the_keys = new map<string, vector<string> >;
127  d_the_original_keys = new map<string, vector<string> >;
128  initialize_keys();
129 }
130 
131 #if 0
132 TheBESKeys::TheBESKeys(const string &keys_file_name, map<string, vector<string> > *keys) :
133  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)
134 {
135  initialize_keys();
136 }
137 #endif
138 
142 {
143  clean();
144 }
145 
146 void TheBESKeys::initialize_keys()
147 {
148  kvp::load_keys(d_keys_file_name, d_ingested_key_files, *d_the_keys);
149  *d_the_original_keys = *d_the_keys;
150  BESDEBUG(MODULE, prolog << " d_keys_file_name: " << d_keys_file_name << endl);
151  BESDEBUG(MODULE, prolog << " d_the_keys.size(): " << d_the_keys->size() << endl);
152  BESDEBUG(MODULE, prolog << "d_the_original_keys.size(): " << d_the_original_keys->size() << endl);
153 }
154 
155 void TheBESKeys::clean()
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  // Wrote and used this in place of the more cumbersome if(...) since this
431  // same operation is performed in many places in our software. jhrg 1/26/22
433  return value;
434  }
435  else {
436  return default_value;
437  }
438 }
439 
450 int TheBESKeys::read_int_key(const string &key, int default_value)
451 {
452  bool found = false;
453  string value;
454  TheBESKeys::TheKeys()->get_value(key, value, found);
455  // 'key' holds the string value at this point if found is true
456  if (found) {
457  std::istringstream iss(value);
458  int int_val;
459  iss >> int_val;
460  if (!iss.eof() || iss.bad() || iss.fail())
461  return default_value;
462  else
463  return int_val;
464  }
465  else {
466  return default_value;
467  }
468 }
469 
476 void TheBESKeys::dump(ostream &strm) const
477 {
478  strm << dump();
479 }
480 
486 string TheBESKeys::dump() const
487 {
488  stringstream ss;
489  ss << BESIndent::LMarg << "BESKeys::dump - (" << (void *) this << ")" << endl;
490  BESIndent::Indent();
491  ss << BESIndent::LMarg << "key file:" << d_keys_file_name << endl;
492 
493 #if 0
494  if (_keys_file && *_keys_file) {
495  strm << BESIndent::LMarg << "key file is valid" << endl;
496  }
497  else {
498  strm << BESIndent::LMarg << "key file is NOT valid" << endl;
499  }
500 #endif
501 
502  if (d_the_keys && d_the_keys->size()) {
503  ss << BESIndent::LMarg << " keys:" << endl;
504  BESIndent::Indent();
505  Keys_citer i = d_the_keys->begin();
506  Keys_citer ie = d_the_keys->end();
507  for (; i != ie; i++) {
508  ss << BESIndent::LMarg << (*i).first << ": " /*<< endl*/;
509  // BESIndent::Indent();
510  vector<string>::const_iterator v = (*i).second.begin();
511  vector<string>::const_iterator ve = (*i).second.end();
512  for (; v != ve; v++) {
513  ss << (*v) << " "; //endl;
514  }
515  ss << endl;
516  //BESIndent::UnIndent();
517  }
518  BESIndent::UnIndent();
519  }
520  else {
521  ss << BESIndent::LMarg << "keys: none" << endl;
522  }
523  BESIndent::UnIndent();
524  return ss.str();
525 }
526 
527 
528 string TheBESKeys::get_as_config() const
529 {
530  stringstream ss;
531  ss << endl;
532  ss << "# TheBESKeys::get_as_config()" << endl;
533  if (d_the_keys && d_the_keys->size()) {
534  Keys_citer i = d_the_keys->begin();
535  Keys_citer ie = d_the_keys->end();
536  for (; i != ie; i++) {
537  string name = (*i).first;
538  vector<string> values = (*i).second;
539  bool first = true;
540  for(string value: values){
541  ss << name << (first?"=":"+=") << value << endl;
542  first = false;
543  }
544  }
545  }
546  else {
547  ss << "# TheBESKeys are empty()" << endl;
548  }
549  return ss.str();
550 }
551 
552 
553 #define MAP_SEPARATOR ":"
554 
555 bool parse_map_record(const string &map_record, const bool &case_insensitive_map_keys, string &key, string &value) {
556  int primary_index = map_record.find(MAP_SEPARATOR);
557  if (primary_index > 0) {
558  key = map_record.substr(0, primary_index);
559  if (case_insensitive_map_keys)
560  key = BESUtil::lowercase(key);
561  value = map_record.substr(primary_index + 1);
562  BESDEBUG(MODULE, prolog << "key: '" << key << "' value: " << value << endl);
563  return true;
564  }
565  return false;
566 }
567 
568 
577  const std::string &key,
578  std::map<std::string,std::string> &map_values,
579  const bool &case_insensitive_map_keys,
580  bool &found){
581 
582  vector<string> values;
583  get_values(key, values, found);
584  if(!found){
585  return;
586  }
587 
588  vector<string>::iterator it;
589  for(it=values.begin(); it!=values.end(); it++){
590  string map_key;
591  string map_value;
592  if(parse_map_record(*it,case_insensitive_map_keys,map_key,map_value)){
593  map_values.insert( std::pair<string,string>(map_key,map_value));
594  }
595  else {
596  BESDEBUG(MODULE, prolog << string("The configuration entry for the ") << key << " was not " <<
597  "formatted as a map record. The offending entry: " << *it << " HAS BEEN SKIPPED." << endl);
598  }
599  }
600 
601 }
602 
603 
612  const std::string &key,
613  std::map< std::string, std::map<std::string,std::vector<std::string> > > &primary_map,
614  const bool &case_insensitive_map_keys,
615  bool &found){
616 
617  BESDEBUG(MODULE, prolog << "BEGIN" << endl);
618  vector<string> values;
619  get_values(key, values, found);
620  if(!found){
621  return;
622  }
623 
624  vector<string>::iterator it;
625  for(it=values.begin(); it!=values.end(); it++){
626  string map_record = *it;
627  string primary_map_key;
628  string primary_map_value;
629  if(parse_map_record(map_record,case_insensitive_map_keys,primary_map_key,primary_map_value)){
630  string secondary_key;
631  string secondary_value;
632  if(parse_map_record(primary_map_value,case_insensitive_map_keys,secondary_key,secondary_value)){
633  map<string, map<string,vector<string>>>::iterator pit;
634  pit = primary_map.find(primary_map_key);
635  if(pit!=primary_map.end()){
636  map<string,vector<string>>::iterator sit;
637  sit = pit->second.find(secondary_key);
638  if(sit!=pit->second.end()){
639  sit->second.push_back(secondary_value);
640  }
641  else {
642  // How to make a vector<string>> and poke in to the secondary_map??
643  vector<string> secondary_map_entry_values;
644  secondary_map_entry_values.push_back(secondary_value);
645  pit->second.insert(pair<string,vector<string>>(secondary_key,secondary_map_entry_values));
646  }
647  }
648  else {
649  // How to make a map<string,vector<string>> and poke in to the primary_map??
650  map<string,vector<string>> secondary_map_entry;
651  vector<string> secondary_map_entry_values;
652  secondary_map_entry_values.push_back(secondary_value);
653  secondary_map_entry.insert(pair<string,vector<string>>(secondary_key,secondary_map_entry_values));
654  primary_map.insert(pair<string, map<string,vector<string>>>(primary_map_key,secondary_map_entry));
655  }
656  }
657  else {
658  // Map entry improperly formatted.
659  BESDEBUG(MODULE, prolog << string("The configuration entry for the ") << key << " was not " <<
660  "formatted as a map record. The offending entry: " << map_record <<
661  " HAS BEEN SKIPPED." << endl);
662  }
663  }
664  else {
665  BESDEBUG(MODULE, prolog << string("The configuration entry for the ") << key << " was not " <<
666  "formatted as a map record. The offending entry: " << map_record <<
667  " HAS BEEN SKIPPED." << endl);
668  }
669  }
670  BESDEBUG(MODULE, prolog << "END" << endl);
671 
672 }
673 
674 bool TheBESKeys::using_dynamic_config(){
675  return d_dynamic_config_in_use;
676 }
677 
682 void TheBESKeys::load_dynamic_config(const string name)
683 {
684 #if DYNAMIC_CONFIG_ENABLED
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:254
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)....
Definition: BESUtil.cc:110
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:450
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:682
virtual std::string dump() const
dumps information about this object
Definition: TheBESKeys.cc:486
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:141