]>
Commit | Line | Data |
---|---|---|
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 |
30 | const ArgvMap::param_t::const_iterator ArgvMap::begin() |
31 | { | |
32 | return params.begin(); | |
33 | } | |
34 | ||
35 | const ArgvMap::param_t::const_iterator ArgvMap::end() | |
36 | { | |
37 | return params.end(); | |
38 | } | |
39 | ||
40 | string & ArgvMap::set(const string &var) | |
41 | { | |
42 | return params[var]; | |
43 | } | |
44 | ||
45 | bool ArgvMap::mustDo(const string &var) | |
bd11bd1d | 46 | { |
12c86877 BH |
47 | return ((*this)[var]!="no") && ((*this)[var]!="off"); |
48 | } | |
49 | ||
50 | vector<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 | ||
58 | string ArgvMap::getHelp(const string &item) | |
59 | { | |
60 | return helpmap[item]; | |
61 | } | |
62 | ||
63 | string & ArgvMap::set(const string &var, const string &help) | |
64 | { | |
65 | helpmap[var]=help; | |
66 | d_typeMap[var]="Parameter"; | |
67 | return set(var); | |
68 | } | |
69 | ||
70 | void ArgvMap::setCmd(const string &var, const string &help) | |
71 | { | |
72 | helpmap[var]=help; | |
73 | d_typeMap[var]="Command"; | |
74 | set(var)="no"; | |
75 | } | |
76 | ||
77 | string & 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 |
85 | bool 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 |
104 | string 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 | 140 | string 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 | ||
169 | const 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 |
179 | mode_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 | ||
195 | gid_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 | ||
216 | uid_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 |
238 | int 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 | ||
259 | bool ArgvMap::isEmpty(const string &arg) | |
260 | { | |
261 | if(!parmIsset(arg)) | |
262 | return true; | |
263 | return params[arg].empty(); | |
12c86877 BH |
264 | } |
265 | ||
266 | double 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 | ||
287 | ArgvMap::ArgvMap() | |
288 | { | |
289 | ||
290 | } | |
291 | ||
292 | bool ArgvMap::parmIsset(const string &var) | |
293 | { | |
294 | return (params.find(var)!=params.end()); | |
295 | } | |
296 | ||
297 | void 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 | ||
352 | const vector<string>&ArgvMap::getCommands() | |
353 | { | |
354 | return d_cmds; | |
355 | } | |
356 | ||
357 | void 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 | ||
366 | void 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 | 375 | bool 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 | 417 | bool ArgvMap::file(const char *fname, bool lax) |
4f6ef75f AT |
418 | { |
419 | return file(fname,lax,false); | |
420 | } | |
421 | ||
422 | bool 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 | } |