bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
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
49using namespace std;
50
51namespace kvp {
52
53 // Forward declaration, implementation at end of file...
54 void load_keys(
55 const std::string &keys_file_name,
56 set<string> &loaded_kvp_files,
57 std::unordered_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 "// + d_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, '=' character not found.\n";
105 throw BESInternalFatalError(s, __FILE__, __LINE__);
106 }
107
108 string s = b;
109 key = s.substr(0, pos);
111 if (addto)
112 value = s.substr(pos + 2, s.size());
113 else
114 value = s.substr(pos + 1, s.size());
116 return true;
117 }
118 return false;
119 }
120 return false;
121 }
122
123
130 void load_include_file(
131 const string &file,
132 set<string> &loaded_kvp_files,
133 std::unordered_map<std::string, std::vector<std::string> > &keystore
134 ) {
135 // make sure the file exists and is readable
136 // throws exception if unable to read
137 // not loaded if has already be started to be loaded
138 auto it = loaded_kvp_files.find(file);
139
140 if (it == loaded_kvp_files.end()) {
141 // Didn't find it, better load it...
142 loaded_kvp_files.insert(file);
143 load_keys(file, loaded_kvp_files, keystore);
144 }
145 }
146
147
159 void load_include_files(
160 const string &current_keys_file_name,
161 const string &file_expr,
162 set<string> &loaded_kvp_files,
163 std::unordered_map<std::string, std::vector<std::string> > &keystore
164 ) {
165 string newdir = "";
166 BESFSFile allfiles(file_expr);
167
168 // If the files specified begin with a /, then use that directory
169 // instead of the current keys file directory.
170 if (!file_expr.empty() && file_expr[0] == '/') {
171 newdir = allfiles.getDirName();
172 } else {
173 // determine the directory of the current keys file. All included
174 // files will be relative to this file.
175 BESFSFile currfile(current_keys_file_name);
176 string currdir = currfile.getDirName();
177
178 string alldir = allfiles.getDirName();
179
180 if ((currdir == "./" || currdir == ".") && (alldir == "./" || alldir == ".")) {
181 newdir = "./";
182 } else {
183 if (alldir == "./" || alldir == ".") {
184 newdir = currdir;
185 } else {
186 newdir = currdir + "/" + alldir;
187 }
188 }
189 }
190
191 // load the files one at a time. If the directory doesn't exist,
192 // then don't load any configuration files
193 BESFSDir fsd(newdir, allfiles.getFileName());
194 BESFSDir::fileIterator i = fsd.beginOfFileList();
195 BESFSDir::fileIterator e = fsd.endOfFileList();
196 for (; i != e; i++) {
197 string include_file = (*i).getFullPath();
198 load_include_file(include_file, loaded_kvp_files, keystore);
199 }
200 }
201
202 void set_key(
203 const string &key,
204 const string &val,
205 bool addto,
206 std::unordered_map<std::string, std::vector<std::string> > &keystore) {
207
208 auto i = keystore.find(key);
209 if (i == keystore.end()) {
210 vector<string> vals;
211 keystore[key] = vals;
212 }
213 if (!addto) keystore[key].clear();
214 if (!val.empty()) {
215 keystore[key].push_back(val);
216 }
217 }
218
219 void load_keys(
220 const string &current_keys_file_name,
221 std::ifstream &keys_file,
222 set<string> &loaded_kvp_files,
223 std::unordered_map<std::string, std::vector<std::string> > &keystore ) {
224
225 string key, value, line;
226 while (!keys_file.eof()) {
227 bool addto = false;
228 getline(keys_file, line);
229 if (break_pair(line.c_str(), key, value, addto)) {
230 if (key == BES_INCLUDE_KEY) {
231 // We make this call to set_key() and force 'addto' to
232 // be true because we need access to the child configuration
233 // files and their values for the admin interface.
234 set_key(key, value, true, keystore);
235 //load_include_files(kvp_files, value, keystore);
236 load_include_files(current_keys_file_name, value, loaded_kvp_files, keystore );
237 } else {
238 set_key(key, value, addto, keystore);
239 }
240 }
241 }
242 }
243
244 void load_keys(
245 const std::string &keys_file_name,
246 set<string> &loaded_kvp_files,
247 std::unordered_map<std::string, std::vector<std::string> > &keystore
248 ) {
249 std::ifstream keys_file(keys_file_name.c_str());
250
251 if (!keys_file) {
252 char path[500];
253 getcwd(path, sizeof(path));
254 string s = string("Cannot open configuration file '") + keys_file_name + "': ";
255 const char *err = strerror(errno);
256 if (err)
257 s += err;
258 else
259 s += "Unknown error";
260
261 s += (string) ".\n" + "The current working directory is " + path;
262 throw BESInternalFatalError(s, __FILE__, __LINE__);
263 }
264
265 try {
266 loaded_kvp_files.insert(keys_file_name);
267 load_keys(keys_file_name, keys_file, loaded_kvp_files, keystore);
268 }
269 catch (const BESError &e) {
270 throw BESInternalFatalError(e.get_message(), e.get_file(), e.get_line());
271 }
272 catch (const std::exception &e) {
273 string s = (string) "Caught exception load keys from the BES configuration file '"
274 + keys_file_name + "' message:" + e.what();
275 throw BESInternalFatalError(s, __FILE__, __LINE__);
276 }
277 }
278
279 void load_keys(
280 const std::string &keys_file_name,
281 std::unordered_map<std::string, std::vector<std::string> > &keystore
282 ) {
283 set<string> loaded_kvp_files;
284 // FIXME: Don't make this just to throw it away. jhrg 2/2/23
285 load_keys(keys_file_name, loaded_kvp_files, keystore);
286 }
287
288} // namespace kvp
unsigned int get_line() const
get the line number where the exception was thrown
Definition BESError.h:148
std::string get_file() const
get the file name where the exception was thrown
Definition BESError.h:140
std::string get_message() const
get the error message for this exception
Definition BESError.h:132
static void removeLeadingAndTrailingBlanks(std::string &key)
Definition BESUtil.cc:448