Kea 2.0.1
database_connection.cc
Go to the documentation of this file.
1// Copyright (C) 2015-2021 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <cc/cfg_to_element.h>
12#include <database/db_log.h>
15#include <util/strutil.h>
16
17#include <boost/algorithm/string.hpp>
18#include <boost/foreach.hpp>
19#include <vector>
20
21using namespace std;
22
23namespace isc {
24namespace db {
25
26const time_t DatabaseConnection::MAX_DB_TIME = 2147483647;
27
28std::string
29DatabaseConnection::getParameter(const std::string& name) const {
30 ParameterMap::const_iterator param = parameters_.find(name);
31 if (param == parameters_.end()) {
32 isc_throw(BadValue, "Parameter " << name << " not found");
33 }
34 return (param->second);
35}
36
38DatabaseConnection::parse(const std::string& dbaccess) {
40 std::string dba = dbaccess;
41
42 if (!dba.empty()) {
43 try {
44 vector<string> tokens;
45
46 // Handle the special case of a password which is enclosed in apostrophes.
47 // Such password may include whitespace.
48 std::string password_prefix = "password='";
49 auto password_pos = dba.find(password_prefix);
50 if (password_pos != string::npos) {
51 // Password starts with apostrophe, so let's find ending apostrophe.
52 auto password_end_pos = dba.find('\'', password_pos + password_prefix.length());
53 if (password_end_pos == string::npos) {
54 // No ending apostrophe. This is wrong.
55 isc_throw(InvalidParameter, "Apostrophe (') expected at the end of password");
56 }
57 // Extract the password value. It starts after the password=' prefix and ends
58 // at the position of ending apostrophe.
59 auto password = dba.substr(password_pos + password_prefix.length(),
60 password_end_pos - password_pos - password_prefix.length());
61 mapped_tokens.insert(make_pair("password", password));
62
63 // We need to erase the password from the access string because the generic
64 // algorithm parsing other parameters requires that there are no whitespaces
65 // within the parameter values.
66 dba.erase(password_pos, password_prefix.length() + password.length() + 2);
67 // Leading or trailing whitespace may remain after the password removal.
68 dba = util::str::trim(dba);
69 // If the password was the only parameter in the access string, there is
70 // nothing more to do.
71 if (dba.empty()) {
72 return (mapped_tokens);
73 }
74 }
75
76 // We need to pass a string to is_any_of, not just char*. Otherwise
77 // there are cryptic warnings on Debian6 running g++ 4.4 in
78 // /usr/include/c++/4.4/bits/stl_algo.h:2178 "array subscript is above
79 // array bounds"
80 boost::split(tokens, dba, boost::is_any_of(string("\t ")));
81 BOOST_FOREACH(std::string token, tokens) {
82 size_t pos = token.find("=");
83 if (pos != string::npos) {
84 string name = token.substr(0, pos);
85 string value = token.substr(pos + 1);
86 mapped_tokens.insert(make_pair(name, value));
87 } else {
88 isc_throw(InvalidParameter, "Cannot parse " << token
89 << ", expected format is name=value");
90 }
91 }
92 } catch (const std::exception& ex) {
93 // We'd obscure the password if we could parse the access string.
95 throw;
96 }
97 }
98
99 return (mapped_tokens);
100}
101
102std::string
104 // Reconstruct the access string: start of with an empty string, then
105 // work through all the parameters in the original string and add them.
106 std::string access;
107 for (DatabaseConnection::ParameterMap::const_iterator i = parameters.begin();
108 i != parameters.end(); ++i) {
109
110 // Separate second and subsequent tokens are preceded by a space.
111 if (!access.empty()) {
112 access += " ";
113 }
114
115 // Append name of parameter...
116 access += i->first;
117 access += "=";
118
119 // ... and the value, except in the case of the password, where a
120 // redacted value is appended.
121 if (i->first == std::string("password")) {
122 access += "*****";
123 } else {
124 access += i->second;
125 }
126 }
127
128 return (access);
129}
130
131bool
133 std::string readonly_value = "false";
134 try {
135 readonly_value = getParameter("readonly");
136 boost::algorithm::to_lower(readonly_value);
137 } catch (...) {
138 // Parameter "readonly" hasn't been specified so we simply use
139 // the default value of "false".
140 }
141
142 if ((readonly_value != "false") && (readonly_value != "true")) {
143 isc_throw(DbInvalidReadOnly, "invalid value '" << readonly_value
144 << "' specified for boolean parameter 'readonly'");
145 }
146
147 return (readonly_value == "true");
148}
149
150void
151DatabaseConnection::makeReconnectCtl(const std::string& timer_name) {
152 string type = "unknown";
153 unsigned int retries = 0;
154 unsigned int interval = 0;
155
156 // Assumes that parsing ensures only valid values are present
157 try {
158 type = getParameter("type");
159 } catch (...) {
160 // Wasn't specified so we'll use default of "unknown".
161 }
162
163 std::string parm_str;
164 try {
165 parm_str = getParameter("max-reconnect-tries");
166 retries = boost::lexical_cast<unsigned int>(parm_str);
167 } catch (...) {
168 // Wasn't specified so we'll use default of 0;
169 }
170
171 try {
172 parm_str = getParameter("reconnect-wait-time");
173 interval = boost::lexical_cast<unsigned int>(parm_str);
174 } catch (...) {
175 // Wasn't specified so we'll use default of 0;
176 }
177
179 try {
180 parm_str = getParameter("on-fail");
181 action = ReconnectCtl::onFailActionFromText(parm_str);
182 } catch (...) {
183 // Wasn't specified so we'll use default of "stop-retry-exit";
184 }
185
186 reconnect_ctl_ = boost::make_shared<ReconnectCtl>(type, timer_name, retries,
187 interval, action);
188}
189
190bool
193 return (DatabaseConnection::db_lost_callback_(db_reconnect_ctl));
194 }
195
196 return (false);
197}
198
199bool
202 return (DatabaseConnection::db_recovered_callback_(db_reconnect_ctl));
203 }
204
205 return (false);
206}
207
208bool
211 return (DatabaseConnection::db_failed_callback_(db_reconnect_ctl));
212 }
213
214 return (false);
215}
216
220
221 for (auto param: params) {
222 std::string keyword = param.first;
223 std::string value = param.second;
224
225 if ((keyword == "lfc-interval") ||
226 (keyword == "connect-timeout") ||
227 (keyword == "reconnect-wait-time") ||
228 (keyword == "max-reconnect-tries") ||
229 (keyword == "request-timeout") ||
230 (keyword == "tcp-keepalive") ||
231 (keyword == "port") ||
232 (keyword == "max-row-errors")) {
233 // integer parameters
234 int64_t int_value;
235 try {
236 int_value = boost::lexical_cast<int64_t>(value);
237 result->set(keyword, isc::data::Element::create(int_value));
238 } catch (...) {
240 .arg("integer").arg(keyword).arg(value);
241 }
242 } else if ((keyword == "persist") ||
243 (keyword == "tcp-nodelay") ||
244 (keyword == "readonly")) {
245 if (value == "true") {
246 result->set(keyword, isc::data::Element::create(true));
247 } else if (value == "false") {
248 result->set(keyword, isc::data::Element::create(false));
249 } else {
251 .arg("boolean").arg(keyword).arg(value);
252 }
253 } else if ((keyword == "type") ||
254 (keyword == "user") ||
255 (keyword == "password") ||
256 (keyword == "host") ||
257 (keyword == "name") ||
258 (keyword == "contact-points") ||
259 (keyword == "consistency") ||
260 (keyword == "serial-consistency") ||
261 (keyword == "keyspace") ||
262 (keyword == "on-fail")) {
263 result->set(keyword, isc::data::Element::create(value));
264 } else {
266 .arg("unknown").arg(keyword).arg(value);
267 }
268 }
269
270 return (result);
271}
272
275 ParameterMap params = parse(dbaccess);
276 return (toElement(params));
277}
278
279std::string
281 switch (action) {
283 return ("stop-retry-exit");
285 return ("serve-retry-exit");
287 return ("serve-retry-continue");
288 }
289 return ("invalid-action-type");
290}
291
293ReconnectCtl::onFailActionFromText(const std::string& text) {
294 if (text == "stop-retry-exit") {
296 } else if (text == "serve-retry-exit") {
298 } else if (text == "serve-retry-continue") {
300 } else {
301 isc_throw(BadValue, "Invalid action on connection loss: " << text);
302 }
303}
304
308
309} // namespace db
310} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition: data.cc:241
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition: data.cc:286
bool configuredReadOnly() const
Convenience method checking if database should be opened with read only access.
std::string getParameter(const std::string &name) const
Returns value of a connection parameter.
static std::string redactedAccessString(const ParameterMap &parameters)
Redact database access string.
static bool invokeDbLostCallback(const ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's lost connectivity callback.
static isc::data::ElementPtr toElement(const ParameterMap &params)
Unparse a parameter map.
static isc::data::ElementPtr toElementDbAccessString(const std::string &dbaccess)
Unparse an access string.
static DbCallback db_recovered_callback_
Optional callback function to invoke if an opened connection recovery succeeded.
virtual void makeReconnectCtl(const std::string &timer_name)
Instantiates a ReconnectCtl based on the connection's reconnect parameters.
static ParameterMap parse(const std::string &dbaccess)
Parse database access string.
static bool invokeDbFailedCallback(const ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restore failed connectivity callback.
static DbCallback db_failed_callback_
Optional callback function to invoke if an opened connection recovery failed.
static DbCallback db_lost_callback_
Optional callback function to invoke if an opened connection is lost.
static const time_t MAX_DB_TIME
Defines maximum value for time that can be reliably stored.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
static bool invokeDbRecoveredCallback(const ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restored connectivity callback.
Invalid 'readonly' value specification.
static OnFailAction onFailActionFromText(const std::string &text)
Convert string to action.
static std::string onFailActionToText(OnFailAction action)
Convert action to string.
We want to reuse the database backend connection and exchange code for other uses,...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
isc::log::Logger database_logger("database")
Common database library logger.
Definition: db_log.h:46
std::function< bool(ReconnectCtlPtr db_reconnect_ctl)> DbCallback
Defines a callback prototype for propagating events upward.
@ DB_INVALID_ACCESS
Definition: db_log.h:52
const isc::log::MessageID DATABASE_TO_JSON_ERROR
Definition: db_messages.h:25
OnFailAction
Type of action to take on connection loss.
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
string trim(const string &instring)
Trim Leading and Trailing Spaces.
Definition: strutil.cc:53
vector< string > tokens(const std::string &text, const std::string &delim, bool escape)
Split String into Tokens.
Definition: strutil.cc:77
Defines the logger used by the top-level component of kea-lfc.
DB_LOG & arg(T first, Args... args)
Pass parameters to replace logger placeholders.
Definition: db_log.h:144