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