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