]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/arguments.cc
Merge pull request #1093 from zeha/restore-bindparser-h
[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*/
12c86877 22#include "arguments.hh"
5b2cb3be 23#include <boost/algorithm/string.hpp>
2dfec518 24#include <boost/algorithm/string/compare.hpp>
4f6ef75f 25#include <boost/algorithm/string/predicate.hpp>
4f6ef75f 26#include <boost/foreach.hpp>
61b26744 27#include "namespaces.hh"
4f6ef75f 28#include "logger.hh"
8a16d778
AT
29#include <sys/types.h>
30#include <dirent.h>
31#include <sys/stat.h>
32#include <unistd.h>
12c86877 33
12c86877
BH
34const ArgvMap::param_t::const_iterator ArgvMap::begin()
35{
36 return params.begin();
37}
38
39const ArgvMap::param_t::const_iterator ArgvMap::end()
40{
41 return params.end();
42}
43
44string & ArgvMap::set(const string &var)
45{
46 return params[var];
47}
48
49bool ArgvMap::mustDo(const string &var)
bd11bd1d 50{
12c86877
BH
51 return ((*this)[var]!="no") && ((*this)[var]!="off");
52}
53
54vector<string>ArgvMap::list()
55{
56 vector<string> ret;
57 for(map<string,string>::const_iterator i=params.begin();i!=params.end();++i)
58 ret.push_back(i->first);
59 return ret;
60}
61
62string ArgvMap::getHelp(const string &item)
63{
64 return helpmap[item];
65}
66
67string & ArgvMap::set(const string &var, const string &help)
68{
69 helpmap[var]=help;
70 d_typeMap[var]="Parameter";
71 return set(var);
72}
73
74void ArgvMap::setCmd(const string &var, const string &help)
75{
76 helpmap[var]=help;
77 d_typeMap[var]="Command";
78 set(var)="no";
79}
80
81string & ArgvMap::setSwitch(const string &var, const string &help)
82{
83 helpmap[var]=help;
84 d_typeMap[var]="Switch";
85 return set(var);
86}
87
88
12c86877
BH
89bool ArgvMap::contains(const string &var, const string &val)
90{
102eb646
BH
91 params_t::const_iterator param = params.find(var);
92 if(param == params.end() || param->second.empty()) {
93 return false;
94 }
562588a3
BH
95 vector<string> parts;
96 vector<string>::const_iterator i;
97
102eb646 98 stringtok( parts, param->second, ", \t" );
562588a3
BH
99 for( i = parts.begin(); i != parts.end(); i++ ) {
100 if( *i == val ) {
101 return true;
102 }
103 }
104
105 return false;
12c86877
BH
106}
107
12c86877
BH
108string ArgvMap::helpstring(string prefix)
109{
110 if(prefix=="no")
111 prefix="";
112
113 string help;
114
115 for(map<string,string>::const_iterator i=helpmap.begin();
116 i!=helpmap.end();
117 i++)
118 {
119 if(!prefix.empty() && i->first.find(prefix)) // only print items with prefix
4957a608 120 continue;
12c86877
BH
121
122 help+=" --";
123 help+=i->first;
124
125 string type=d_typeMap[i->first];
126
127 if(type=="Parameter")
4957a608 128 help+="=...";
12c86877 129 else if(type=="Switch")
4957a608
BH
130 {
131 help+=" | --"+i->first+"=yes";
132 help+=" | --"+i->first+"=no";
133 }
12c86877
BH
134
135
136 help+="\n\t";
137 help+=i->second;
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
c7efa8ff
RA
153 for(map<string,string>::const_iterator i=helpmap.begin(); i!=helpmap.end(); i++) {
154 if(d_typeMap[i->first]=="Command")
155 continue;
156
157 help+="#################################\n";
158 help+="# ";
159 help+=i->first;
160 help+="\t";
161 help+=i->second;
162 help+="\n#\n";
163 if (current) {
164 help+=i->first+"="+params[i->first]+"\n\n";
165 } else {
166 help+="# "+i->first+"="+params[i->first]+"\n\n";
12c86877 167 }
c7efa8ff 168 }
12c86877
BH
169 return help;
170}
171
172
173const string & ArgvMap::operator[](const string &arg)
174{
175 if(!parmIsset(arg))
176 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
177
178
179 return params[arg];
180}
181
cc2978dc
BH
182mode_t ArgvMap::asMode(const string &arg)
183{
184 mode_t mode;
185 const char *cptr_orig;
186 char *cptr_ret = NULL;
187
188 if(!parmIsset(arg))
189 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
190
191 cptr_orig = params[arg].c_str();
192 mode = static_cast<mode_t>(strtol(cptr_orig, &cptr_ret, 8));
193 if (mode == 0 && cptr_ret == cptr_orig)
194 throw ArgException("'" + arg + string("' contains invalid octal mode"));
195 return mode;
196}
197
198gid_t ArgvMap::asGid(const string &arg)
199{
200 gid_t gid;
201 const char *cptr_orig;
202 char *cptr_ret = NULL;
203
204 if(!parmIsset(arg))
205 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
206
207 cptr_orig = params[arg].c_str();
208 gid = static_cast<gid_t>(strtol(cptr_orig, &cptr_ret, 0));
209 if (gid == 0 && cptr_ret == cptr_orig) {
210 // try to resolve
211 struct group *group = getgrnam(params[arg].c_str());
212 if (group == NULL)
213 throw ArgException("'" + arg + string("' contains invalid group"));
214 gid = group->gr_gid;
215 }
216 return gid;
217}
218
219uid_t ArgvMap::asUid(const string &arg)
220{
221 uid_t uid;
222 const char *cptr_orig;
223 char *cptr_ret = NULL;
224
225 if(!parmIsset(arg))
226 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
227
228 cptr_orig = params[arg].c_str();
229 uid = static_cast<uid_t>(strtol(cptr_orig, &cptr_ret, 0));
230 if (uid == 0 && cptr_ret == cptr_orig) {
231 // try to resolve
232 struct passwd *pwent = getpwnam(params[arg].c_str());
233 if (pwent == NULL)
234 throw ArgException("'" + arg + string("' contains invalid group"));
235 uid = pwent->pw_uid;
236 }
237 return uid;
238}
cc2978dc 239
12c86877
BH
240int ArgvMap::asNum(const string &arg)
241{
cc2978dc
BH
242 int retval;
243 const char *cptr_orig;
244 char *cptr_ret = NULL;
245
12c86877
BH
246 if(!parmIsset(arg))
247 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
248
cc2978dc
BH
249 // treat empty values as zeros
250 if (params[arg].empty())
251 return 0;
252
253 cptr_orig = params[arg].c_str();
254 retval = static_cast<int>(strtol(cptr_orig, &cptr_ret, 0));
255 if (!retval && cptr_ret == cptr_orig)
256 throw ArgException("'"+arg+string("' is not valid number"));
257
258 return retval;
259}
260
261bool ArgvMap::isEmpty(const string &arg)
262{
263 if(!parmIsset(arg))
264 return true;
265 return params[arg].empty();
12c86877
BH
266}
267
268double ArgvMap::asDouble(const string &arg)
269{
cc2978dc
BH
270 double retval;
271 const char *cptr_orig;
272 char *cptr_ret = NULL;
273
12c86877
BH
274 if(!parmIsset(arg))
275 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
276
cc2978dc
BH
277 if (params[arg].empty())
278 return 0.0;
279
280 cptr_orig = params[arg].c_str();
281 retval = strtod(cptr_orig, &cptr_ret);
282
283 if (retval == 0 && cptr_ret == cptr_orig)
284 throw ArgException("'"+arg+string("' is not valid double"));
285
286 return retval;
12c86877
BH
287}
288
289ArgvMap::ArgvMap()
290{
291
292}
293
294bool ArgvMap::parmIsset(const string &var)
295{
296 return (params.find(var)!=params.end());
297}
298
299void ArgvMap::parseOne(const string &arg, const string &parseOnly, bool lax)
300{
301 string var, val;
bdf40704 302 string::size_type pos;
5a177409 303 bool incremental = false;
4d752b05
KM
304
305 if(!arg.find("--") && (pos=arg.find("+="))!=string::npos) // this is a --port+=25 case
306 {
307 var=arg.substr(2,pos-2);
308 val=arg.substr(pos+2);
309 incremental = true;
310 }
311 else if(!arg.find("--") && (pos=arg.find("="))!=string::npos) // this is a --port=25 case
312 {
313 var=arg.substr(2,pos-2);
314 val=arg.substr(pos+1);
315 }
12c86877 316 else if(!arg.find("--") && (arg.find("=")==string::npos)) // this is a --daemon case
4d752b05
KM
317 {
318 var=arg.substr(2);
319 val="";
320 }
12c86877 321 else if(arg[0]=='-')
4d752b05
KM
322 {
323 var=arg.substr(1);
324 val="";
12c86877 325 }
4d752b05
KM
326 else // command
327 d_cmds.push_back(arg);
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
KM
352 else if(!lax)
353 throw ArgException("Trying to set unexisting 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];
375 if(!varval.find("--"+arg))
376 parseOne(argv[n]);
377 }
378}
379
f64de501 380bool ArgvMap::preParseFile(const char *fname, const string &arg, const string& theDefault)
12c86877 381{
f64de501 382 params[arg]=theDefault;
b56ac6fd 383
12c86877 384 ifstream f(fname);
eefd15f9 385 if(!f)
12c86877 386 return false;
eefd15f9 387
12c86877
BH
388 string line;
389 string pline;
bdf40704 390 string::size_type pos;
12c86877
BH
391
392 while(getline(f,pline)) {
5b2cb3be
BH
393 trim_right(pline);
394
12c86877
BH
395 if(pline[pline.size()-1]=='\\') {
396 line+=pline.substr(0,pline.length()-1);
397 continue;
398 }
eefd15f9 399 else
12c86877 400 line+=pline;
12c86877
BH
401
402 // strip everything after a #
403 if((pos=line.find("#"))!=string::npos)
404 line=line.substr(0,pos);
405
406 // strip trailing spaces
5b2cb3be 407 trim_right(line);
eefd15f9 408
12c86877
BH
409 // strip leading spaces
410 if((pos=line.find_first_not_of(" \t\r\n"))!=string::npos)
411 line=line.substr(pos);
eefd15f9 412
12c86877
BH
413 // gpgsql-basic-query=sdfsdfs dfsdfsdf sdfsdfsfd
414
eefd15f9 415 parseOne( string("--") + line, arg );
12c86877
BH
416 line="";
417 }
eefd15f9 418
12c86877
BH
419 return true;
420}
421
12c86877 422bool ArgvMap::file(const char *fname, bool lax)
4f6ef75f
AT
423{
424 return file(fname,lax,false);
425}
426
427bool ArgvMap::file(const char *fname, bool lax, bool included)
12c86877
BH
428{
429 ifstream f(fname);
430 if(!f) {
12c86877
BH
431 return false;
432 }
eefd15f9 433
8a16d778 434 if (!parmIsset("include-dir")) // inject include-dir
4f6ef75f
AT
435 set("include-dir","Directory to include configuration files from");
436
12c86877
BH
437 string line;
438 string pline;
bdf40704 439 string::size_type pos;
eefd15f9 440
12c86877 441 while(getline(f,pline)) {
5b2cb3be 442 trim_right(pline);
ccd3ed60
BH
443 if(pline.empty())
444 continue;
12c86877
BH
445
446 if(pline[pline.size()-1]=='\\') {
447 line+=pline.substr(0,pline.length()-1);
448
449 continue;
450 }
eefd15f9 451 else
12c86877
BH
452 line+=pline;
453
454 // strip everything after a #
455 if((pos=line.find("#"))!=string::npos)
456 line=line.substr(0,pos);
457
458 // strip trailing spaces
9ff13837 459 trim(line);
eefd15f9
BH
460
461 parseOne(string("--")+line,"",lax);
12c86877
BH
462 line="";
463 }
eefd15f9 464
4f6ef75f 465 // handle include here (avoid re-include)
8a16d778 466 if (!included && !params["include-dir"].empty()) {
4f6ef75f 467 // rerun parser for all files
8a16d778
AT
468 struct stat st;
469 DIR *dir;
470 struct dirent *ent;
471 char namebuf[PATH_MAX] = {0};
472
473 // stat
474 if (stat(params["include-dir"].c_str(), &st)) {
4f6ef75f
AT
475 L << Logger::Error << params["include-dir"] << " does not exist!" << std::endl;
476 throw ArgException(params["include-dir"] + " does not exist!");
477 }
8a16d778
AT
478
479 // wonder if it's accessible directory
480 if (!S_ISDIR(st.st_mode)) {
481 L << Logger::Error << params["include-dir"] << " is not a directory" << std::endl;
482 throw ArgException(params["include-dir"] + " is not a directory");
483 }
484
485 if (!(dir = opendir(params["include-dir"].c_str()))) {
486 L << Logger::Error << params["include-dir"] << " is not accessible" << std::endl;
487 throw ArgException(params["include-dir"] + " is not accessible");
488 }
2dfec518 489
2aa1b703 490 std::vector<std::string> extraConfigs;
8a16d778
AT
491 while((ent = readdir(dir)) != NULL) {
492 if (ent->d_name[0] == '.') continue; // skip any dots
493 if (boost::ends_with(ent->d_name, ".conf")) {
494 // ensure it's readable file
495 snprintf(namebuf, sizeof namebuf, "%s/%s", params["include-dir"].c_str(), ent->d_name);
496 if (stat(namebuf, &st) || !S_ISREG(st.st_mode)) {
497 L << Logger::Error << namebuf << " is not a file" << std::endl;
498 throw ArgException(std::string(namebuf) + " does not exist!");
499 }
2dfec518 500 extraConfigs.push_back(std::string(namebuf));
8a16d778 501 }
4f6ef75f 502 }
2aa1b703 503 std::sort(extraConfigs.begin(), extraConfigs.end(), CIStringComparePOSIX());
2dfec518 504 BOOST_FOREACH(const std::string& fn, extraConfigs) {
2dfec518
AT
505 if (!file(fn.c_str(), lax, true)) {
506 L << Logger::Error << namebuf << " could not be parsed" << std::endl;
507 throw ArgException(fn + " could not be parsed");
508 }
509 }
da748130
CH
510
511 closedir(dir);
4f6ef75f
AT
512 }
513
12c86877
BH
514 return true;
515}