bes  Updated for version 3.20.5
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 <sstream>
44 
45 #include "TheBESKeys.h"
46 #include "BESUtil.h"
47 #include "BESFSDir.h"
48 #include "BESFSFile.h"
49 #include "BESInternalFatalError.h"
50 #include "BESSyntaxUserError.h"
51 
52 #define BES_INCLUDE_KEY "BES.Include"
53 
54 using namespace std;
55 
56 vector<string> TheBESKeys::KeyList;
57 
58 TheBESKeys *TheBESKeys::_instance = 0;
59 string TheBESKeys::ConfigFile = "";
60 
62 {
63  if (_instance) return _instance;
64 
65  if (!TheBESKeys::ConfigFile.empty()) {
66  _instance = new TheBESKeys(TheBESKeys::ConfigFile);
67  return _instance;
68  }
69 
70  // _instance is a nullptr and TheBESKeys::ConfigFile is ""
71  // so lets try some obvious places...
72 
73  string try_ini = "/usr/local/etc/bes/bes.conf";
74  if (access(try_ini.c_str(), R_OK) == 0) {
75  TheBESKeys::ConfigFile = try_ini;
76  _instance = new TheBESKeys(TheBESKeys::ConfigFile);
77  return _instance;
78  }
79 
80  try_ini = "/etc/bes/bes.conf";
81  if (access(try_ini.c_str(), R_OK) == 0) {
82  TheBESKeys::ConfigFile = try_ini;
83  _instance = new TheBESKeys(TheBESKeys::ConfigFile);
84  return _instance;
85  }
86 
87  try_ini = "/usr/etc/bes/bes.conf";
88  if (access(try_ini.c_str(), R_OK) == 0) {
89  TheBESKeys::ConfigFile = try_ini;
90  _instance = new TheBESKeys(TheBESKeys::ConfigFile);
91  return _instance;
92  }
93  throw BESInternalFatalError("Unable to locate a BES configuration file.", __FILE__, __LINE__);
94 }
95 
112 TheBESKeys::TheBESKeys(const string &keys_file_name) :
113  _keys_file(0), _keys_file_name(keys_file_name), _the_keys(0), _own_keys(true)
114 {
115  _the_keys = new map<string, vector<string> >;
116  initialize_keys();
117 }
118 
119 TheBESKeys::TheBESKeys(const string &keys_file_name, map<string, vector<string> > *keys) :
120  _keys_file(0), _keys_file_name(keys_file_name), _the_keys(keys), _own_keys(false)
121 {
122  initialize_keys();
123 }
124 
128 {
129  clean();
130 }
131 
132 void TheBESKeys::initialize_keys()
133 {
134  _keys_file = new ifstream(_keys_file_name.c_str());
135 
136  if (!(*_keys_file)) {
137  char path[500];
138  getcwd(path, sizeof(path));
139  string s = string("Cannot open BES configuration file '") + _keys_file_name + "': ";
140  char *err = strerror(errno);
141  if (err)
142  s += err;
143  else
144  s += "Unknown error";
145 
146  s += (string) ".\n" + "The current working directory is " + path;
147  throw BESInternalFatalError(s, __FILE__, __LINE__);
148  }
149 
150  try {
151  load_keys();
152  }
153  catch (BESError &e) {
154  // be sure we're throwing a fatal error, since the BES can't run
155  // without the configuration file
156  clean();
157  throw BESInternalFatalError(e.get_message(), e.get_file(), e.get_line());
158  }
159  catch (...) {
160  clean();
161  string s = (string) "Undefined exception while trying to load keys from the BES configuration file '"
162  + _keys_file_name + "'";
163  throw BESInternalFatalError(s, __FILE__, __LINE__);
164  }
165 }
166 
167 void TheBESKeys::clean()
168 {
169  if (_keys_file) {
170  _keys_file->close();
171  delete _keys_file;
172  }
173 
174  if (_the_keys && _own_keys) {
175  delete _the_keys;
176  }
177 }
178 
186 bool TheBESKeys::LoadedKeys(const string &key_file)
187 {
188  vector<string>::const_iterator i = TheBESKeys::KeyList.begin();
189  vector<string>::const_iterator e = TheBESKeys::KeyList.end();
190  for (; i != e; i++) {
191  if ((*i) == key_file) {
192  return true;
193  }
194  }
195 
196  return false;
197 }
198 
199 void TheBESKeys::load_keys()
200 {
201  string key, value, line;
202  while (!_keys_file->eof()) {
203  bool addto = false;
204  getline(*_keys_file, line);
205  if (break_pair(line.c_str(), key, value, addto)) {
206  if (key == BES_INCLUDE_KEY) {
207  // We make this call to set_key() and force 'addto' to
208  // be true because we need access to the child configuration
209  // files and their values for the admin interface.
210  set_key(key, value, true);
211  load_include_files(value);
212  }
213  else {
214  set_key(key, value, addto);
215  }
216  }
217  }
218 }
219 
220 // The string contained in the character buffer b should be of the
221 // format key=value or key+=value. The pair is broken apart, storing the
222 // key in the key parameter and the value of the key in the value
223 // parameter. If += is used, then the value should be added to the value
224 // of key, not replacing.
225 //
226 // It used to be that we would validate the key=value line. Instead,
227 // anything after the equal sign is considered the value of the key.
228 inline bool TheBESKeys::break_pair(const char* b, string& key, string &value, bool &addto)
229 {
230  addto = false;
231  // Ignore comments and lines with only spaces
232  if (b && (b[0] != '#') && (!only_blanks(b))) {
233  register size_t l = strlen(b);
234  if (l > 1) {
235  int pos = 0;
236  bool done = false;
237  for (register size_t j = 0; j < l && !done; j++) {
238  if (b[j] == '=') {
239  if (!addto)
240  pos = j;
241  else {
242  if (pos != static_cast<int>(j - 1)) {
243  string s = string("BES: Invalid entry ") + b + " in configuration file " + _keys_file_name
244  + " '+' character found in variable name" + " or attempting '+=' with space"
245  + " between the characters.\n";
246  throw BESInternalFatalError(s, __FILE__, __LINE__);
247  }
248  }
249  done = true;
250  }
251  else if (b[j] == '+') {
252  addto = true;
253  pos = j;
254  }
255  }
256  if (!done) {
257  string s = string("BES: Invalid entry ") + b + " in configuration file " + _keys_file_name + ": "
258  + " '=' character not found.\n";
259  throw BESInternalFatalError(s, __FILE__, __LINE__);
260  }
261 
262  string s = b;
263  key = s.substr(0, pos);
265  if (addto)
266  value = s.substr(pos + 2, s.size());
267  else
268  value = s.substr(pos + 1, s.size());
270 
271  return true;
272  }
273 
274  return false;
275  }
276 
277  return false;
278 }
279 
289 void TheBESKeys::load_include_files(const string &files)
290 {
291  string newdir;
292  BESFSFile allfiles(files);
293 
294  // If the files specified begin with a /, then use that directory
295  // instead of the current keys file directory.
296  if (!files.empty() && files[0] == '/') {
297  newdir = allfiles.getDirName();
298  }
299  else {
300  // determine the directory of the current keys file. All included
301  // files will be relative to this file.
302  BESFSFile currfile(_keys_file_name);
303  string currdir = currfile.getDirName();
304 
305  string alldir = allfiles.getDirName();
306 
307  if ((currdir == "./" || currdir == ".") && (alldir == "./" || alldir == ".")) {
308  newdir = "./";
309  }
310  else {
311  if (alldir == "./" || alldir == ".") {
312  newdir = currdir;
313  }
314  else {
315  newdir = currdir + "/" + alldir;
316  }
317  }
318  }
319 
320  // load the files one at a time. If the directory doesn't exist,
321  // then don't load any configuration files
322  BESFSDir fsd(newdir, allfiles.getFileName());
323  BESFSDir::fileIterator i = fsd.beginOfFileList();
324  BESFSDir::fileIterator e = fsd.endOfFileList();
325  for (; i != e; i++) {
326  load_include_file((*i).getFullPath());
327  }
328 }
329 
336 void TheBESKeys::load_include_file(const string &file)
337 {
338  // make sure the file exists and is readable
339  // throws exception if unable to read
340  // not loaded if has already be started to be loaded
341  if (!TheBESKeys::LoadedKeys(file)) {
342  TheBESKeys::KeyList.push_back(file);
343  TheBESKeys tmp(file, _the_keys);
344  }
345 }
346 
347 bool TheBESKeys::only_blanks(const char *line)
348 {
349  string my_line = line;
350  if (my_line.find_first_not_of(" ") != string::npos)
351  return false;
352  else
353  return true;
354 }
355 
372 void TheBESKeys::set_key(const string &key, const string &val, bool addto)
373 {
374  map<string, vector<string> >::iterator i;
375  i = _the_keys->find(key);
376  if (i == _the_keys->end()) {
377  vector<string> vals;
378  (*_the_keys)[key] = vals;
379  }
380  if (!addto) (*_the_keys)[key].clear();
381  if (!val.empty()) {
382  (*_the_keys)[key].push_back(val);
383  }
384 }
385 
397 void TheBESKeys::set_key(const string &pair)
398 {
399  string key;
400  string val;
401  bool addto = false;
402  break_pair(pair.c_str(), key, val, addto);
403  set_key(key, val, addto);
404 }
405 
420 void TheBESKeys::get_value(const string& s, string &val, bool &found)
421 {
422  found = false;
423  map<string, vector<string> >::iterator i;
424  i = _the_keys->find(s);
425  if (i != _the_keys->end()) {
426  found = true;
427  if ((*i).second.size() > 1) {
428  string err = string("Multiple values for the key ") + s + " found, should only be one.";
429  throw BESSyntaxUserError(err, __FILE__, __LINE__);
430  }
431  if ((*i).second.size() == 1) {
432  val = (*i).second[0];
433  }
434  else {
435  val = "";
436  }
437  }
438 }
439 
451 void TheBESKeys::get_values(const string& s, vector<string> &vals, bool &found)
452 {
453  found = false;
454  map<string, vector<string> >::iterator i;
455  i = _the_keys->find(s);
456  if (i != _the_keys->end()) {
457  found = true;
458  vals = (*i).second;
459  }
460 }
461 
474 bool TheBESKeys::read_bool_key(const string &key, bool default_value)
475 {
476  bool found = false;
477  string value;
478  TheBESKeys::TheKeys()->get_value(key, value, found);
479  // 'key' holds the string value at this point if key_found is true
480  if (found) {
481  value = BESUtil::lowercase(value);
482  return (value == "true" || value == "yes"|| value == "on");
483  }
484  else {
485  return default_value;
486  }
487  }
488 
499 string TheBESKeys::read_string_key(const string &key, const string &default_value)
500 {
501  bool found = false;
502  string value;
503  TheBESKeys::TheKeys()->get_value(key, value, found);
504  // 'value' holds the string value at this point if found is true
505  if (found) {
506  if (value[value.length() - 1] == '/') value.erase(value.length() - 1);
507  return value;
508  }
509  else {
510  return default_value;
511  }
512 }
513 
524 int TheBESKeys::read_int_key(const string &key, int default_value)
525 {
526  bool found = false;
527  string value;
528  TheBESKeys::TheKeys()->get_value(key, value, found);
529  // 'key' holds the string value at this point if found is true
530  if (found) {
531  std::istringstream iss(value);
532  int int_val;
533  iss >> int_val;
534  if (iss.eof() || iss.bad() || iss.fail())
535  return default_value;
536  else
537  return int_val;
538  }
539  else {
540  return default_value;
541  }
542 }
543 
550 void TheBESKeys::dump(ostream &strm) const
551 {
552  strm << BESIndent::LMarg << "BESKeys::dump - (" << (void *) this << ")" << endl;
553  BESIndent::Indent();
554  strm << BESIndent::LMarg << "key file:" << _keys_file_name << endl;
555  if (_keys_file && *_keys_file) {
556  strm << BESIndent::LMarg << "key file is valid" << endl;
557  }
558  else {
559  strm << BESIndent::LMarg << "key file is NOT valid" << endl;
560  }
561  if (_the_keys && _the_keys->size()) {
562  strm << BESIndent::LMarg << " keys:" << endl;
563  BESIndent::Indent();
564  Keys_citer i = _the_keys->begin();
565  Keys_citer ie = _the_keys->end();
566  for (; i != ie; i++) {
567  strm << BESIndent::LMarg << (*i).first << ": " /*<< endl*/;
568  // BESIndent::Indent();
569  vector<string>::const_iterator v = (*i).second.begin();
570  vector<string>::const_iterator ve = (*i).second.end();
571  for (; v != ve; v++) {
572  strm << (*v) << " "; //endl;
573  }
574  strm << endl;
575  //BESIndent::UnIndent();
576  }
577  BESIndent::UnIndent();
578  }
579  else {
580  strm << BESIndent::LMarg << "keys: none" << endl;
581  }
582  BESIndent::UnIndent();
583 }
584 
BESError::get_line
virtual int get_line()
get the line number where the exception was thrown
Definition: BESError.h:115
TheBESKeys::ConfigFile
static std::string ConfigFile
Definition: TheBESKeys.h:147
BESInternalFatalError
exception thrown if an internal error is found and is fatal to the BES
Definition: BESInternalFatalError.h:43
BESUtil::removeLeadingAndTrailingBlanks
static void removeLeadingAndTrailingBlanks(string &key)
Definition: BESUtil.cc:463
BESError::get_message
virtual std::string get_message()
get the error message for this exception
Definition: BESError.h:99
TheBESKeys::read_string_key
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:499
BESError::get_file
virtual std::string get_file()
get the file name where the exception was thrown
Definition: BESError.h:107
TheBESKeys::read_bool_key
bool read_bool_key(const std::string &key, bool default_value)
Read a boolean-valued key from the bes.conf file.
Definition: TheBESKeys.cc:474
TheBESKeys::TheKeys
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:61
BESUtil::lowercase
static string lowercase(const string &s)
Definition: BESUtil.cc:197
TheBESKeys
mapping of key/value pairs defining different behaviors of an application.
Definition: TheBESKeys.h:79
BESSyntaxUserError
error thrown if there is a user syntax error in the request or any other user error
Definition: BESSyntaxUserError.h:41
TheBESKeys::get_value
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:420
TheBESKeys::get_values
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:451
TheBESKeys::set_key
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:372
BESError
Abstract exception class for the BES with basic string message.
Definition: BESError.h:58
TheBESKeys::~TheBESKeys
virtual ~TheBESKeys()
cleans up the key/value pair mapping
Definition: TheBESKeys.cc:127
TheBESKeys::dump
virtual void dump(std::ostream &strm) const
dumps information about this object
Definition: TheBESKeys.cc:550
BESFSDir
Definition: BESFSDir.h:46
TheBESKeys::read_int_key
int read_int_key(const std::string &key, int default_value)
Read an integer-valued key from the bes.conf file.
Definition: TheBESKeys.cc:524
BESFSFile
Definition: BESFSFile.h:42