]>
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 | ||
140 | string 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 | ||
165 | const 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 |
175 | mode_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 | ||
191 | gid_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 | ||
212 | uid_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 |
234 | int 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 | ||
255 | bool ArgvMap::isEmpty(const string &arg) | |
256 | { | |
257 | if(!parmIsset(arg)) | |
258 | return true; | |
259 | return params[arg].empty(); | |
12c86877 BH |
260 | } |
261 | ||
262 | double 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 | ||
283 | ArgvMap::ArgvMap() | |
284 | { | |
285 | ||
286 | } | |
287 | ||
288 | bool ArgvMap::parmIsset(const string &var) | |
289 | { | |
290 | return (params.find(var)!=params.end()); | |
291 | } | |
292 | ||
293 | void 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 | ||
345 | const vector<string>&ArgvMap::getCommands() | |
346 | { | |
347 | return d_cmds; | |
348 | } | |
349 | ||
350 | void 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 | ||
359 | void 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 | 368 | bool 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 | 410 | bool ArgvMap::file(const char *fname, bool lax) |
4f6ef75f AT |
411 | { |
412 | return file(fname,lax,false); | |
413 | } | |
414 | ||
415 | bool 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 | } |