]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/arguments.cc
silence dead code warning in version.cc
[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
BH
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
06bd9ccf 16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
12c86877 17*/
12c86877 18#include "arguments.hh"
5b2cb3be 19#include <boost/algorithm/string.hpp>
2dfec518 20#include <boost/algorithm/string/compare.hpp>
4f6ef75f 21#include <boost/algorithm/string/predicate.hpp>
4f6ef75f 22#include <boost/foreach.hpp>
61b26744 23#include "namespaces.hh"
4f6ef75f 24#include "logger.hh"
8a16d778
AT
25#include <sys/types.h>
26#include <dirent.h>
27#include <sys/stat.h>
28#include <unistd.h>
12c86877 29
12c86877
BH
30const ArgvMap::param_t::const_iterator ArgvMap::begin()
31{
32 return params.begin();
33}
34
35const ArgvMap::param_t::const_iterator ArgvMap::end()
36{
37 return params.end();
38}
39
40string & ArgvMap::set(const string &var)
41{
42 return params[var];
43}
44
45bool ArgvMap::mustDo(const string &var)
bd11bd1d 46{
12c86877
BH
47 return ((*this)[var]!="no") && ((*this)[var]!="off");
48}
49
50vector<string>ArgvMap::list()
51{
52 vector<string> ret;
53 for(map<string,string>::const_iterator i=params.begin();i!=params.end();++i)
54 ret.push_back(i->first);
55 return ret;
56}
57
58string ArgvMap::getHelp(const string &item)
59{
60 return helpmap[item];
61}
62
63string & ArgvMap::set(const string &var, const string &help)
64{
65 helpmap[var]=help;
66 d_typeMap[var]="Parameter";
67 return set(var);
68}
69
70void ArgvMap::setCmd(const string &var, const string &help)
71{
72 helpmap[var]=help;
73 d_typeMap[var]="Command";
74 set(var)="no";
75}
76
77string & ArgvMap::setSwitch(const string &var, const string &help)
78{
79 helpmap[var]=help;
80 d_typeMap[var]="Switch";
81 return set(var);
82}
83
84
12c86877
BH
85bool ArgvMap::contains(const string &var, const string &val)
86{
102eb646
BH
87 params_t::const_iterator param = params.find(var);
88 if(param == params.end() || param->second.empty()) {
89 return false;
90 }
562588a3
BH
91 vector<string> parts;
92 vector<string>::const_iterator i;
93
102eb646 94 stringtok( parts, param->second, ", \t" );
562588a3
BH
95 for( i = parts.begin(); i != parts.end(); i++ ) {
96 if( *i == val ) {
97 return true;
98 }
99 }
100
101 return false;
12c86877
BH
102}
103
12c86877
BH
104string ArgvMap::helpstring(string prefix)
105{
106 if(prefix=="no")
107 prefix="";
108
109 string help;
110
111 for(map<string,string>::const_iterator i=helpmap.begin();
112 i!=helpmap.end();
113 i++)
114 {
115 if(!prefix.empty() && i->first.find(prefix)) // only print items with prefix
4957a608 116 continue;
12c86877
BH
117
118 help+=" --";
119 help+=i->first;
120
121 string type=d_typeMap[i->first];
122
123 if(type=="Parameter")
4957a608 124 help+="=...";
12c86877 125 else if(type=="Switch")
4957a608
BH
126 {
127 help+=" | --"+i->first+"=yes";
128 help+=" | --"+i->first+"=no";
129 }
12c86877
BH
130
131
132 help+="\n\t";
133 help+=i->second;
134 help+="\n";
135
136 }
137 return help;
138}
139
6bef3d91 140string ArgvMap::configstring(bool current)
12c86877
BH
141{
142 string help;
6bef3d91
RA
143
144 if (current)
145 help="# Autogenerated configuration file based on running instance\n";
146 else
147 help="# Autogenerated configuration file template\n";
12c86877 148
c7efa8ff
RA
149 for(map<string,string>::const_iterator i=helpmap.begin(); i!=helpmap.end(); i++) {
150 if(d_typeMap[i->first]=="Command")
151 continue;
152
153 help+="#################################\n";
154 help+="# ";
155 help+=i->first;
156 help+="\t";
157 help+=i->second;
158 help+="\n#\n";
159 if (current) {
160 help+=i->first+"="+params[i->first]+"\n\n";
161 } else {
162 help+="# "+i->first+"="+params[i->first]+"\n\n";
12c86877 163 }
c7efa8ff 164 }
12c86877
BH
165 return help;
166}
167
168
169const string & ArgvMap::operator[](const string &arg)
170{
171 if(!parmIsset(arg))
172 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
173
174
175 return params[arg];
176}
177
cc2978dc
BH
178#ifndef WIN32
179mode_t ArgvMap::asMode(const string &arg)
180{
181 mode_t mode;
182 const char *cptr_orig;
183 char *cptr_ret = NULL;
184
185 if(!parmIsset(arg))
186 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
187
188 cptr_orig = params[arg].c_str();
189 mode = static_cast<mode_t>(strtol(cptr_orig, &cptr_ret, 8));
190 if (mode == 0 && cptr_ret == cptr_orig)
191 throw ArgException("'" + arg + string("' contains invalid octal mode"));
192 return mode;
193}
194
195gid_t ArgvMap::asGid(const string &arg)
196{
197 gid_t gid;
198 const char *cptr_orig;
199 char *cptr_ret = NULL;
200
201 if(!parmIsset(arg))
202 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
203
204 cptr_orig = params[arg].c_str();
205 gid = static_cast<gid_t>(strtol(cptr_orig, &cptr_ret, 0));
206 if (gid == 0 && cptr_ret == cptr_orig) {
207 // try to resolve
208 struct group *group = getgrnam(params[arg].c_str());
209 if (group == NULL)
210 throw ArgException("'" + arg + string("' contains invalid group"));
211 gid = group->gr_gid;
212 }
213 return gid;
214}
215
216uid_t ArgvMap::asUid(const string &arg)
217{
218 uid_t uid;
219 const char *cptr_orig;
220 char *cptr_ret = NULL;
221
222 if(!parmIsset(arg))
223 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
224
225 cptr_orig = params[arg].c_str();
226 uid = static_cast<uid_t>(strtol(cptr_orig, &cptr_ret, 0));
227 if (uid == 0 && cptr_ret == cptr_orig) {
228 // try to resolve
229 struct passwd *pwent = getpwnam(params[arg].c_str());
230 if (pwent == NULL)
231 throw ArgException("'" + arg + string("' contains invalid group"));
232 uid = pwent->pw_uid;
233 }
234 return uid;
235}
236#endif
237
12c86877
BH
238int ArgvMap::asNum(const string &arg)
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
cc2978dc
BH
247 // treat empty values as zeros
248 if (params[arg].empty())
249 return 0;
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)
254 throw ArgException("'"+arg+string("' is not valid number"));
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;
3752660e 302 if(!arg.find("--") &&(pos=arg.find("+="))!=string::npos) // this is a --port+=25 case
5a177409
AT
303 {
304 var=arg.substr(2,pos-2);
305 val=arg.substr(pos+2);
306 incremental = true;
307 }
308 else if(!arg.find("--") &&(pos=arg.find("="))!=string::npos) // this is a --port=25 case
12c86877
BH
309 {
310 var=arg.substr(2,pos-2);
311 val=arg.substr(pos+1);
312 }
313 else if(!arg.find("--") && (arg.find("=")==string::npos)) // this is a --daemon case
314 {
315 var=arg.substr(2);
316 val="";
317 }
318 else if(arg[0]=='-')
319 {
320 var=arg.substr(1);
321 val="";
322 }
323 else { // command
324 d_cmds.push_back(arg);
325 }
326
327 if(var!="" && (parseOnly.empty() || var==parseOnly)) {
bd11bd1d
BH
328
329 pos=val.find_first_not_of(" \t"); // strip leading whitespace
3752660e 330 if(pos && pos!=string::npos)
bd11bd1d 331 val=val.substr(pos);
3702b428 332 if (!incremental && val.empty()) d_cleared.insert(var);
5a177409 333 if(parmIsset(var)) {
3702b428
AT
334 if (incremental) {
335 if (params[var].empty() && !d_cleared.count(var)) {
58a57699 336 throw ArgException("Incremental parameter '"+var+"' without a parent");
5a177409 337 }
3702b428
AT
338 if (params[var].empty())
339 params[var]=val;
340 else
341 params[var]+=", " + val;
342 } else {
5a177409
AT
343 params[var]=val;
344 }
3702b428 345 }
12c86877
BH
346 else
347 if(!lax)
4957a608 348 throw ArgException("Trying to set unexisting parameter '"+var+"'");
12c86877
BH
349 }
350}
351
352const vector<string>&ArgvMap::getCommands()
353{
354 return d_cmds;
355}
356
357void ArgvMap::parse(int &argc, char **argv, bool lax)
358{
116cda91 359 d_cmds.clear();
58a57699 360 d_cleared.clear();
12c86877
BH
361 for(int n=1;n<argc;n++) {
362 parseOne(argv[n],"",lax);
363 }
364}
365
366void ArgvMap::preParse(int &argc, char **argv, const string &arg)
367{
368 for(int n=1;n<argc;n++) {
369 string varval=argv[n];
370 if(!varval.find("--"+arg))
371 parseOne(argv[n]);
372 }
373}
374
f64de501 375bool ArgvMap::preParseFile(const char *fname, const string &arg, const string& theDefault)
12c86877 376{
f64de501 377 params[arg]=theDefault;
b56ac6fd 378
12c86877 379 ifstream f(fname);
eefd15f9 380 if(!f)
12c86877 381 return false;
eefd15f9 382
12c86877
BH
383 string line;
384 string pline;
bdf40704 385 string::size_type pos;
12c86877
BH
386
387 while(getline(f,pline)) {
5b2cb3be
BH
388 trim_right(pline);
389
12c86877
BH
390 if(pline[pline.size()-1]=='\\') {
391 line+=pline.substr(0,pline.length()-1);
392 continue;
393 }
eefd15f9 394 else
12c86877 395 line+=pline;
12c86877
BH
396
397 // strip everything after a #
398 if((pos=line.find("#"))!=string::npos)
399 line=line.substr(0,pos);
400
401 // strip trailing spaces
5b2cb3be 402 trim_right(line);
eefd15f9 403
12c86877
BH
404 // strip leading spaces
405 if((pos=line.find_first_not_of(" \t\r\n"))!=string::npos)
406 line=line.substr(pos);
eefd15f9 407
12c86877
BH
408 // gpgsql-basic-query=sdfsdfs dfsdfsdf sdfsdfsfd
409
eefd15f9 410 parseOne( string("--") + line, arg );
12c86877
BH
411 line="";
412 }
eefd15f9 413
12c86877
BH
414 return true;
415}
416
12c86877 417bool ArgvMap::file(const char *fname, bool lax)
4f6ef75f
AT
418{
419 return file(fname,lax,false);
420}
421
422bool ArgvMap::file(const char *fname, bool lax, bool included)
12c86877
BH
423{
424 ifstream f(fname);
425 if(!f) {
12c86877
BH
426 return false;
427 }
eefd15f9 428
8a16d778 429 if (!parmIsset("include-dir")) // inject include-dir
4f6ef75f
AT
430 set("include-dir","Directory to include configuration files from");
431
12c86877
BH
432 string line;
433 string pline;
bdf40704 434 string::size_type pos;
eefd15f9 435
12c86877 436 while(getline(f,pline)) {
5b2cb3be 437 trim_right(pline);
ccd3ed60
BH
438 if(pline.empty())
439 continue;
12c86877
BH
440
441 if(pline[pline.size()-1]=='\\') {
442 line+=pline.substr(0,pline.length()-1);
443
444 continue;
445 }
eefd15f9 446 else
12c86877
BH
447 line+=pline;
448
449 // strip everything after a #
450 if((pos=line.find("#"))!=string::npos)
451 line=line.substr(0,pos);
452
453 // strip trailing spaces
9ff13837 454 trim(line);
eefd15f9
BH
455
456 parseOne(string("--")+line,"",lax);
12c86877
BH
457 line="";
458 }
eefd15f9 459
4f6ef75f 460 // handle include here (avoid re-include)
8a16d778 461 if (!included && !params["include-dir"].empty()) {
4f6ef75f 462 // rerun parser for all files
8a16d778
AT
463 struct stat st;
464 DIR *dir;
465 struct dirent *ent;
466 char namebuf[PATH_MAX] = {0};
467
468 // stat
469 if (stat(params["include-dir"].c_str(), &st)) {
4f6ef75f
AT
470 L << Logger::Error << params["include-dir"] << " does not exist!" << std::endl;
471 throw ArgException(params["include-dir"] + " does not exist!");
472 }
8a16d778
AT
473
474 // wonder if it's accessible directory
475 if (!S_ISDIR(st.st_mode)) {
476 L << Logger::Error << params["include-dir"] << " is not a directory" << std::endl;
477 throw ArgException(params["include-dir"] + " is not a directory");
478 }
479
480 if (!(dir = opendir(params["include-dir"].c_str()))) {
481 L << Logger::Error << params["include-dir"] << " is not accessible" << std::endl;
482 throw ArgException(params["include-dir"] + " is not accessible");
483 }
2dfec518 484
2aa1b703 485 std::vector<std::string> extraConfigs;
8a16d778
AT
486 while((ent = readdir(dir)) != NULL) {
487 if (ent->d_name[0] == '.') continue; // skip any dots
488 if (boost::ends_with(ent->d_name, ".conf")) {
489 // ensure it's readable file
490 snprintf(namebuf, sizeof namebuf, "%s/%s", params["include-dir"].c_str(), ent->d_name);
491 if (stat(namebuf, &st) || !S_ISREG(st.st_mode)) {
492 L << Logger::Error << namebuf << " is not a file" << std::endl;
493 throw ArgException(std::string(namebuf) + " does not exist!");
494 }
2dfec518 495 extraConfigs.push_back(std::string(namebuf));
8a16d778 496 }
4f6ef75f 497 }
2aa1b703 498 std::sort(extraConfigs.begin(), extraConfigs.end(), CIStringComparePOSIX());
2dfec518 499 BOOST_FOREACH(const std::string& fn, extraConfigs) {
2dfec518
AT
500 if (!file(fn.c_str(), lax, true)) {
501 L << Logger::Error << namebuf << " could not be parsed" << std::endl;
502 throw ArgException(fn + " could not be parsed");
503 }
504 }
4f6ef75f
AT
505 }
506
12c86877
BH
507 return true;
508}