]>
Commit | Line | Data |
---|---|---|
12c86877 | 1 | /* |
12471842 PL |
2 | * This file is part of PowerDNS or dnsdist. |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
13 | * This program 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 | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
870a0fe4 AT |
22 | #ifdef HAVE_CONFIG_H |
23 | #include "config.h" | |
24 | #endif | |
12c86877 | 25 | #include "arguments.hh" |
5b2cb3be | 26 | #include <boost/algorithm/string.hpp> |
2dfec518 | 27 | #include <boost/algorithm/string/compare.hpp> |
4f6ef75f | 28 | #include <boost/algorithm/string/predicate.hpp> |
fa8fd4d2 | 29 | |
61b26744 | 30 | #include "namespaces.hh" |
4f6ef75f | 31 | #include "logger.hh" |
8a16d778 AT |
32 | #include <sys/types.h> |
33 | #include <dirent.h> | |
34 | #include <sys/stat.h> | |
35 | #include <unistd.h> | |
e64bde34 | 36 | #include <climits> |
12c86877 | 37 | |
bace7695 | 38 | ArgvMap::param_t::const_iterator ArgvMap::begin() |
12c86877 | 39 | { |
9d6e970b | 40 | return d_params.begin(); |
12c86877 BH |
41 | } |
42 | ||
bace7695 | 43 | ArgvMap::param_t::const_iterator ArgvMap::end() |
12c86877 | 44 | { |
9d6e970b | 45 | return d_params.end(); |
12c86877 BH |
46 | } |
47 | ||
725bf803 | 48 | string& ArgvMap::set(const string& var) |
12c86877 | 49 | { |
9d6e970b | 50 | return d_params[var]; |
12c86877 BH |
51 | } |
52 | ||
725bf803 | 53 | void ArgvMap::setDefault(const string& var, const string& value) |
8864bdf6 | 54 | { |
bace7695 | 55 | if (defaultmap.count(var) == 0) { |
8864bdf6 | 56 | defaultmap.insert(pair<string, string>(var, value)); |
bace7695 | 57 | } |
8864bdf6 KM |
58 | } |
59 | ||
60 | void ArgvMap::setDefaults() | |
61 | { | |
bace7695 OM |
62 | for (const auto& param : d_params) { |
63 | if (defaultmap.count(param.first) == 0) { | |
64 | defaultmap.insert(param); | |
65 | } | |
66 | } | |
8864bdf6 KM |
67 | } |
68 | ||
725bf803 | 69 | bool ArgvMap::mustDo(const string& var) |
bd11bd1d | 70 | { |
725bf803 | 71 | return ((*this)[var] != "no") && ((*this)[var] != "off"); |
12c86877 BH |
72 | } |
73 | ||
725bf803 | 74 | vector<string> ArgvMap::list() |
12c86877 BH |
75 | { |
76 | vector<string> ret; | |
bace7695 OM |
77 | ret.reserve(d_params.size()); |
78 | for (const auto& param : d_params) { | |
79 | ret.push_back(param.first); | |
80 | } | |
12c86877 BH |
81 | return ret; |
82 | } | |
83 | ||
725bf803 | 84 | string& ArgvMap::set(const string& var, const string& help) |
12c86877 | 85 | { |
725bf803 OM |
86 | helpmap[var] = help; |
87 | d_typeMap[var] = "Parameter"; | |
12c86877 BH |
88 | return set(var); |
89 | } | |
90 | ||
725bf803 | 91 | void ArgvMap::setCmd(const string& var, const string& help) |
12c86877 | 92 | { |
725bf803 OM |
93 | helpmap[var] = help; |
94 | d_typeMap[var] = "Command"; | |
95 | set(var) = "no"; | |
12c86877 BH |
96 | } |
97 | ||
725bf803 | 98 | string& ArgvMap::setSwitch(const string& var, const string& help) |
12c86877 | 99 | { |
725bf803 OM |
100 | helpmap[var] = help; |
101 | d_typeMap[var] = "Switch"; | |
12c86877 BH |
102 | return set(var); |
103 | } | |
104 | ||
725bf803 | 105 | bool ArgvMap::contains(const string& var, const string& val) |
12c86877 | 106 | { |
9d6e970b | 107 | const auto& param = d_params.find(var); |
725bf803 | 108 | if (param == d_params.end() || param->second.empty()) { |
102eb646 BH |
109 | return false; |
110 | } | |
562588a3 | 111 | vector<string> parts; |
3ea9dcbc | 112 | |
9d6e970b | 113 | stringtok(parts, param->second, ", \t"); |
bace7695 | 114 | return std::any_of(parts.begin(), parts.end(), [&](const std::string& str) { return str == val; }); |
12c86877 BH |
115 | } |
116 | ||
12c86877 BH |
117 | string ArgvMap::helpstring(string prefix) |
118 | { | |
bace7695 | 119 | if (prefix == "no") { |
725bf803 | 120 | prefix = ""; |
bace7695 | 121 | } |
3ea9dcbc | 122 | |
12c86877 | 123 | string help; |
93a471a3 | 124 | |
bace7695 OM |
125 | for (const auto& helpitem : helpmap) { |
126 | if (!prefix.empty() && helpitem.first.find(prefix) != 0) { // only print items with prefix | |
725bf803 | 127 | continue; |
bace7695 | 128 | } |
3ea9dcbc | 129 | |
725bf803 | 130 | help += " --"; |
bace7695 | 131 | help += helpitem.first; |
12c86877 | 132 | |
bace7695 | 133 | string type = d_typeMap[helpitem.first]; |
12c86877 | 134 | |
bace7695 | 135 | if (type == "Parameter") { |
725bf803 | 136 | help += "=..."; |
bace7695 | 137 | } |
725bf803 | 138 | else if (type == "Switch") { |
bace7695 OM |
139 | help += " | --" + helpitem.first + "=yes"; |
140 | help += " | --" + helpitem.first + "=no"; | |
12c86877 | 141 | } |
725bf803 OM |
142 | |
143 | help += "\n\t"; | |
bace7695 | 144 | help += helpitem.second; |
725bf803 OM |
145 | help += "\n"; |
146 | } | |
12c86877 BH |
147 | return help; |
148 | } | |
149 | ||
bace7695 | 150 | string ArgvMap::formatOne(bool running, bool full, const string& var, const string& help, const string& theDefault, const string& current) |
9d6e970b CH |
151 | { |
152 | string out; | |
153 | ||
154 | if (!running || full) { | |
155 | out += "#################################\n"; | |
156 | out += "# "; | |
157 | out += var; | |
158 | out += "\t"; | |
159 | out += help; | |
160 | out += "\n#\n"; | |
725bf803 OM |
161 | } |
162 | else { | |
9d6e970b CH |
163 | if (theDefault == current) { |
164 | return ""; | |
165 | } | |
166 | } | |
167 | ||
725bf803 | 168 | if (!running || theDefault == current) { |
9d6e970b CH |
169 | out += "# "; |
170 | } | |
171 | ||
172 | if (running) { | |
173 | out += var + "=" + current + "\n"; | |
174 | if (full) { | |
175 | out += "\n"; | |
176 | } | |
725bf803 OM |
177 | } |
178 | else { | |
9d6e970b CH |
179 | out += var + "=" + theDefault + "\n\n"; |
180 | } | |
181 | ||
182 | return out; | |
183 | } | |
184 | ||
185 | // If running and full, only changed settings are returned. | |
8864bdf6 | 186 | string ArgvMap::configstring(bool running, bool full) |
12c86877 BH |
187 | { |
188 | string help; | |
6bef3d91 | 189 | |
bace7695 | 190 | if (running) { |
725bf803 | 191 | help = "# Autogenerated configuration file based on running instance (" + nowTime() + ")\n\n"; |
bace7695 OM |
192 | } |
193 | else { | |
725bf803 | 194 | help = "# Autogenerated configuration file template\n\n"; |
bace7695 | 195 | } |
8864bdf6 | 196 | |
9d6e970b CH |
197 | // Affects parsing, should come first. |
198 | help += formatOne(running, full, "ignore-unknown-settings", helpmap["ignore-unknown-settings"], defaultmap["ignore-unknown-settings"], d_params["ignore-unknown-settings"]); | |
199 | ||
cdb00dca OM |
200 | for (const auto& helpitem : helpmap) { |
201 | if (d_typeMap[helpitem.first] == "Command") { | |
9d6e970b | 202 | continue; |
bace7695 | 203 | } |
cdb00dca | 204 | if (helpitem.first == "ignore-unknown-settings") { |
c7efa8ff | 205 | continue; |
bace7695 | 206 | } |
c7efa8ff | 207 | |
cdb00dca OM |
208 | if (defaultmap.count(helpitem.first) == 0) { |
209 | throw ArgException(string("Default for setting '") + helpitem.first + "' not set"); | |
8864bdf6 KM |
210 | } |
211 | ||
cdb00dca | 212 | help += formatOne(running, full, helpitem.first, helpitem.second, defaultmap[helpitem.first], d_params[helpitem.first]); |
9d6e970b | 213 | } |
8864bdf6 | 214 | |
9d6e970b | 215 | if (running) { |
bace7695 OM |
216 | for (const auto& unknown : d_unknownParams) { |
217 | help += formatOne(running, full, unknown.first, "unknown setting", "", unknown.second); | |
12c86877 | 218 | } |
c7efa8ff | 219 | } |
9d6e970b | 220 | |
12c86877 BH |
221 | return help; |
222 | } | |
223 | ||
725bf803 | 224 | const string& ArgvMap::operator[](const string& arg) |
12c86877 | 225 | { |
bace7695 | 226 | if (!parmIsset(arg)) { |
725bf803 | 227 | throw ArgException(string("Undefined but needed argument: '") + arg + "'"); |
bace7695 | 228 | } |
12c86877 | 229 | |
9d6e970b | 230 | return d_params[arg]; |
12c86877 BH |
231 | } |
232 | ||
725bf803 | 233 | mode_t ArgvMap::asMode(const string& arg) |
cc2978dc | 234 | { |
bace7695 | 235 | if (!parmIsset(arg)) { |
725bf803 | 236 | throw ArgException(string("Undefined but needed argument: '") + arg + "'"); |
bace7695 | 237 | } |
cc2978dc | 238 | |
bace7695 OM |
239 | const auto* const cptr_orig = d_params[arg].c_str(); |
240 | char* cptr_ret = nullptr; | |
241 | ||
242 | auto mode = static_cast<mode_t>(strtol(cptr_orig, &cptr_ret, 8)); | |
243 | if (mode == 0 && cptr_ret == cptr_orig) { | |
cc2978dc | 244 | throw ArgException("'" + arg + string("' contains invalid octal mode")); |
bace7695 | 245 | } |
725bf803 | 246 | return mode; |
cc2978dc BH |
247 | } |
248 | ||
725bf803 | 249 | gid_t ArgvMap::asGid(const string& arg) |
cc2978dc | 250 | { |
bace7695 | 251 | if (!parmIsset(arg)) { |
725bf803 | 252 | throw ArgException(string("Undefined but needed argument: '") + arg + "'"); |
bace7695 | 253 | } |
cc2978dc | 254 | |
bace7695 OM |
255 | const auto* cptr_orig = d_params[arg].c_str(); |
256 | char* cptr_ret = nullptr; | |
257 | auto gid = static_cast<gid_t>(strtol(cptr_orig, &cptr_ret, 0)); | |
cc2978dc BH |
258 | if (gid == 0 && cptr_ret == cptr_orig) { |
259 | // try to resolve | |
bace7695 OM |
260 | |
261 | struct group* group = getgrnam(d_params[arg].c_str()); // NOLINT: called before going multi-threaded | |
262 | if (group == nullptr) { | |
725bf803 | 263 | throw ArgException("'" + arg + string("' contains invalid group")); |
bace7695 | 264 | } |
cc2978dc | 265 | gid = group->gr_gid; |
725bf803 OM |
266 | } |
267 | return gid; | |
cc2978dc BH |
268 | } |
269 | ||
725bf803 | 270 | uid_t ArgvMap::asUid(const string& arg) |
cc2978dc | 271 | { |
bace7695 | 272 | if (!parmIsset(arg)) { |
725bf803 | 273 | throw ArgException(string("Undefined but needed argument: '") + arg + "'"); |
bace7695 OM |
274 | } |
275 | ||
276 | const auto* cptr_orig = d_params[arg].c_str(); | |
277 | char* cptr_ret = nullptr; | |
cc2978dc | 278 | |
bace7695 | 279 | auto uid = static_cast<uid_t>(strtol(cptr_orig, &cptr_ret, 0)); |
cc2978dc BH |
280 | if (uid == 0 && cptr_ret == cptr_orig) { |
281 | // try to resolve | |
bace7695 OM |
282 | struct passwd* pwent = getpwnam(d_params[arg].c_str()); // NOLINT: called before going multi-threaded |
283 | if (pwent == nullptr) { | |
725bf803 | 284 | throw ArgException("'" + arg + string("' contains invalid group")); |
bace7695 | 285 | } |
cc2978dc | 286 | uid = pwent->pw_uid; |
725bf803 OM |
287 | } |
288 | return uid; | |
cc2978dc | 289 | } |
cc2978dc | 290 | |
725bf803 | 291 | int ArgvMap::asNum(const string& arg, int def) |
12c86877 | 292 | { |
bace7695 | 293 | if (!parmIsset(arg)) { |
725bf803 | 294 | throw ArgException(string("Undefined but needed argument: '") + arg + "'"); |
bace7695 | 295 | } |
12c86877 | 296 | |
ff99a74b | 297 | // use default for empty values |
bace7695 | 298 | if (d_params[arg].empty()) { |
725bf803 | 299 | return def; |
bace7695 | 300 | } |
cc2978dc | 301 | |
bace7695 OM |
302 | const auto* cptr_orig = d_params[arg].c_str(); |
303 | char* cptr_ret = nullptr; | |
304 | auto retval = static_cast<int>(strtol(cptr_orig, &cptr_ret, 0)); | |
305 | if (retval == 0 && cptr_ret == cptr_orig) { | |
725bf803 | 306 | throw ArgException("'" + arg + "' value '" + string(cptr_orig) + string("' is not a valid number")); |
bace7695 | 307 | } |
cc2978dc BH |
308 | |
309 | return retval; | |
310 | } | |
311 | ||
725bf803 | 312 | bool ArgvMap::isEmpty(const string& arg) |
cc2978dc | 313 | { |
bace7695 | 314 | if (!parmIsset(arg)) { |
cc2978dc | 315 | return true; |
bace7695 | 316 | } |
725bf803 | 317 | return d_params[arg].empty(); |
12c86877 BH |
318 | } |
319 | ||
725bf803 | 320 | double ArgvMap::asDouble(const string& arg) |
12c86877 | 321 | { |
bace7695 | 322 | if (!parmIsset(arg)) { |
725bf803 | 323 | throw ArgException(string("Undefined but needed argument: '") + arg + "'"); |
bace7695 | 324 | } |
12c86877 | 325 | |
bace7695 | 326 | if (d_params[arg].empty()) { |
725bf803 | 327 | return 0.0; |
bace7695 | 328 | } |
cc2978dc | 329 | |
bace7695 OM |
330 | const auto* cptr_orig = d_params[arg].c_str(); |
331 | char* cptr_ret = nullptr; | |
332 | auto retval = strtod(cptr_orig, &cptr_ret); | |
3ea9dcbc | 333 | |
bace7695 | 334 | if (retval == 0 && cptr_ret == cptr_orig) { |
725bf803 | 335 | throw ArgException("'" + arg + string("' is not valid double")); |
bace7695 | 336 | } |
cc2978dc BH |
337 | |
338 | return retval; | |
12c86877 BH |
339 | } |
340 | ||
341 | ArgvMap::ArgvMap() | |
342 | { | |
725bf803 | 343 | set("ignore-unknown-settings", "Configuration settings to ignore if they are unknown") = ""; |
12c86877 BH |
344 | } |
345 | ||
725bf803 | 346 | bool ArgvMap::parmIsset(const string& var) |
12c86877 | 347 | { |
9d6e970b | 348 | return d_params.find(var) != d_params.end(); |
12c86877 BH |
349 | } |
350 | ||
3d324e00 | 351 | // ATM Shared between Recursor and Auth, is that a good idea? |
725bf803 OM |
352 | static const map<string, string> deprecateList = { |
353 | {"stats-api-blacklist", "stats-api-disabled-list"}, | |
354 | {"stats-carbon-blacklist", "stats-carbon-disabled-list"}, | |
355 | {"stats-rec-control-blacklist", "stats-rec-control-disabled-list"}, | |
356 | {"stats-snmp-blacklist", "stats-snmp-disabled-list"}, | |
357 | {"edns-subnet-whitelist", "edns-subnet-allow-list"}, | |
358 | {"new-domain-whitelist", "new-domain-ignore-list"}, | |
359 | {"snmp-master-socket", "snmp-daemon-socket"}, | |
360 | {"xpf-allow-from", "Proxy Protocol"}, | |
361 | {"xpf-rr-code", "Proxy Protocol"}, | |
d0fbd333 | 362 | {"domain-metadata-cache-ttl", "zone-metadata-cache-ttl"}, |
3d324e00 O |
363 | }; |
364 | ||
9883d3f9 OM |
365 | // NOLINTNEXTLINE(readability-convert-member-functions-to-static): accesses d_log (compiled out in auth, hence clang-tidy message) |
366 | void ArgvMap::warnIfDeprecated(const string& var) const | |
3d324e00 O |
367 | { |
368 | const auto msg = deprecateList.find(var); | |
369 | if (msg != deprecateList.end()) { | |
ebaffe64 | 370 | SLOG(g_log << Logger::Warning << "'" << var << "' is deprecated and will be removed in a future release, use '" << msg->second << "' instead" << endl, |
7e32a0b9 | 371 | d_log->info(Logr::Warning, "Option is deprecated and will be removed in a future release", "deprecatedName", Logging::Loggable(var), "alternative", Logging::Loggable(msg->second))); |
3d324e00 O |
372 | } |
373 | } | |
374 | ||
9883d3f9 OM |
375 | string ArgvMap::isDeprecated(const string& var) |
376 | { | |
377 | const auto msg = deprecateList.find(var); | |
378 | return msg != deprecateList.end() ? msg->second : ""; | |
379 | } | |
380 | ||
725bf803 | 381 | void ArgvMap::parseOne(const string& arg, const string& parseOnly, bool lax) |
12c86877 | 382 | { |
bace7695 OM |
383 | string var; |
384 | string val; | |
385 | string::size_type pos = 0; | |
5a177409 | 386 | bool incremental = false; |
4d752b05 | 387 | |
725bf803 | 388 | if (arg.find("--") == 0 && (pos = arg.find("+=")) != string::npos) // this is a --port+=25 case |
4d752b05 | 389 | { |
725bf803 OM |
390 | var = arg.substr(2, pos - 2); |
391 | val = arg.substr(pos + 2); | |
4d752b05 KM |
392 | incremental = true; |
393 | } | |
725bf803 | 394 | else if (arg.find("--") == 0 && (pos = arg.find('=')) != string::npos) // this is a --port=25 case |
4d752b05 | 395 | { |
725bf803 OM |
396 | var = arg.substr(2, pos - 2); |
397 | val = arg.substr(pos + 1); | |
4d752b05 | 398 | } |
725bf803 | 399 | else if (arg.find("--") == 0 && (arg.find('=') == string::npos)) // this is a --daemon case |
4d752b05 | 400 | { |
725bf803 OM |
401 | var = arg.substr(2); |
402 | val = ""; | |
4d752b05 | 403 | } |
725bf803 OM |
404 | else if (arg[0] == '-' && arg.length() > 1) { |
405 | var = arg.substr(1); | |
406 | val = ""; | |
12c86877 | 407 | } |
bace7695 | 408 | else { // command |
4d752b05 | 409 | d_cmds.push_back(arg); |
bace7695 | 410 | } |
3ea9dcbc | 411 | |
c63f0951 | 412 | boost::trim(var); |
12c86877 | 413 | |
bace7695 | 414 | if (!var.empty() && (parseOnly.empty() || var == parseOnly)) { |
ebaffe64 OM |
415 | if (!lax) { |
416 | warnIfDeprecated(var); | |
417 | } | |
725bf803 | 418 | pos = val.find_first_not_of(" \t"); // strip leading whitespace |
bace7695 | 419 | if (pos != 0 && pos != string::npos) { |
725bf803 | 420 | val = val.substr(pos); |
bace7695 | 421 | } |
725bf803 OM |
422 | if (parmIsset(var)) { |
423 | if (incremental) { | |
424 | if (d_params[var].empty()) { | |
bace7695 | 425 | if (d_cleared.count(var) == 0) { |
725bf803 | 426 | throw ArgException("Incremental setting '" + var + "' without a parent"); |
bace7695 | 427 | } |
9d6e970b | 428 | d_params[var] = val; |
4d752b05 | 429 | } |
bace7695 | 430 | else { |
9d6e970b | 431 | d_params[var] += ", " + val; |
bace7695 | 432 | } |
4d752b05 | 433 | } |
725bf803 | 434 | else { |
9d6e970b | 435 | d_params[var] = val; |
4d752b05 | 436 | d_cleared.insert(var); |
5a177409 | 437 | } |
3702b428 | 438 | } |
725bf803 | 439 | else { |
9d6e970b | 440 | // unknown setting encountered. see if its on the ignore list before throwing. |
6e01be56 | 441 | vector<string> parts; |
9d6e970b | 442 | stringtok(parts, d_params["ignore-unknown-settings"], " ,\t\n\r"); |
6e01be56 | 443 | if (find(parts.begin(), parts.end(), var) != parts.end()) { |
3b45a434 | 444 | d_unknownParams[var] = std::move(val); |
725bf803 | 445 | SLOG(g_log << Logger::Warning << "Ignoring unknown setting '" << var << "' as requested" << endl, |
ebaffe64 | 446 | d_log->info(Logr::Warning, "Ignoring unknown setting as requested", "name", Logging::Loggable(var))); |
9d6e970b CH |
447 | return; |
448 | } | |
449 | ||
450 | if (!lax) { | |
725bf803 | 451 | throw ArgException("Trying to set unknown setting '" + var + "'"); |
9d6e970b CH |
452 | } |
453 | } | |
12c86877 BH |
454 | } |
455 | } | |
456 | ||
725bf803 | 457 | const vector<string>& ArgvMap::getCommands() |
12c86877 BH |
458 | { |
459 | return d_cmds; | |
460 | } | |
461 | ||
725bf803 | 462 | void ArgvMap::parse(int& argc, char** argv, bool lax) |
12c86877 | 463 | { |
116cda91 | 464 | d_cmds.clear(); |
58a57699 | 465 | d_cleared.clear(); |
bace7695 OM |
466 | for (int i = 1; i < argc; i++) { |
467 | parseOne(argv[i], "", lax); // NOLINT: Posix argument parsing | |
12c86877 BH |
468 | } |
469 | } | |
470 | ||
725bf803 | 471 | void ArgvMap::preParse(int& argc, char** argv, const string& arg) |
12c86877 | 472 | { |
bace7695 OM |
473 | for (int i = 1; i < argc; i++) { |
474 | string varval = argv[i]; // NOLINT: Posix argument parsing | |
475 | if (varval.find("--" + arg) == 0) { | |
476 | parseOne(argv[i]); // NOLINT: Posix argument parsing | |
477 | } | |
12c86877 BH |
478 | } |
479 | } | |
480 | ||
9883d3f9 | 481 | bool ArgvMap::parseFile(const string& fname, const string& arg, bool lax) |
5a249fb1 | 482 | { |
d3002a04 | 483 | string line; |
07b7e0b5 | 484 | string pline; |
07b7e0b5 | 485 | |
564db8c5 | 486 | std::ifstream configFileStream(fname); |
e64bde34 | 487 | if (!configFileStream) { |
d3002a04 | 488 | return false; |
5a249fb1 | 489 | } |
07b7e0b5 | 490 | |
e64bde34 | 491 | while (getline(configFileStream, pline)) { |
dc593046 | 492 | boost::trim_right(pline); |
3ea9dcbc | 493 | |
5a249fb1 FM |
494 | if (!pline.empty() && pline[pline.size() - 1] == '\\') { |
495 | line += pline.substr(0, pline.length() - 1); | |
d3002a04 AT |
496 | continue; |
497 | } | |
e64bde34 FM |
498 | |
499 | line += pline; | |
d3002a04 AT |
500 | |
501 | // strip everything after a # | |
e64bde34 FM |
502 | string::size_type pos = line.find('#'); |
503 | if (pos != string::npos) { | |
d3002a04 AT |
504 | // make sure it's either first char or has whitespace before |
505 | // fixes issue #354 | |
e64bde34 | 506 | if (pos == 0 || (std::isspace(line[pos - 1]) != 0)) { |
5a249fb1 FM |
507 | line = line.substr(0, pos); |
508 | } | |
d3002a04 AT |
509 | } |
510 | ||
511 | // strip trailing spaces | |
dc593046 | 512 | boost::trim_right(line); |
07b7e0b5 | 513 | |
d3002a04 | 514 | // strip leading spaces |
e64bde34 FM |
515 | pos = line.find_first_not_of(" \t\r\n"); |
516 | if (pos != string::npos) { | |
5a249fb1 FM |
517 | line = line.substr(pos); |
518 | } | |
07b7e0b5 | 519 | |
d3002a04 AT |
520 | // gpgsql-basic-query=sdfsdfs dfsdfsdf sdfsdfsfd |
521 | ||
5a249fb1 FM |
522 | parseOne(string("--") + line, arg, lax); |
523 | line = ""; | |
d3002a04 | 524 | } |
07b7e0b5 | 525 | |
07b7e0b5 AT |
526 | return true; |
527 | } | |
528 | ||
9883d3f9 | 529 | bool ArgvMap::preParseFile(const string& fname, const string& arg, const string& theDefault) |
12c86877 | 530 | { |
9d6e970b | 531 | d_params[arg] = theDefault; |
b56ac6fd | 532 | |
d3002a04 | 533 | return parseFile(fname, arg, false); |
12c86877 BH |
534 | } |
535 | ||
9883d3f9 | 536 | bool ArgvMap::file(const string& fname, bool lax) |
4f6ef75f | 537 | { |
fe6861c2 | 538 | return file(fname, lax, false); |
4f6ef75f AT |
539 | } |
540 | ||
9883d3f9 | 541 | bool ArgvMap::file(const string& fname, bool lax, bool included) |
12c86877 | 542 | { |
5a249fb1 FM |
543 | if (!parmIsset("include-dir")) { // inject include-dir |
544 | set("include-dir", "Directory to include configuration files from"); | |
545 | } | |
4f6ef75f | 546 | |
5a249fb1 | 547 | if (!parseFile(fname, "", lax)) { |
ebaffe64 | 548 | SLOG(g_log << Logger::Warning << "Unable to open " << fname << std::endl, |
ed3ec74d | 549 | d_log->error(Logr::Warning, "Unable to open file", "name", Logging::Loggable(fname))); |
d3002a04 AT |
550 | return false; |
551 | } | |
eefd15f9 | 552 | |
4f6ef75f | 553 | // handle include here (avoid re-include) |
9d6e970b | 554 | if (!included && !d_params["include-dir"].empty()) { |
93d7e233 | 555 | std::vector<std::string> extraConfigs; |
9883d3f9 | 556 | gatherIncludes(d_params["include-dir"], ".conf", extraConfigs); |
5a249fb1 FM |
557 | for (const std::string& filename : extraConfigs) { |
558 | if (!file(filename.c_str(), lax, true)) { | |
559 | SLOG(g_log << Logger::Error << filename << " could not be parsed" << std::endl, | |
560 | d_log->info(Logr::Error, "Unable to parse config file", "name", Logging::Loggable(filename))); | |
561 | throw ArgException(filename + " could not be parsed"); | |
4f6ef75f | 562 | } |
93d7e233 | 563 | } |
93d7e233 | 564 | } |
8a16d778 | 565 | |
93d7e233 AT |
566 | return true; |
567 | } | |
8a16d778 | 568 | |
9883d3f9 OM |
569 | // NOLINTNEXTLINE(readability-convert-member-functions-to-static): accesses d_log (compiled out in auth, hence clang-tidy message) |
570 | void ArgvMap::gatherIncludes(const std::string& directory, const std::string& suffix, std::vector<std::string>& extraConfigs) | |
725bf803 | 571 | { |
9883d3f9 | 572 | if (directory.empty()) { |
a73da04b | 573 | return; // nothing to do |
bace7695 | 574 | } |
a73da04b | 575 | |
9883d3f9 | 576 | std::vector<std::string> vec; |
d3190b7d RG |
577 | auto directoryError = pdns::visit_directory(directory, [this, &directory, &suffix, &vec]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) { |
578 | (void)this; | |
579 | if (boost::starts_with(name, ".")) { | |
580 | return true; // skip any dots | |
bace7695 | 581 | } |
d3190b7d | 582 | if (boost::ends_with(name, suffix)) { |
a73da04b | 583 | // build name |
d3190b7d | 584 | string fullName = directory + "/" + std::string(name); |
a73da04b | 585 | // ensure it's readable file |
bace7695 OM |
586 | struct stat statInfo |
587 | { | |
588 | }; | |
d3190b7d RG |
589 | if (stat(fullName.c_str(), &statInfo) != 0 || !S_ISREG(statInfo.st_mode)) { |
590 | string msg = fullName + " is not a regular file"; | |
ebaffe64 | 591 | SLOG(g_log << Logger::Error << msg << std::endl, |
d3190b7d | 592 | d_log->info(Logr::Error, "Unable to open non-regular file", "name", Logging::Loggable(fullName))); |
a73da04b | 593 | throw ArgException(msg); |
93d7e233 | 594 | } |
d3190b7d | 595 | vec.emplace_back(fullName); |
93d7e233 | 596 | } |
d3190b7d RG |
597 | return true; |
598 | }); | |
599 | ||
600 | if (directoryError) { | |
601 | int err = errno; | |
602 | string msg = directory + " is not accessible: " + stringerror(err); | |
603 | SLOG(g_log << Logger::Error << msg << std::endl, | |
604 | d_log->error(Logr::Error, err, "Directory is not accessible", "name", Logging::Loggable(directory))); | |
605 | throw ArgException(msg); | |
a73da04b | 606 | } |
d3190b7d | 607 | |
9883d3f9 OM |
608 | std::sort(vec.begin(), vec.end(), CIStringComparePOSIX()); |
609 | extraConfigs.insert(extraConfigs.end(), vec.begin(), vec.end()); | |
12c86877 | 610 | } |