]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/arguments.cc
Merge pull request #7945 from pieterlexis/syncres-CNAME-cache-cleanup
[thirdparty/pdns.git] / pdns / arguments.cc
CommitLineData
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>
64f3490e 36#include <limits.h>
12c86877 37
12c86877
BH
38const ArgvMap::param_t::const_iterator ArgvMap::begin()
39{
40 return params.begin();
41}
42
43const ArgvMap::param_t::const_iterator ArgvMap::end()
44{
45 return params.end();
46}
47
48string & ArgvMap::set(const string &var)
49{
50 return params[var];
51}
52
53bool ArgvMap::mustDo(const string &var)
bd11bd1d 54{
12c86877
BH
55 return ((*this)[var]!="no") && ((*this)[var]!="off");
56}
57
58vector<string>ArgvMap::list()
59{
60 vector<string> ret;
61 for(map<string,string>::const_iterator i=params.begin();i!=params.end();++i)
62 ret.push_back(i->first);
63 return ret;
64}
65
66string ArgvMap::getHelp(const string &item)
67{
68 return helpmap[item];
69}
70
71string & ArgvMap::set(const string &var, const string &help)
72{
73 helpmap[var]=help;
74 d_typeMap[var]="Parameter";
75 return set(var);
76}
77
78void ArgvMap::setCmd(const string &var, const string &help)
79{
80 helpmap[var]=help;
81 d_typeMap[var]="Command";
82 set(var)="no";
83}
84
85string & ArgvMap::setSwitch(const string &var, const string &help)
86{
87 helpmap[var]=help;
88 d_typeMap[var]="Switch";
89 return set(var);
90}
91
92
12c86877
BH
93bool ArgvMap::contains(const string &var, const string &val)
94{
102eb646
BH
95 params_t::const_iterator param = params.find(var);
96 if(param == params.end() || param->second.empty()) {
97 return false;
98 }
562588a3 99 vector<string> parts;
562588a3 100
102eb646 101 stringtok( parts, param->second, ", \t" );
93a471a3
CHB
102 for (const auto& part: parts) {
103 if (part == val) {
562588a3
BH
104 return true;
105 }
106 }
107
108 return false;
12c86877
BH
109}
110
12c86877
BH
111string ArgvMap::helpstring(string prefix)
112{
113 if(prefix=="no")
114 prefix="";
115
116 string help;
93a471a3
CHB
117
118 for (const auto& i: helpmap) {
119 if(!prefix.empty() && i.first.find(prefix) != 0) // only print items with prefix
120 continue;
12c86877
BH
121
122 help+=" --";
93a471a3 123 help+=i.first;
12c86877 124
93a471a3 125 string type=d_typeMap[i.first];
12c86877
BH
126
127 if(type=="Parameter")
4957a608 128 help+="=...";
12c86877 129 else if(type=="Switch")
4957a608 130 {
93a471a3
CHB
131 help+=" | --"+i.first+"=yes";
132 help+=" | --"+i.first+"=no";
4957a608 133 }
12c86877
BH
134
135
136 help+="\n\t";
93a471a3 137 help+=i.second;
12c86877
BH
138 help+="\n";
139
140 }
141 return help;
142}
143
6bef3d91 144string ArgvMap::configstring(bool current)
12c86877
BH
145{
146 string help;
6bef3d91
RA
147
148 if (current)
149 help="# Autogenerated configuration file based on running instance\n";
150 else
151 help="# Autogenerated configuration file template\n";
12c86877 152
93a471a3
CHB
153 for(const auto& i: helpmap) {
154 if(d_typeMap[i.first]=="Command")
c7efa8ff
RA
155 continue;
156
157 help+="#################################\n";
158 help+="# ";
93a471a3 159 help+=i.first;
c7efa8ff 160 help+="\t";
93a471a3 161 help+=i.second;
c7efa8ff
RA
162 help+="\n#\n";
163 if (current) {
93a471a3 164 help+=i.first+"="+params[i.first]+"\n\n";
c7efa8ff 165 } else {
93a471a3 166 help+="# "+i.first+"="+params[i.first]+"\n\n";
12c86877 167 }
c7efa8ff 168 }
12c86877
BH
169 return help;
170}
171
12c86877
BH
172const string & ArgvMap::operator[](const string &arg)
173{
174 if(!parmIsset(arg))
175 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
176
12c86877
BH
177 return params[arg];
178}
179
cc2978dc
BH
180mode_t ArgvMap::asMode(const string &arg)
181{
182 mode_t mode;
183 const char *cptr_orig;
184 char *cptr_ret = NULL;
185
186 if(!parmIsset(arg))
187 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
188
189 cptr_orig = params[arg].c_str();
190 mode = static_cast<mode_t>(strtol(cptr_orig, &cptr_ret, 8));
191 if (mode == 0 && cptr_ret == cptr_orig)
192 throw ArgException("'" + arg + string("' contains invalid octal mode"));
193 return mode;
194}
195
196gid_t ArgvMap::asGid(const string &arg)
197{
198 gid_t gid;
199 const char *cptr_orig;
200 char *cptr_ret = NULL;
201
202 if(!parmIsset(arg))
203 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
204
205 cptr_orig = params[arg].c_str();
206 gid = static_cast<gid_t>(strtol(cptr_orig, &cptr_ret, 0));
207 if (gid == 0 && cptr_ret == cptr_orig) {
208 // try to resolve
209 struct group *group = getgrnam(params[arg].c_str());
210 if (group == NULL)
211 throw ArgException("'" + arg + string("' contains invalid group"));
212 gid = group->gr_gid;
213 }
214 return gid;
215}
216
217uid_t ArgvMap::asUid(const string &arg)
218{
219 uid_t uid;
220 const char *cptr_orig;
221 char *cptr_ret = NULL;
222
223 if(!parmIsset(arg))
224 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
225
226 cptr_orig = params[arg].c_str();
227 uid = static_cast<uid_t>(strtol(cptr_orig, &cptr_ret, 0));
228 if (uid == 0 && cptr_ret == cptr_orig) {
229 // try to resolve
230 struct passwd *pwent = getpwnam(params[arg].c_str());
231 if (pwent == NULL)
232 throw ArgException("'" + arg + string("' contains invalid group"));
233 uid = pwent->pw_uid;
234 }
235 return uid;
236}
cc2978dc 237
ff99a74b 238int ArgvMap::asNum(const string &arg, int def)
12c86877 239{
cc2978dc
BH
240 int retval;
241 const char *cptr_orig;
242 char *cptr_ret = NULL;
243
12c86877
BH
244 if(!parmIsset(arg))
245 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
246
ff99a74b 247 // use default for empty values
cc2978dc 248 if (params[arg].empty())
ff99a74b 249 return def;
cc2978dc
BH
250
251 cptr_orig = params[arg].c_str();
252 retval = static_cast<int>(strtol(cptr_orig, &cptr_ret, 0));
253 if (!retval && cptr_ret == cptr_orig)
fd378ba9 254 throw ArgException("'"+arg+"' value '"+string(cptr_orig) + string( "' is not a valid number"));
cc2978dc
BH
255
256 return retval;
257}
258
259bool ArgvMap::isEmpty(const string &arg)
260{
261 if(!parmIsset(arg))
262 return true;
263 return params[arg].empty();
12c86877
BH
264}
265
266double ArgvMap::asDouble(const string &arg)
267{
cc2978dc
BH
268 double retval;
269 const char *cptr_orig;
270 char *cptr_ret = NULL;
271
12c86877
BH
272 if(!parmIsset(arg))
273 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
274
cc2978dc
BH
275 if (params[arg].empty())
276 return 0.0;
277
278 cptr_orig = params[arg].c_str();
279 retval = strtod(cptr_orig, &cptr_ret);
280
281 if (retval == 0 && cptr_ret == cptr_orig)
282 throw ArgException("'"+arg+string("' is not valid double"));
283
284 return retval;
12c86877
BH
285}
286
287ArgvMap::ArgvMap()
288{
289
290}
291
292bool ArgvMap::parmIsset(const string &var)
293{
294 return (params.find(var)!=params.end());
295}
296
297void ArgvMap::parseOne(const string &arg, const string &parseOnly, bool lax)
298{
299 string var, val;
bdf40704 300 string::size_type pos;
5a177409 301 bool incremental = false;
4d752b05 302
a350fc7a 303 if(arg.find("--") == 0 && (pos=arg.find("+="))!=string::npos) // this is a --port+=25 case
4d752b05
KM
304 {
305 var=arg.substr(2,pos-2);
306 val=arg.substr(pos+2);
307 incremental = true;
308 }
a350fc7a 309 else if(arg.find("--") == 0 && (pos=arg.find("="))!=string::npos) // this is a --port=25 case
4d752b05
KM
310 {
311 var=arg.substr(2,pos-2);
312 val=arg.substr(pos+1);
313 }
a350fc7a 314 else if(arg.find("--") == 0 && (arg.find("=")==string::npos)) // this is a --daemon case
4d752b05
KM
315 {
316 var=arg.substr(2);
317 val="";
318 }
12c86877 319 else if(arg[0]=='-')
4d752b05
KM
320 {
321 var=arg.substr(1);
322 val="";
12c86877 323 }
4d752b05
KM
324 else // command
325 d_cmds.push_back(arg);
c63f0951
AT
326
327 boost::trim(var);
12c86877
BH
328
329 if(var!="" && (parseOnly.empty() || var==parseOnly)) {
bd11bd1d 330 pos=val.find_first_not_of(" \t"); // strip leading whitespace
3752660e 331 if(pos && pos!=string::npos)
bd11bd1d 332 val=val.substr(pos);
4d752b05
KM
333 if(parmIsset(var))
334 {
335 if(incremental)
336 {
337 if(params[var].empty())
338 {
339 if(!d_cleared.count(var))
340 throw ArgException("Incremental parameter '"+var+"' without a parent");
341 params[var]=val;
342 }
343 else
344 params[var]+=", " + val;
345 }
346 else
347 {
348 params[var]=val;
349 d_cleared.insert(var);
5a177409 350 }
3702b428 351 }
4d752b05 352 else if(!lax)
a67dd0cf 353 throw ArgException("Trying to set unknown parameter '"+var+"'");
12c86877
BH
354 }
355}
356
357const vector<string>&ArgvMap::getCommands()
358{
359 return d_cmds;
360}
361
362void ArgvMap::parse(int &argc, char **argv, bool lax)
363{
116cda91 364 d_cmds.clear();
58a57699 365 d_cleared.clear();
12c86877
BH
366 for(int n=1;n<argc;n++) {
367 parseOne(argv[n],"",lax);
368 }
369}
370
371void ArgvMap::preParse(int &argc, char **argv, const string &arg)
372{
373 for(int n=1;n<argc;n++) {
374 string varval=argv[n];
a350fc7a 375 if(varval.find("--"+arg) == 0)
12c86877
BH
376 parseOne(argv[n]);
377 }
378}
379
d3002a04
AT
380bool ArgvMap::parseFile(const char *fname, const string& arg, bool lax) {
381 string line;
07b7e0b5
AT
382 string pline;
383 string::size_type pos;
384
d3002a04
AT
385 ifstream f(fname);
386 if(!f)
387 return false;
07b7e0b5 388
d3002a04
AT
389 while(getline(f,pline)) {
390 trim_right(pline);
391
83281a74 392 if(!pline.empty() && pline[pline.size()-1]=='\\') {
d3002a04
AT
393 line+=pline.substr(0,pline.length()-1);
394 continue;
395 }
396 else
397 line+=pline;
398
399 // strip everything after a #
400 if((pos=line.find("#"))!=string::npos) {
401 // make sure it's either first char or has whitespace before
402 // fixes issue #354
403 if (pos == 0 || std::isspace(line[pos-1]))
404 line=line.substr(0,pos);
405 }
406
407 // strip trailing spaces
408 trim_right(line);
07b7e0b5 409
d3002a04
AT
410 // strip leading spaces
411 if((pos=line.find_first_not_of(" \t\r\n"))!=string::npos)
412 line=line.substr(pos);
07b7e0b5 413
d3002a04
AT
414 // gpgsql-basic-query=sdfsdfs dfsdfsdf sdfsdfsfd
415
416 parseOne( string("--") + line, arg, lax );
417 line="";
418 }
07b7e0b5 419
07b7e0b5
AT
420 return true;
421}
422
423
f64de501 424bool ArgvMap::preParseFile(const char *fname, const string &arg, const string& theDefault)
12c86877 425{
f64de501 426 params[arg]=theDefault;
b56ac6fd 427
d3002a04 428 return parseFile(fname, arg, false);
12c86877
BH
429}
430
12c86877 431bool ArgvMap::file(const char *fname, bool lax)
4f6ef75f
AT
432{
433 return file(fname,lax,false);
434}
435
436bool ArgvMap::file(const char *fname, bool lax, bool included)
12c86877 437{
8a16d778 438 if (!parmIsset("include-dir")) // inject include-dir
4f6ef75f
AT
439 set("include-dir","Directory to include configuration files from");
440
d3002a04 441 if(!parseFile(fname, "", lax)) {
e6a9dde5 442 g_log << Logger::Warning << "Unable to open " << fname << std::endl;
d3002a04
AT
443 return false;
444 }
eefd15f9 445
4f6ef75f 446 // handle include here (avoid re-include)
8a16d778 447 if (!included && !params["include-dir"].empty()) {
93d7e233
AT
448 std::vector<std::string> extraConfigs;
449 gatherIncludes(extraConfigs);
ef7cd021 450 for(const std::string& fn : extraConfigs) {
93d7e233 451 if (!file(fn.c_str(), lax, true)) {
e6a9dde5 452 g_log << Logger::Error << fn << " could not be parsed" << std::endl;
93d7e233 453 throw ArgException(fn + " could not be parsed");
4f6ef75f 454 }
93d7e233 455 }
93d7e233 456 }
8a16d778 457
93d7e233
AT
458 return true;
459}
8a16d778 460
93d7e233
AT
461void ArgvMap::gatherIncludes(std::vector<std::string> &extraConfigs) {
462 extraConfigs.clear();
463 if (params["include-dir"].empty()) return; // nothing to do
464 struct stat st;
465 DIR *dir;
466 struct dirent *ent;
467
468 // stat
469 if (stat(params["include-dir"].c_str(), &st)) {
e6a9dde5 470 g_log << Logger::Error << params["include-dir"] << " does not exist!" << std::endl;
93d7e233
AT
471 throw ArgException(params["include-dir"] + " does not exist!");
472 }
da748130 473
93d7e233
AT
474 // wonder if it's accessible directory
475 if (!S_ISDIR(st.st_mode)) {
e6a9dde5 476 g_log << Logger::Error << params["include-dir"] << " is not a directory" << std::endl;
93d7e233
AT
477 throw ArgException(params["include-dir"] + " is not a directory");
478 }
4f6ef75f 479
93d7e233 480 if (!(dir = opendir(params["include-dir"].c_str()))) {
e6a9dde5 481 g_log << Logger::Error << params["include-dir"] << " is not accessible" << std::endl;
93d7e233
AT
482 throw ArgException(params["include-dir"] + " is not accessible");
483 }
484
485 while((ent = readdir(dir)) != NULL) {
486 if (ent->d_name[0] == '.') continue; // skip any dots
487 if (boost::ends_with(ent->d_name, ".conf")) {
488 // build name
489 std::ostringstream namebuf;
490 namebuf << params["include-dir"].c_str() << "/" << ent->d_name; // FIXME: Use some path separator
491 // ensure it's readable file
492 if (stat(namebuf.str().c_str(), &st) || !S_ISREG(st.st_mode)) {
e6a9dde5 493 g_log << Logger::Error << namebuf.str() << " is not a file" << std::endl;
d7c676a5 494 closedir(dir);
93d7e233
AT
495 throw ArgException(namebuf.str() + " does not exist!");
496 }
497 extraConfigs.push_back(namebuf.str());
498 }
499 }
500 std::sort(extraConfigs.begin(), extraConfigs.end(), CIStringComparePOSIX());
829849d6 501 closedir(dir);
12c86877 502}