bes  Updated for version 3.19.1
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 #include <unistd.h>
36 
37 #include "TheBESKeys.h"
38 #include "BESInternalFatalError.h"
39 #include "BESSyntaxUserError.h"
40 
41 #include "config.h"
42 
43 #include <cerrno>
44 #include <cstring>
45 
46 #if HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 
50 #include "TheBESKeys.h"
51 #include "BESUtil.h"
52 #include "BESFSDir.h"
53 #include "BESFSFile.h"
54 
55 #define BES_INCLUDE_KEY "BES.Include"
56 
57 std::vector<string> TheBESKeys::KeyList;
58 
59 TheBESKeys *TheBESKeys::_instance = 0;
60 string TheBESKeys::ConfigFile = "";
61 
63 {
64  if (_instance) return _instance;
65 
66  if (!TheBESKeys::ConfigFile.empty()) {
67  _instance = new TheBESKeys(TheBESKeys::ConfigFile);
68  return _instance;
69  }
70 
71  // _instance is a nullptr and TheBESKeys::ConfigFile is ""
72  // so lets try some obvious places...
73 
74  string try_ini = "/usr/local/etc/bes/bes.conf";
75  if (access(try_ini.c_str(), R_OK) == 0) {
76  TheBESKeys::ConfigFile = try_ini;
77  _instance = new TheBESKeys(TheBESKeys::ConfigFile);
78  return _instance;
79  }
80 
81  try_ini = "/etc/bes/bes.conf";
82  if (access(try_ini.c_str(), R_OK) == 0) {
83  TheBESKeys::ConfigFile = try_ini;
84  _instance = new TheBESKeys(TheBESKeys::ConfigFile);
85  return _instance;
86  }
87 
88  try_ini = "/usr/etc/bes/bes.conf";
89  if (access(try_ini.c_str(), R_OK) == 0) {
90  TheBESKeys::ConfigFile = try_ini;
91  _instance = new TheBESKeys(TheBESKeys::ConfigFile);
92  return _instance;
93  }
94 
95  throw BESInternalFatalError("Unable to find a conf file or module version mismatch.", __FILE__, __LINE__);
96 }
97 
114 TheBESKeys::TheBESKeys(const string &keys_file_name) :
115  _keys_file(0), _keys_file_name(keys_file_name), _the_keys(0), _own_keys(true)
116 {
117  _the_keys = new map<string, vector<string> >;
118  initialize_keys();
119 }
120 
121 TheBESKeys::TheBESKeys(const string &keys_file_name, map<string, vector<string> > *keys) :
122  _keys_file(0), _keys_file_name(keys_file_name), _the_keys(keys), _own_keys(false)
123 {
124  initialize_keys();
125 }
126 
130 {
131  clean();
132 }
133 
134 void TheBESKeys::initialize_keys()
135 {
136  _keys_file = new ifstream(_keys_file_name.c_str());
137 
138  if (!(*_keys_file)) {
139  char path[500];
140  getcwd(path, sizeof(path));
141  string s = string("BES: fatal, cannot open BES configuration file ") + _keys_file_name + ": ";
142  char *err = strerror(errno);
143  if (err)
144  s += err;
145  else
146  s += "Unknown error";
147 
148  s += (string) ".\n" + "The current working directory is " + path;
149  throw BESInternalFatalError(s, __FILE__, __LINE__);
150  }
151 
152  try {
153  load_keys();
154  }
155  catch (BESError &e) {
156  // be sure we're throwing a fatal error, since the BES can't run
157  // within the configuration file
158  clean();
159  throw BESInternalFatalError(e.get_message(), e.get_file(), e.get_line());
160  }
161  catch (...) {
162  clean();
163  string s = (string) "Undefined exception while trying to load keys from bes configuration file "
164  + _keys_file_name;
165  throw BESInternalFatalError(s, __FILE__, __LINE__);
166  }
167 }
168 
169 void TheBESKeys::clean()
170 {
171  if (_keys_file) {
172  _keys_file->close();
173  delete _keys_file;
174  }
175 
176  if (_the_keys && _own_keys) {
177  delete _the_keys;
178  }
179 }
180 
181 /* @brief Determine if the specified key file has been loaded yet
182  *
183  * Given the name of the key file, determine if it has already been
184  * loaded. More specifically, if started to load the file.
185  *
186  * @returns true if already started to load, false otherwise
187  */
188 bool TheBESKeys::LoadedKeys(const string &key_file)
189 {
190  vector<string>::const_iterator i = TheBESKeys::KeyList.begin();
191  vector<string>::const_iterator e = TheBESKeys::KeyList.end();
192  for (; i != e; i++) {
193  if ((*i) == key_file) {
194  return true;
195  }
196  }
197 
198  return false;
199 }
200 
201 void TheBESKeys::load_keys()
202 {
203  string key, value, line;
204  while (!_keys_file->eof()) {
205  bool addto = false;
206  getline(*_keys_file, line);
207  if (break_pair(line.c_str(), key, value, addto)) {
208  if (key == BES_INCLUDE_KEY) {
209  // We make this call to set_key() and force 'addto' to
210  // be true because we need access to the child configuration
211  // files and their values for the admin interface.
212  set_key(key, value, true);
213  load_include_files(value);
214  }
215  else {
216  set_key(key, value, addto);
217  }
218  }
219  }
220 }
221 
222 // The string contained in the character buffer b should be of the
223 // format key=value or key+=value. The pair is broken apart, storing the
224 // key in the key parameter and the value of the key in the value
225 // parameter. If += is used, then the value should be added to the value
226 // of key, not replacing.
227 //
228 // It used to be that we would validate the key=value line. Instead,
229 // anything after the equal sign is considered the value of the key.
230 inline bool TheBESKeys::break_pair(const char* b, string& key, string &value, bool &addto)
231 {
232  addto = false;
233  // Ignore comments and lines with only spaces
234  if (b && (b[0] != '#') && (!only_blanks(b))) {
235  register size_t l = strlen(b);
236  if (l > 1) {
237  int pos = 0;
238  bool done = false;
239  for (register size_t j = 0; j < l && !done; j++) {
240  if (b[j] == '=') {
241  if (!addto)
242  pos = j;
243  else {
244  if (pos != static_cast<int>(j - 1)) {
245  string s = string("BES: Invalid entry ") + b + " in configuration file " + _keys_file_name
246  + " '+' character found in variable name" + " or attempting '+=' with space"
247  + " between the characters.\n";
248  throw BESInternalFatalError(s, __FILE__, __LINE__);
249  }
250  }
251  done = true;
252  }
253  else if (b[j] == '+') {
254  addto = true;
255  pos = j;
256  }
257  }
258  if (!done) {
259  string s = string("BES: Invalid entry ") + b + " in configuration file " + _keys_file_name + ": "
260  + " '=' character not found.\n";
261  throw BESInternalFatalError(s, __FILE__, __LINE__);
262  }
263 
264  string s = b;
265  key = s.substr(0, pos);
267  if (addto)
268  value = s.substr(pos + 2, s.size());
269  else
270  value = s.substr(pos + 1, s.size());
272 
273  return true;
274  }
275 
276  return false;
277  }
278 
279  return false;
280 }
281 
291 void TheBESKeys::load_include_files(const string &files)
292 {
293  string newdir;
294  BESFSFile allfiles(files);
295 
296  // If the files specified begin with a /, then use that directory
297  // instead of the current keys file directory.
298  if (!files.empty() && files[0] == '/') {
299  newdir = allfiles.getDirName();
300  }
301  else {
302  // determine the directory of the current keys file. All included
303  // files will be relative to this file.
304  BESFSFile currfile(_keys_file_name);
305  string currdir = currfile.getDirName();
306 
307  string alldir = allfiles.getDirName();
308 
309  if ((currdir == "./" || currdir == ".") && (alldir == "./" || alldir == ".")) {
310  newdir = "./";
311  }
312  else {
313  if (alldir == "./" || alldir == ".") {
314  newdir = currdir;
315  }
316  else {
317  newdir = currdir + "/" + alldir;
318  }
319  }
320  }
321 
322  // load the files one at a time. If the directory doesn't exist,
323  // then don't load any configuration files
324  BESFSDir fsd(newdir, allfiles.getFileName());
325  BESFSDir::fileIterator i = fsd.beginOfFileList();
326  BESFSDir::fileIterator e = fsd.endOfFileList();
327  for (; i != e; i++) {
328  load_include_file((*i).getFullPath());
329  }
330 }
331 
338 void TheBESKeys::load_include_file(const string &file)
339 {
340  // make sure the file exists and is readable
341  // throws exception if unable to read
342  // not loaded if has already be started to be loaded
343  if (!TheBESKeys::LoadedKeys(file)) {
344  TheBESKeys::KeyList.push_back(file);
345  TheBESKeys tmp(file, _the_keys);
346  }
347 }
348 
349 bool TheBESKeys::only_blanks(const char *line)
350 {
351  string my_line = line;
352  if (my_line.find_first_not_of(" ") != string::npos)
353  return false;
354  else
355  return true;
356 }
357 
374 void TheBESKeys::set_key(const string &key, const string &val, bool addto)
375 {
376  map<string, vector<string> >::iterator i;
377  i = _the_keys->find(key);
378  if (i == _the_keys->end()) {
379  vector<string> vals;
380  (*_the_keys)[key] = vals;
381  }
382  if (!addto) (*_the_keys)[key].clear();
383  if (!val.empty()) {
384  (*_the_keys)[key].push_back(val);
385  }
386 }
387 
399 void TheBESKeys::set_key(const string &pair)
400 {
401  string key;
402  string val;
403  bool addto = false;
404  break_pair(pair.c_str(), key, val, addto);
405  set_key(key, val, addto);
406 }
407 
422 void TheBESKeys::get_value(const string& s, string &val, bool &found)
423 {
424  found = false;
425  map<string, vector<string> >::iterator i;
426  i = _the_keys->find(s);
427  if (i != _the_keys->end()) {
428  found = true;
429  if ((*i).second.size() > 1) {
430  string err = string("Multiple values for the key ") + s + " found, should only be one.";
431  throw BESSyntaxUserError(err, __FILE__, __LINE__);
432  }
433  if ((*i).second.size() == 1) {
434  val = (*i).second[0];
435  }
436  else {
437  val = "";
438  }
439  }
440 }
441 
453 void TheBESKeys::get_values(const string& s, vector<string> &vals, bool &found)
454 {
455  found = false;
456  map<string, vector<string> >::iterator i;
457  i = _the_keys->find(s);
458  if (i != _the_keys->end()) {
459  found = true;
460  vals = (*i).second;
461  }
462 }
463 
470 void TheBESKeys::dump(ostream &strm) const
471 {
472  strm << BESIndent::LMarg << "BESKeys::dump - (" << (void *) this << ")" << endl;
473  BESIndent::Indent();
474  strm << BESIndent::LMarg << "key file:" << _keys_file_name << endl;
475  if (_keys_file && *_keys_file) {
476  strm << BESIndent::LMarg << "key file is valid" << endl;
477  }
478  else {
479  strm << BESIndent::LMarg << "key file is NOT valid" << endl;
480  }
481  if (_the_keys && _the_keys->size()) {
482  strm << BESIndent::LMarg << " keys:" << endl;
483  BESIndent::Indent();
484  Keys_citer i = _the_keys->begin();
485  Keys_citer ie = _the_keys->end();
486  for (; i != ie; i++) {
487  strm << BESIndent::LMarg << (*i).first << ":" << endl;
488  BESIndent::Indent();
489  vector<string>::const_iterator v = (*i).second.begin();
490  vector<string>::const_iterator ve = (*i).second.end();
491  for (; v != ve; v++) {
492  strm << (*v) << endl;
493  }
494  BESIndent::UnIndent();
495  }
496  BESIndent::UnIndent();
497  }
498  else {
499  strm << BESIndent::LMarg << "keys: none" << endl;
500  }
501  BESIndent::UnIndent();
502 }
503 
exception thrown if an internal error is found and is fatal to the BES
mapping of key/value pairs defining different behaviors of an application.
Definition: TheBESKeys.h:84
virtual std::string get_message()
get the error message for this exception
Definition: BESError.h:97
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:422
void set_key(const string &key, const string &val, bool addto=false)
allows the user to set key/value pairs from within the application.
Definition: TheBESKeys.cc:374
static void removeLeadingAndTrailingBlanks(string &key)
Definition: BESUtil.cc:450
error thrown if there is a user syntax error in the request or any other user error ...
Abstract exception class for the BES with basic string message.
Definition: BESError.h:56
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:62
virtual ~TheBESKeys()
cleans up the key/value pair mapping
Definition: TheBESKeys.cc:129
void get_values(const string &s, vector< string > &vals, bool &found)
Retrieve the values of a given key, if set.
Definition: TheBESKeys.cc:453
virtual std::string get_file()
get the file name where the exception was thrown
Definition: BESError.h:105
static string ConfigFile
Definition: TheBESKeys.h:145
virtual int get_line()
get the line number where the exception was thrown
Definition: BESError.h:113
virtual void dump(ostream &strm) const
dumps information about this object
Definition: TheBESKeys.cc:470