]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/arguments.cc
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
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.
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
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "arguments.hh"
26 #include <boost/algorithm/string.hpp>
27 #include <boost/algorithm/string/compare.hpp>
28 #include <boost/algorithm/string/predicate.hpp>
30 #include "namespaces.hh"
32 #include <sys/types.h>
38 const ArgvMap::param_t::const_iterator
ArgvMap::begin()
40 return params
.begin();
43 const ArgvMap::param_t::const_iterator
ArgvMap::end()
48 string
& ArgvMap::set(const string
&var
)
53 bool ArgvMap::mustDo(const string
&var
)
55 return ((*this)[var
]!="no") && ((*this)[var
]!="off");
58 vector
<string
>ArgvMap::list()
61 for(map
<string
,string
>::const_iterator i
=params
.begin();i
!=params
.end();++i
)
62 ret
.push_back(i
->first
);
66 string
ArgvMap::getHelp(const string
&item
)
71 string
& ArgvMap::set(const string
&var
, const string
&help
)
74 d_typeMap
[var
]="Parameter";
78 void ArgvMap::setCmd(const string
&var
, const string
&help
)
81 d_typeMap
[var
]="Command";
85 string
& ArgvMap::setSwitch(const string
&var
, const string
&help
)
88 d_typeMap
[var
]="Switch";
93 bool ArgvMap::contains(const string
&var
, const string
&val
)
95 params_t::const_iterator param
= params
.find(var
);
96 if(param
== params
.end() || param
->second
.empty()) {
101 stringtok( parts
, param
->second
, ", \t" );
102 for (const auto& part
: parts
) {
111 string
ArgvMap::helpstring(string prefix
)
118 for (const auto& i
: helpmap
) {
119 if(!prefix
.empty() && i
.first
.find(prefix
) != 0) // only print items with prefix
125 string type
=d_typeMap
[i
.first
];
127 if(type
=="Parameter")
129 else if(type
=="Switch")
131 help
+=" | --"+i
.first
+"=yes";
132 help
+=" | --"+i
.first
+"=no";
144 string
ArgvMap::configstring(bool current
)
149 help
="# Autogenerated configuration file based on running instance\n";
151 help
="# Autogenerated configuration file template\n";
153 for(const auto& i
: helpmap
) {
154 if(d_typeMap
[i
.first
]=="Command")
157 help
+="#################################\n";
164 help
+=i
.first
+"="+params
[i
.first
]+"\n\n";
166 help
+="# "+i
.first
+"="+params
[i
.first
]+"\n\n";
172 const string
& ArgvMap::operator[](const string
&arg
)
175 throw ArgException(string("Undefined but needed argument: '")+arg
+"'");
180 mode_t
ArgvMap::asMode(const string
&arg
)
183 const char *cptr_orig
;
184 char *cptr_ret
= NULL
;
187 throw ArgException(string("Undefined but needed argument: '")+arg
+"'");
189 cptr_orig
= params
[arg
].c_str();
190 mode
= static_cast<mode_t
>(strtol(cptr_orig
, &cptr_ret
, 8));
191 if (mode
== 0 && cptr_ret
== cptr_orig
)
192 throw ArgException("'" + arg
+ string("' contains invalid octal mode"));
196 gid_t
ArgvMap::asGid(const string
&arg
)
199 const char *cptr_orig
;
200 char *cptr_ret
= NULL
;
203 throw ArgException(string("Undefined but needed argument: '")+arg
+"'");
205 cptr_orig
= params
[arg
].c_str();
206 gid
= static_cast<gid_t
>(strtol(cptr_orig
, &cptr_ret
, 0));
207 if (gid
== 0 && cptr_ret
== cptr_orig
) {
209 struct group
*group
= getgrnam(params
[arg
].c_str());
211 throw ArgException("'" + arg
+ string("' contains invalid group"));
217 uid_t
ArgvMap::asUid(const string
&arg
)
220 const char *cptr_orig
;
221 char *cptr_ret
= NULL
;
224 throw ArgException(string("Undefined but needed argument: '")+arg
+"'");
226 cptr_orig
= params
[arg
].c_str();
227 uid
= static_cast<uid_t
>(strtol(cptr_orig
, &cptr_ret
, 0));
228 if (uid
== 0 && cptr_ret
== cptr_orig
) {
230 struct passwd
*pwent
= getpwnam(params
[arg
].c_str());
232 throw ArgException("'" + arg
+ string("' contains invalid group"));
238 int ArgvMap::asNum(const string
&arg
, int def
)
241 const char *cptr_orig
;
242 char *cptr_ret
= NULL
;
245 throw ArgException(string("Undefined but needed argument: '")+arg
+"'");
247 // use default for empty values
248 if (params
[arg
].empty())
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
+"' value '"+string(cptr_orig
) + string( "' is not a valid number"));
259 bool ArgvMap::isEmpty(const string
&arg
)
263 return params
[arg
].empty();
266 double ArgvMap::asDouble(const string
&arg
)
269 const char *cptr_orig
;
270 char *cptr_ret
= NULL
;
273 throw ArgException(string("Undefined but needed argument: '")+arg
+"'");
275 if (params
[arg
].empty())
278 cptr_orig
= params
[arg
].c_str();
279 retval
= strtod(cptr_orig
, &cptr_ret
);
281 if (retval
== 0 && cptr_ret
== cptr_orig
)
282 throw ArgException("'"+arg
+string("' is not valid double"));
292 bool ArgvMap::parmIsset(const string
&var
)
294 return (params
.find(var
)!=params
.end());
297 void ArgvMap::parseOne(const string
&arg
, const string
&parseOnly
, bool lax
)
300 string::size_type pos
;
301 bool incremental
= false;
303 if(arg
.find("--") == 0 && (pos
=arg
.find("+="))!=string::npos
) // this is a --port+=25 case
305 var
=arg
.substr(2,pos
-2);
306 val
=arg
.substr(pos
+2);
309 else if(arg
.find("--") == 0 && (pos
=arg
.find("="))!=string::npos
) // this is a --port=25 case
311 var
=arg
.substr(2,pos
-2);
312 val
=arg
.substr(pos
+1);
314 else if(arg
.find("--") == 0 && (arg
.find("=")==string::npos
)) // this is a --daemon case
325 d_cmds
.push_back(arg
);
329 if(var
!="" && (parseOnly
.empty() || var
==parseOnly
)) {
330 pos
=val
.find_first_not_of(" \t"); // strip leading whitespace
331 if(pos
&& pos
!=string::npos
)
337 if(params
[var
].empty())
339 if(!d_cleared
.count(var
))
340 throw ArgException("Incremental parameter '"+var
+"' without a parent");
344 params
[var
]+=", " + val
;
349 d_cleared
.insert(var
);
353 throw ArgException("Trying to set unknown parameter '"+var
+"'");
357 const vector
<string
>&ArgvMap::getCommands()
362 void ArgvMap::parse(int &argc
, char **argv
, bool lax
)
366 for(int n
=1;n
<argc
;n
++) {
367 parseOne(argv
[n
],"",lax
);
371 void ArgvMap::preParse(int &argc
, char **argv
, const string
&arg
)
373 for(int n
=1;n
<argc
;n
++) {
374 string varval
=argv
[n
];
375 if(varval
.find("--"+arg
) == 0)
380 bool ArgvMap::parseFile(const char *fname
, const string
& arg
, bool lax
) {
383 string::size_type pos
;
389 while(getline(f
,pline
)) {
392 if(!pline
.empty() && pline
[pline
.size()-1]=='\\') {
393 line
+=pline
.substr(0,pline
.length()-1);
399 // strip everything after a #
400 if((pos
=line
.find("#"))!=string::npos
) {
401 // make sure it's either first char or has whitespace before
403 if (pos
== 0 || std::isspace(line
[pos
-1]))
404 line
=line
.substr(0,pos
);
407 // strip trailing spaces
410 // strip leading spaces
411 if((pos
=line
.find_first_not_of(" \t\r\n"))!=string::npos
)
412 line
=line
.substr(pos
);
414 // gpgsql-basic-query=sdfsdfs dfsdfsdf sdfsdfsfd
416 parseOne( string("--") + line
, arg
, lax
);
424 bool ArgvMap::preParseFile(const char *fname
, const string
&arg
, const string
& theDefault
)
426 params
[arg
]=theDefault
;
428 return parseFile(fname
, arg
, false);
431 bool ArgvMap::file(const char *fname
, bool lax
)
433 return file(fname
,lax
,false);
436 bool ArgvMap::file(const char *fname
, bool lax
, bool included
)
438 if (!parmIsset("include-dir")) // inject include-dir
439 set("include-dir","Directory to include configuration files from");
441 if(!parseFile(fname
, "", lax
)) {
442 g_log
<< Logger::Warning
<< "Unable to open " << fname
<< std::endl
;
446 // handle include here (avoid re-include)
447 if (!included
&& !params
["include-dir"].empty()) {
448 std::vector
<std::string
> extraConfigs
;
449 gatherIncludes(extraConfigs
);
450 for(const std::string
& fn
: extraConfigs
) {
451 if (!file(fn
.c_str(), lax
, true)) {
452 g_log
<< Logger::Error
<< fn
<< " could not be parsed" << std::endl
;
453 throw ArgException(fn
+ " could not be parsed");
461 void ArgvMap::gatherIncludes(std::vector
<std::string
> &extraConfigs
) {
462 extraConfigs
.clear();
463 if (params
["include-dir"].empty()) return; // nothing to do
469 if (stat(params
["include-dir"].c_str(), &st
)) {
470 g_log
<< Logger::Error
<< params
["include-dir"] << " does not exist!" << std::endl
;
471 throw ArgException(params
["include-dir"] + " does not exist!");
474 // wonder if it's accessible directory
475 if (!S_ISDIR(st
.st_mode
)) {
476 g_log
<< Logger::Error
<< params
["include-dir"] << " is not a directory" << std::endl
;
477 throw ArgException(params
["include-dir"] + " is not a directory");
480 if (!(dir
= opendir(params
["include-dir"].c_str()))) {
481 g_log
<< Logger::Error
<< params
["include-dir"] << " is not accessible" << std::endl
;
482 throw ArgException(params
["include-dir"] + " is not accessible");
485 while((ent
= readdir(dir
)) != NULL
) {
486 if (ent
->d_name
[0] == '.') continue; // skip any dots
487 if (boost::ends_with(ent
->d_name
, ".conf")) {
489 std::ostringstream namebuf
;
490 namebuf
<< params
["include-dir"].c_str() << "/" << ent
->d_name
; // FIXME: Use some path separator
491 // ensure it's readable file
492 if (stat(namebuf
.str().c_str(), &st
) || !S_ISREG(st
.st_mode
)) {
493 g_log
<< Logger::Error
<< namebuf
.str() << " is not a file" << std::endl
;
495 throw ArgException(namebuf
.str() + " does not exist!");
497 extraConfigs
.push_back(namebuf
.str());
500 std::sort(extraConfigs
.begin(), extraConfigs
.end(), CIStringComparePOSIX());