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