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
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 throw BESInternalFatalError("Unable to locate a BES configuration file.", __FILE__, __LINE__);
104}
105
122TheBESKeys::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
131TheBESKeys::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
145void 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
154void 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
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 if (value[value.length() - 1] == '/') value.erase(value.length() - 1);
431 return value;
432 }
433 else {
434 return default_value;
435 }
436}
437
448int 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
474void TheBESKeys::dump(ostream &strm) const
475{
476 strm << dump();
477}
478
484string 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
526string 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
553bool 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
672bool TheBESKeys::using_dynamic_config(){
673 return d_dynamic_config_in_use;
674}
675
676
681void 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