bes  Updated for version 3.20.6
kvp_utils.cc
1 // -*- mode: c++; c-basic-offset:4 -*-
2 
3 // This file is part of the BES
4 
5 // Copyright (c) 2020 OPeNDAP, Inc.
6 // Author: Nathan Potter<ndp@opendap.org>
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
17 //
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //
22 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
23 // Created by ndp on 12/11/19.
24 //
25 
26 #include "config.h"
27 
28 #include <cerrno>
29 #include <cstring>
30 
31 #include <string>
32 #include <sstream>
33 #include <set>
34 
35 #if HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 
39 #include "BESUtil.h"
40 #include "BESFSDir.h"
41 #include "BESFSFile.h"
42 #include "BESInternalFatalError.h"
43 
44 #include "kvp_utils.h"
45 
46 
47 #define BES_INCLUDE_KEY "BES.Include"
48 
49 using namespace std;
50 
51 namespace kvp {
52 
53  // Forward declaration, implementation at end of file..
54  void load_keys(
55  set<string> &loaded_kvp_files,
56  const std::string &keys_file_name,
57  std::map<std::string, std::vector<std::string> > &keystore);
58 
59  bool only_blanks(const char *line) {
60  string my_line = line;
61  if (my_line.find_first_not_of(" ") != string::npos)
62  return false;
63  else
64  return true;
65  }
66 
67 
68 // The string contained in the character buffer b should be of the
69 // format key=value or key+=value. The pair is broken apart, storing the
70 // key in the key parameter and the value of the key in the value
71 // parameter. If += is used, then the value should be added to the value
72 // of key, not replacing.
73 //
74 // It used to be that we would validate the key=value line. Instead,
75 // anything after the equal sign is considered the value of the key.
76  bool break_pair(const char *b, string &key, string &value, bool &addto) {
77  addto = false;
78  // Ignore comments and lines with only spaces
79  if (b && (b[0] != '#') && (!only_blanks(b))) {
80  size_t l = strlen(b);
81  if (l > 1) {
82  int pos = 0;
83  bool done = false;
84  for (size_t j = 0; j < l && !done; j++) {
85  if (b[j] == '=') {
86  if (!addto)
87  pos = j;
88  else {
89  if (pos != static_cast<int>(j - 1)) {
90  string s = string("BES: Invalid entry ") + b +
91  " in configuration file "// + _keys_file_name
92  + " '+' character found in variable name" + " or attempting '+=' with space"
93  + " between the characters.\n";
94  throw BESInternalFatalError(s, __FILE__, __LINE__);
95  }
96  }
97  done = true;
98  } else if (b[j] == '+') {
99  addto = true;
100  pos = j;
101  }
102  }
103  if (!done) {
104  string s = string("BES: Invalid entry ") + b + " in configuration file " //+ _keys_file_name + ": "
105  + " '=' character not found.\n";
106  throw BESInternalFatalError(s, __FILE__, __LINE__);
107  }
108 
109  string s = b;
110  key = s.substr(0, pos);
112  if (addto)
113  value = s.substr(pos + 2, s.size());
114  else
115  value = s.substr(pos + 1, s.size());
117  return true;
118  }
119  return false;
120  }
121  return false;
122  }
123 
124 
131  void load_include_file(
132  set<string> &loaded_kvp_files,
133  const string &file,
134  std::map<std::string, std::vector<std::string> > &keystore
135  ) {
136  // make sure the file exists and is readable
137  // throws exception if unable to read
138  // not loaded if has already be started to be loaded
139  set<string>::iterator it = loaded_kvp_files.find(file);
140 
141  if (it == loaded_kvp_files.end()) {
142  // Didn't find it, better load it...
143  loaded_kvp_files.insert(file);
144  load_keys(loaded_kvp_files, file, keystore);
145  }
146  }
147 
148 
160  void load_include_files(
161  set<string> &loaded_kvp_files,
162  const string &file_expr,
163  std::map<std::string, std::vector<std::string> > &keystore,
164  const string &current_keys_file_name
165  ) {
166  string newdir = "";
167  BESFSFile allfiles(file_expr);
168 
169  // If the files specified begin with a /, then use that directory
170  // instead of the current keys file directory.
171  if (!file_expr.empty() && file_expr[0] == '/') {
172  newdir = allfiles.getDirName();
173  } else {
174  // determine the directory of the current keys file. All included
175  // files will be relative to this file.
176  BESFSFile currfile(current_keys_file_name);
177  string currdir = currfile.getDirName();
178 
179  string alldir = allfiles.getDirName();
180 
181  if ((currdir == "./" || currdir == ".") && (alldir == "./" || alldir == ".")) {
182  newdir = "./";
183  } else {
184  if (alldir == "./" || alldir == ".") {
185  newdir = currdir;
186  } else {
187  newdir = currdir + "/" + alldir;
188  }
189  }
190  }
191 
192  // load the files one at a time. If the directory doesn't exist,
193  // then don't load any configuration files
194  BESFSDir fsd(newdir, allfiles.getFileName());
195  BESFSDir::fileIterator i = fsd.beginOfFileList();
196  BESFSDir::fileIterator e = fsd.endOfFileList();
197  for (; i != e; i++) {
198  string include_file = (*i).getFullPath();
199  load_include_file(loaded_kvp_files, include_file, keystore);
200  }
201  }
202 
203  void set_key(
204  const string &key,
205  const string &val,
206  bool addto,
207  std::map<std::string, std::vector<std::string> > &keystore) {
208  map<string, vector<string> >::iterator i;
209  i = keystore.find(key);
210  if (i == keystore.end()) {
211  vector<string> vals;
212  keystore[key] = vals;
213  }
214  if (!addto) keystore[key].clear();
215  if (!val.empty()) {
216  keystore[key].push_back(val);
217  }
218  }
219 
220  void load_keys(
221  set<string> &loaded_kvp_files,
222  std::ifstream &keys_file,
223  std::map<std::string, std::vector<std::string> > &keystore,
224  const string &current_keys_file_name) {
225 
226  string key, value, line;
227  while (!keys_file.eof()) {
228  bool addto = false;
229  getline(keys_file, line);
230  if (break_pair(line.c_str(), key, value, addto)) {
231  if (key == BES_INCLUDE_KEY) {
232  // We make this call to set_key() and force 'addto' to
233  // be true because we need access to the child configuration
234  // files and their values for the admin interface.
235  set_key(key, value, true, keystore);
236  //load_include_files(kvp_files, value, keystore);
237  load_include_files(loaded_kvp_files, value, keystore, current_keys_file_name);
238  } else {
239  set_key(key, value, addto, keystore);
240  }
241  }
242  }
243  }
244 
245  void load_keys(
246  set<string> &loaded_kvp_files,
247  const std::string &keys_file_name,
248  std::map<std::string, std::vector<std::string> > &keystore
249  ) {
250  std::ifstream keys_file(keys_file_name.c_str());
251 
252  if (!keys_file) {
253  char path[500];
254  getcwd(path, sizeof(path));
255  string s = string("Cannot open configuration file '") + keys_file_name + "': ";
256  char *err = strerror(errno);
257  if (err)
258  s += err;
259  else
260  s += "Unknown error";
261 
262  s += (string) ".\n" + "The current working directory is " + path;
263  throw BESInternalFatalError(s, __FILE__, __LINE__);
264  }
265 
266  try {
267  loaded_kvp_files.insert(keys_file_name);
268  load_keys(loaded_kvp_files, keys_file, keystore, keys_file_name);
269  }
270  catch (BESError &e) {
271  // be sure we're throwing a fatal error, since the BES can't run
272  // without the configuration file
273  //clean();
274  throw BESInternalFatalError(e.get_message(), e.get_file(), e.get_line());
275  }
276  catch (std::exception &e) {
277  //clean();
278  string s = (string) "Caught exception load keys from the BES configuration file '"
279  + keys_file_name + "' message:" + e.what();
280  throw BESInternalFatalError(s, __FILE__, __LINE__);
281  }
282  }
283 
284 
285  void load_keys(
286  const std::string &keys_file_name,
287  std::map<std::string, std::vector<std::string> > &keystore
288  ) {
289  set<string> loaded_kvp_files;
290  load_keys(loaded_kvp_files, keys_file_name, keystore);
291  }
292 
293 } // namespace kvp
BESError::get_line
virtual int get_line()
get the line number where the exception was thrown
Definition: BESError.h:115
BESInternalFatalError
exception thrown if an internal error is found and is fatal to the BES
Definition: BESInternalFatalError.h:43
BESError::get_message
virtual std::string get_message()
get the error message for this exception
Definition: BESError.h:99
BESError::get_file
virtual std::string get_file()
get the file name where the exception was thrown
Definition: BESError.h:107
BESError
Abstract exception class for the BES with basic string message.
Definition: BESError.h:58
BESFSDir
Definition: BESFSDir.h:41
BESFSFile
Definition: BESFSFile.h:40
BESUtil::removeLeadingAndTrailingBlanks
static void removeLeadingAndTrailingBlanks(std::string &key)
Definition: BESUtil.cc:466