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
61using namespace std;
62
63#define MODULE "bes"
64#define prolog std::string("TheBESKeys::").append(__func__).append("() - ")
65
66set<string> TheBESKeys::d_ingested_key_files;
67
68TheBESKeys *TheBESKeys::d_instance = 0;
69string 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
123TheBESKeys::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
132TheBESKeys::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
146void 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
155void 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
174bool 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
206void 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
236void 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
317void 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
340void 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
371void 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
398bool 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
423string 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
450int 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
476void TheBESKeys::dump(ostream &strm) const
477{
478 strm << dump();
479}
480
486string 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
528string 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
555bool 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
674bool TheBESKeys::using_dynamic_config(){
675 return d_dynamic_config_in_use;
676}
677
682void 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