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