]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/arguments.cc
Implement sort properly with POSIX locale
[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
140string ArgvMap::configstring()
141{
142 string help;
143
144 help="# Autogenerated configuration file template\n";
145 for(map<string,string>::const_iterator i=helpmap.begin();
146 i!=helpmap.end();
147 i++)
148 {
149 if(d_typeMap[i->first]=="Command")
4957a608 150 continue;
12c86877
BH
151
152 help+="#################################\n";
153 help+="# ";
154 help+=i->first;
155 help+="\t";
156 help+=i->second;
157 help+="\n#\n";
158 help+="# "+i->first+"="+params[i->first]+"\n\n";
159
160 }
161 return help;
162}
163
164
165const string & ArgvMap::operator[](const string &arg)
166{
167 if(!parmIsset(arg))
168 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
169
170
171 return params[arg];
172}
173
cc2978dc
BH
174#ifndef WIN32
175mode_t ArgvMap::asMode(const string &arg)
176{
177 mode_t mode;
178 const char *cptr_orig;
179 char *cptr_ret = NULL;
180
181 if(!parmIsset(arg))
182 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
183
184 cptr_orig = params[arg].c_str();
185 mode = static_cast<mode_t>(strtol(cptr_orig, &cptr_ret, 8));
186 if (mode == 0 && cptr_ret == cptr_orig)
187 throw ArgException("'" + arg + string("' contains invalid octal mode"));
188 return mode;
189}
190
191gid_t ArgvMap::asGid(const string &arg)
192{
193 gid_t gid;
194 const char *cptr_orig;
195 char *cptr_ret = NULL;
196
197 if(!parmIsset(arg))
198 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
199
200 cptr_orig = params[arg].c_str();
201 gid = static_cast<gid_t>(strtol(cptr_orig, &cptr_ret, 0));
202 if (gid == 0 && cptr_ret == cptr_orig) {
203 // try to resolve
204 struct group *group = getgrnam(params[arg].c_str());
205 if (group == NULL)
206 throw ArgException("'" + arg + string("' contains invalid group"));
207 gid = group->gr_gid;
208 }
209 return gid;
210}
211
212uid_t ArgvMap::asUid(const string &arg)
213{
214 uid_t uid;
215 const char *cptr_orig;
216 char *cptr_ret = NULL;
217
218 if(!parmIsset(arg))
219 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
220
221 cptr_orig = params[arg].c_str();
222 uid = static_cast<uid_t>(strtol(cptr_orig, &cptr_ret, 0));
223 if (uid == 0 && cptr_ret == cptr_orig) {
224 // try to resolve
225 struct passwd *pwent = getpwnam(params[arg].c_str());
226 if (pwent == NULL)
227 throw ArgException("'" + arg + string("' contains invalid group"));
228 uid = pwent->pw_uid;
229 }
230 return uid;
231}
232#endif
233
12c86877
BH
234int ArgvMap::asNum(const string &arg)
235{
cc2978dc
BH
236 int retval;
237 const char *cptr_orig;
238 char *cptr_ret = NULL;
239
12c86877
BH
240 if(!parmIsset(arg))
241 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
242
cc2978dc
BH
243 // treat empty values as zeros
244 if (params[arg].empty())
245 return 0;
246
247 cptr_orig = params[arg].c_str();
248 retval = static_cast<int>(strtol(cptr_orig, &cptr_ret, 0));
249 if (!retval && cptr_ret == cptr_orig)
250 throw ArgException("'"+arg+string("' is not valid number"));
251
252 return retval;
253}
254
255bool ArgvMap::isEmpty(const string &arg)
256{
257 if(!parmIsset(arg))
258 return true;
259 return params[arg].empty();
12c86877
BH
260}
261
262double ArgvMap::asDouble(const string &arg)
263{
cc2978dc
BH
264 double retval;
265 const char *cptr_orig;
266 char *cptr_ret = NULL;
267
12c86877
BH
268 if(!parmIsset(arg))
269 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
270
cc2978dc
BH
271 if (params[arg].empty())
272 return 0.0;
273
274 cptr_orig = params[arg].c_str();
275 retval = strtod(cptr_orig, &cptr_ret);
276
277 if (retval == 0 && cptr_ret == cptr_orig)
278 throw ArgException("'"+arg+string("' is not valid double"));
279
280 return retval;
12c86877
BH
281}
282
283ArgvMap::ArgvMap()
284{
285
286}
287
288bool ArgvMap::parmIsset(const string &var)
289{
290 return (params.find(var)!=params.end());
291}
292
293void ArgvMap::parseOne(const string &arg, const string &parseOnly, bool lax)
294{
295 string var, val;
bdf40704 296 string::size_type pos;
5a177409 297 bool incremental = false;
3752660e 298 if(!arg.find("--") &&(pos=arg.find("+="))!=string::npos) // this is a --port+=25 case
5a177409
AT
299 {
300 var=arg.substr(2,pos-2);
301 val=arg.substr(pos+2);
302 incremental = true;
303 }
304 else if(!arg.find("--") &&(pos=arg.find("="))!=string::npos) // this is a --port=25 case
12c86877
BH
305 {
306 var=arg.substr(2,pos-2);
307 val=arg.substr(pos+1);
308 }
309 else if(!arg.find("--") && (arg.find("=")==string::npos)) // this is a --daemon case
310 {
311 var=arg.substr(2);
312 val="";
58a57699 313 d_cleared.insert(var);
12c86877
BH
314 }
315 else if(arg[0]=='-')
316 {
317 var=arg.substr(1);
318 val="";
319 }
320 else { // command
321 d_cmds.push_back(arg);
322 }
323
324 if(var!="" && (parseOnly.empty() || var==parseOnly)) {
bd11bd1d
BH
325
326 pos=val.find_first_not_of(" \t"); // strip leading whitespace
3752660e 327 if(pos && pos!=string::npos)
bd11bd1d
BH
328 val=val.substr(pos);
329
5a177409 330 if(parmIsset(var)) {
58a57699
KM
331 if (incremental && !d_cleared.count(var)) {
332 if (params[var].empty()) {
333 throw ArgException("Incremental parameter '"+var+"' without a parent");
5a177409 334 }
1589abff
AT
335 params[var]+=val;
336 } else
5a177409
AT
337 params[var]=val;
338 }
12c86877
BH
339 else
340 if(!lax)
4957a608 341 throw ArgException("Trying to set unexisting parameter '"+var+"'");
12c86877
BH
342 }
343}
344
345const vector<string>&ArgvMap::getCommands()
346{
347 return d_cmds;
348}
349
350void ArgvMap::parse(int &argc, char **argv, bool lax)
351{
116cda91 352 d_cmds.clear();
58a57699 353 d_cleared.clear();
12c86877
BH
354 for(int n=1;n<argc;n++) {
355 parseOne(argv[n],"",lax);
356 }
357}
358
359void ArgvMap::preParse(int &argc, char **argv, const string &arg)
360{
361 for(int n=1;n<argc;n++) {
362 string varval=argv[n];
363 if(!varval.find("--"+arg))
364 parseOne(argv[n]);
365 }
366}
367
f64de501 368bool ArgvMap::preParseFile(const char *fname, const string &arg, const string& theDefault)
12c86877 369{
f64de501 370 params[arg]=theDefault;
b56ac6fd 371
12c86877 372 ifstream f(fname);
eefd15f9 373 if(!f)
12c86877 374 return false;
eefd15f9 375
12c86877
BH
376 string line;
377 string pline;
bdf40704 378 string::size_type pos;
12c86877
BH
379
380 while(getline(f,pline)) {
5b2cb3be
BH
381 trim_right(pline);
382
12c86877
BH
383 if(pline[pline.size()-1]=='\\') {
384 line+=pline.substr(0,pline.length()-1);
385 continue;
386 }
eefd15f9 387 else
12c86877 388 line+=pline;
12c86877
BH
389
390 // strip everything after a #
391 if((pos=line.find("#"))!=string::npos)
392 line=line.substr(0,pos);
393
394 // strip trailing spaces
5b2cb3be 395 trim_right(line);
eefd15f9 396
12c86877
BH
397 // strip leading spaces
398 if((pos=line.find_first_not_of(" \t\r\n"))!=string::npos)
399 line=line.substr(pos);
eefd15f9 400
12c86877
BH
401 // gpgsql-basic-query=sdfsdfs dfsdfsdf sdfsdfsfd
402
eefd15f9 403 parseOne( string("--") + line, arg );
12c86877
BH
404 line="";
405 }
eefd15f9 406
12c86877
BH
407 return true;
408}
409
12c86877 410bool ArgvMap::file(const char *fname, bool lax)
4f6ef75f
AT
411{
412 return file(fname,lax,false);
413}
414
415bool ArgvMap::file(const char *fname, bool lax, bool included)
12c86877
BH
416{
417 ifstream f(fname);
418 if(!f) {
12c86877
BH
419 return false;
420 }
eefd15f9 421
8a16d778 422 if (!parmIsset("include-dir")) // inject include-dir
4f6ef75f
AT
423 set("include-dir","Directory to include configuration files from");
424
12c86877
BH
425 string line;
426 string pline;
bdf40704 427 string::size_type pos;
eefd15f9 428
12c86877 429 while(getline(f,pline)) {
5b2cb3be 430 trim_right(pline);
ccd3ed60
BH
431 if(pline.empty())
432 continue;
12c86877
BH
433
434 if(pline[pline.size()-1]=='\\') {
435 line+=pline.substr(0,pline.length()-1);
436
437 continue;
438 }
eefd15f9 439 else
12c86877
BH
440 line+=pline;
441
442 // strip everything after a #
443 if((pos=line.find("#"))!=string::npos)
444 line=line.substr(0,pos);
445
446 // strip trailing spaces
9ff13837 447 trim(line);
eefd15f9
BH
448
449 parseOne(string("--")+line,"",lax);
12c86877
BH
450 line="";
451 }
eefd15f9 452
4f6ef75f 453 // handle include here (avoid re-include)
8a16d778 454 if (!included && !params["include-dir"].empty()) {
4f6ef75f 455 // rerun parser for all files
8a16d778
AT
456 struct stat st;
457 DIR *dir;
458 struct dirent *ent;
459 char namebuf[PATH_MAX] = {0};
460
461 // stat
462 if (stat(params["include-dir"].c_str(), &st)) {
4f6ef75f
AT
463 L << Logger::Error << params["include-dir"] << " does not exist!" << std::endl;
464 throw ArgException(params["include-dir"] + " does not exist!");
465 }
8a16d778
AT
466
467 // wonder if it's accessible directory
468 if (!S_ISDIR(st.st_mode)) {
469 L << Logger::Error << params["include-dir"] << " is not a directory" << std::endl;
470 throw ArgException(params["include-dir"] + " is not a directory");
471 }
472
473 if (!(dir = opendir(params["include-dir"].c_str()))) {
474 L << Logger::Error << params["include-dir"] << " is not accessible" << std::endl;
475 throw ArgException(params["include-dir"] + " is not accessible");
476 }
2dfec518 477
2aa1b703 478 std::vector<std::string> extraConfigs;
8a16d778
AT
479 while((ent = readdir(dir)) != NULL) {
480 if (ent->d_name[0] == '.') continue; // skip any dots
481 if (boost::ends_with(ent->d_name, ".conf")) {
482 // ensure it's readable file
483 snprintf(namebuf, sizeof namebuf, "%s/%s", params["include-dir"].c_str(), ent->d_name);
484 if (stat(namebuf, &st) || !S_ISREG(st.st_mode)) {
485 L << Logger::Error << namebuf << " is not a file" << std::endl;
486 throw ArgException(std::string(namebuf) + " does not exist!");
487 }
2dfec518 488 extraConfigs.push_back(std::string(namebuf));
8a16d778 489 }
4f6ef75f 490 }
2aa1b703 491 std::sort(extraConfigs.begin(), extraConfigs.end(), CIStringComparePOSIX());
2dfec518
AT
492 BOOST_FOREACH(const std::string& fn, extraConfigs) {
493 std::cout << "parsing " << fn << std::endl;
494 if (!file(fn.c_str(), lax, true)) {
495 L << Logger::Error << namebuf << " could not be parsed" << std::endl;
496 throw ArgException(fn + " could not be parsed");
497 }
498 }
4f6ef75f
AT
499 }
500
12c86877
BH
501 return true;
502}