]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | #include "auth/Auth.h" | |
7c673cae | 16 | #include "common/ceph_argparse.h" |
7c673cae | 17 | #include "common/config.h" |
7c673cae | 18 | #include "common/version.h" |
7c673cae | 19 | #include "include/str_list.h" |
7c673cae FG |
20 | |
21 | /* | |
22 | * Ceph argument parsing library | |
23 | * | |
24 | * We probably should eventually replace this with something standard like popt. | |
25 | * Until we do that, though, this file is the place for argv parsing | |
26 | * stuff to live. | |
27 | */ | |
28 | ||
29 | #undef dout | |
30 | #undef pdout | |
31 | #undef derr | |
32 | #undef generic_dout | |
33 | #undef dendl | |
34 | ||
35 | struct strict_str_convert { | |
36 | const char *str; | |
37 | std::string *err; | |
38 | strict_str_convert(const char *str, std::string *err) | |
39 | : str(str), err(err) {} | |
40 | ||
41 | inline operator float() const | |
42 | { | |
43 | return strict_strtof(str, err); | |
44 | } | |
45 | inline operator int() const | |
46 | { | |
47 | return strict_strtol(str, 10, err); | |
48 | } | |
49 | inline operator long long() const | |
50 | { | |
51 | return strict_strtoll(str, 10, err); | |
52 | } | |
53 | }; | |
54 | ||
55 | void string_to_vec(std::vector<std::string>& args, std::string argstr) | |
56 | { | |
57 | istringstream iss(argstr); | |
58 | while(iss) { | |
59 | string sub; | |
60 | iss >> sub; | |
61 | if (sub == "") break; | |
62 | args.push_back(sub); | |
63 | } | |
64 | } | |
65 | ||
66 | bool split_dashdash(const std::vector<const char*>& args, | |
67 | std::vector<const char*>& options, | |
68 | std::vector<const char*>& arguments) { | |
69 | bool dashdash = false; | |
70 | for (std::vector<const char*>::const_iterator i = args.begin(); | |
71 | i != args.end(); | |
72 | ++i) { | |
73 | if (dashdash) { | |
74 | arguments.push_back(*i); | |
75 | } else { | |
76 | if (strcmp(*i, "--") == 0) | |
77 | dashdash = true; | |
78 | else | |
79 | options.push_back(*i); | |
80 | } | |
81 | } | |
82 | return dashdash; | |
83 | } | |
84 | ||
85 | void env_to_vec(std::vector<const char*>& args, const char *name) | |
86 | { | |
87 | if (!name) | |
88 | name = "CEPH_ARGS"; | |
89 | char *p = getenv(name); | |
90 | if (!p) | |
91 | return; | |
92 | ||
93 | bool dashdash = false; | |
94 | std::vector<const char*> options; | |
95 | std::vector<const char*> arguments; | |
96 | if (split_dashdash(args, options, arguments)) | |
97 | dashdash = true; | |
98 | ||
99 | std::vector<const char*> env_options; | |
100 | std::vector<const char*> env_arguments; | |
101 | static vector<string> str_vec; | |
102 | std::vector<const char*> env; | |
103 | str_vec.clear(); | |
104 | get_str_vec(p, " ", str_vec); | |
105 | for (vector<string>::iterator i = str_vec.begin(); | |
106 | i != str_vec.end(); | |
107 | ++i) | |
108 | env.push_back(i->c_str()); | |
109 | if (split_dashdash(env, env_options, env_arguments)) | |
110 | dashdash = true; | |
111 | ||
112 | args.clear(); | |
113 | args.insert(args.end(), options.begin(), options.end()); | |
114 | args.insert(args.end(), env_options.begin(), env_options.end()); | |
115 | if (dashdash) | |
116 | args.push_back("--"); | |
117 | args.insert(args.end(), arguments.begin(), arguments.end()); | |
118 | args.insert(args.end(), env_arguments.begin(), env_arguments.end()); | |
119 | } | |
120 | ||
121 | void argv_to_vec(int argc, const char **argv, | |
122 | std::vector<const char*>& args) | |
123 | { | |
124 | args.insert(args.end(), argv + 1, argv + argc); | |
125 | } | |
126 | ||
127 | void vec_to_argv(const char *argv0, std::vector<const char*>& args, | |
128 | int *argc, const char ***argv) | |
129 | { | |
130 | *argv = (const char**)malloc(sizeof(char*) * (args.size() + 1)); | |
131 | if (!*argv) | |
132 | throw bad_alloc(); | |
133 | *argc = 1; | |
134 | (*argv)[0] = argv0; | |
135 | ||
136 | for (unsigned i=0; i<args.size(); i++) | |
137 | (*argv)[(*argc)++] = args[i]; | |
138 | } | |
139 | ||
140 | void ceph_arg_value_type(const char * nextargstr, bool *bool_option, bool *bool_numeric) | |
141 | { | |
142 | bool is_numeric = true; | |
143 | bool is_float = false; | |
144 | bool is_option; | |
145 | ||
146 | if (nextargstr == NULL) { | |
147 | return; | |
148 | } | |
149 | ||
150 | if (strlen(nextargstr) < 2) { | |
151 | is_option = false; | |
152 | } else { | |
153 | is_option = (nextargstr[0] == '-') && (nextargstr[1] == '-'); | |
154 | } | |
155 | ||
156 | for (unsigned int i = 0; i < strlen(nextargstr); i++) { | |
157 | if (!(nextargstr[i] >= '0' && nextargstr[i] <= '9')) { | |
158 | // May be negative numeral value | |
159 | if ((i == 0) && (strlen(nextargstr) >= 2)) { | |
160 | if (nextargstr[0] == '-') | |
161 | continue; | |
162 | } | |
163 | if ( (nextargstr[i] == '.') && (is_float == false) ) { | |
164 | is_float = true; | |
165 | continue; | |
166 | } | |
167 | ||
168 | is_numeric = false; | |
169 | break; | |
170 | } | |
171 | } | |
172 | ||
173 | // -<option> | |
174 | if (nextargstr[0] == '-' && is_numeric == false) { | |
175 | is_option = true; | |
176 | } | |
177 | ||
178 | *bool_option = is_option; | |
179 | *bool_numeric = is_numeric; | |
180 | ||
181 | return; | |
182 | } | |
183 | ||
184 | bool parse_ip_port_vec(const char *s, vector<entity_addr_t>& vec) | |
185 | { | |
186 | const char *p = s; | |
187 | const char *end = p + strlen(p); | |
188 | while (p < end) { | |
189 | entity_addr_t a; | |
190 | //cout << " parse at '" << p << "'" << std::endl; | |
191 | if (!a.parse(p, &p)) { | |
192 | //dout(0) << " failed to parse address '" << p << "'" << dendl; | |
193 | return false; | |
194 | } | |
195 | //cout << " got " << a << ", rest is '" << p << "'" << std::endl; | |
196 | vec.push_back(a); | |
197 | while (*p == ',' || *p == ' ' || *p == ';') | |
198 | p++; | |
199 | } | |
200 | return true; | |
201 | } | |
202 | ||
203 | // The defaults for CephInitParameters | |
204 | CephInitParameters::CephInitParameters(uint32_t module_type_) | |
205 | : module_type(module_type_) | |
206 | { | |
207 | name.set(module_type, "admin"); | |
208 | } | |
209 | ||
210 | static void dashes_to_underscores(const char *input, char *output) | |
211 | { | |
212 | char c = 0; | |
213 | char *o = output; | |
214 | const char *i = input; | |
215 | // first two characters are copied as-is | |
216 | *o = *i++; | |
217 | if (*o++ == '\0') | |
218 | return; | |
219 | *o = *i++; | |
220 | if (*o++ == '\0') | |
221 | return; | |
222 | for (; ((c = *i)); ++i) { | |
223 | if (c == '=') { | |
224 | strcpy(o, i); | |
225 | return; | |
226 | } | |
227 | if (c == '-') | |
228 | *o++ = '_'; | |
229 | else | |
230 | *o++ = c; | |
231 | } | |
232 | *o++ = '\0'; | |
233 | } | |
234 | ||
235 | /** Once we see a standalone double dash, '--', we should remove it and stop | |
236 | * looking for any other options and flags. */ | |
237 | bool ceph_argparse_double_dash(std::vector<const char*> &args, | |
238 | std::vector<const char*>::iterator &i) | |
239 | { | |
240 | if (strcmp(*i, "--") == 0) { | |
241 | i = args.erase(i); | |
242 | return true; | |
243 | } | |
244 | return false; | |
245 | } | |
246 | ||
247 | bool ceph_argparse_flag(std::vector<const char*> &args, | |
248 | std::vector<const char*>::iterator &i, ...) | |
249 | { | |
250 | const char *first = *i; | |
251 | char tmp[strlen(first)+1]; | |
252 | dashes_to_underscores(first, tmp); | |
253 | first = tmp; | |
254 | va_list ap; | |
255 | ||
256 | va_start(ap, i); | |
257 | while (1) { | |
258 | const char *a = va_arg(ap, char*); | |
259 | if (a == NULL) { | |
260 | va_end(ap); | |
261 | return false; | |
262 | } | |
263 | char a2[strlen(a)+1]; | |
264 | dashes_to_underscores(a, a2); | |
265 | if (strcmp(a2, first) == 0) { | |
266 | i = args.erase(i); | |
267 | va_end(ap); | |
268 | return true; | |
269 | } | |
270 | } | |
271 | } | |
272 | ||
273 | static bool va_ceph_argparse_binary_flag(std::vector<const char*> &args, | |
274 | std::vector<const char*>::iterator &i, int *ret, | |
275 | std::ostream *oss, va_list ap) | |
276 | { | |
277 | const char *first = *i; | |
278 | char tmp[strlen(first)+1]; | |
279 | dashes_to_underscores(first, tmp); | |
280 | first = tmp; | |
281 | ||
282 | // does this argument match any of the possibilities? | |
283 | while (1) { | |
284 | const char *a = va_arg(ap, char*); | |
285 | if (a == NULL) | |
286 | return false; | |
287 | int strlen_a = strlen(a); | |
288 | char a2[strlen_a+1]; | |
289 | dashes_to_underscores(a, a2); | |
290 | if (strncmp(a2, first, strlen(a2)) == 0) { | |
291 | if (first[strlen_a] == '=') { | |
292 | i = args.erase(i); | |
293 | const char *val = first + strlen_a + 1; | |
294 | if ((strcmp(val, "true") == 0) || (strcmp(val, "1") == 0)) { | |
295 | *ret = 1; | |
296 | return true; | |
297 | } | |
298 | else if ((strcmp(val, "false") == 0) || (strcmp(val, "0") == 0)) { | |
299 | *ret = 0; | |
300 | return true; | |
301 | } | |
302 | if (oss) { | |
303 | (*oss) << "Parse error parsing binary flag " << a | |
304 | << ". Expected true or false, but got '" << val << "'\n"; | |
305 | } | |
306 | *ret = -EINVAL; | |
307 | return true; | |
308 | } | |
309 | else if (first[strlen_a] == '\0') { | |
310 | i = args.erase(i); | |
311 | *ret = 1; | |
312 | return true; | |
313 | } | |
314 | } | |
315 | } | |
316 | } | |
317 | ||
318 | bool ceph_argparse_binary_flag(std::vector<const char*> &args, | |
319 | std::vector<const char*>::iterator &i, int *ret, | |
320 | std::ostream *oss, ...) | |
321 | { | |
322 | bool r; | |
323 | va_list ap; | |
324 | va_start(ap, oss); | |
325 | r = va_ceph_argparse_binary_flag(args, i, ret, oss, ap); | |
326 | va_end(ap); | |
327 | return r; | |
328 | } | |
329 | ||
330 | static int va_ceph_argparse_witharg(std::vector<const char*> &args, | |
331 | std::vector<const char*>::iterator &i, std::string *ret, | |
332 | std::ostream &oss, va_list ap) | |
333 | { | |
334 | const char *first = *i; | |
335 | char tmp[strlen(first)+1]; | |
336 | dashes_to_underscores(first, tmp); | |
337 | first = tmp; | |
338 | ||
339 | // does this argument match any of the possibilities? | |
340 | while (1) { | |
341 | const char *a = va_arg(ap, char*); | |
342 | if (a == NULL) | |
343 | return 0; | |
344 | int strlen_a = strlen(a); | |
345 | char a2[strlen_a+1]; | |
346 | dashes_to_underscores(a, a2); | |
347 | if (strncmp(a2, first, strlen(a2)) == 0) { | |
348 | if (first[strlen_a] == '=') { | |
349 | *ret = first + strlen_a + 1; | |
350 | i = args.erase(i); | |
351 | return 1; | |
352 | } | |
353 | else if (first[strlen_a] == '\0') { | |
354 | // find second part (or not) | |
355 | if (i+1 == args.end()) { | |
356 | oss << "Option " << *i << " requires an argument." << std::endl; | |
357 | i = args.erase(i); | |
358 | return -EINVAL; | |
359 | } | |
360 | i = args.erase(i); | |
361 | *ret = *i; | |
362 | i = args.erase(i); | |
363 | return 1; | |
364 | } | |
365 | } | |
366 | } | |
367 | } | |
368 | ||
369 | template<class T> | |
370 | bool ceph_argparse_witharg(std::vector<const char*> &args, | |
371 | std::vector<const char*>::iterator &i, T *ret, | |
372 | std::ostream &oss, ...) | |
373 | { | |
374 | int r; | |
375 | va_list ap; | |
376 | bool is_option = false; | |
377 | bool is_numeric = true; | |
378 | std::string str; | |
379 | va_start(ap, oss); | |
380 | r = va_ceph_argparse_witharg(args, i, &str, oss, ap); | |
381 | va_end(ap); | |
382 | if (r == 0) { | |
383 | return false; | |
384 | } else if (r < 0) { | |
385 | return true; | |
386 | } | |
387 | ||
388 | ceph_arg_value_type(str.c_str(), &is_option, &is_numeric); | |
389 | if ((is_option == true) || (is_numeric == false)) { | |
390 | *ret = EXIT_FAILURE; | |
391 | if (is_option == true) { | |
392 | oss << "Missing option value"; | |
393 | } else { | |
394 | oss << "The option value '" << str << "' is invalid"; | |
395 | } | |
396 | return true; | |
397 | } | |
398 | ||
399 | std::string err; | |
400 | T myret = strict_str_convert(str.c_str(), &err); | |
401 | *ret = myret; | |
402 | if (!err.empty()) { | |
403 | oss << err; | |
404 | } | |
405 | return true; | |
406 | } | |
407 | ||
408 | template bool ceph_argparse_witharg<int>(std::vector<const char*> &args, | |
409 | std::vector<const char*>::iterator &i, int *ret, | |
410 | std::ostream &oss, ...); | |
411 | ||
412 | template bool ceph_argparse_witharg<long long>(std::vector<const char*> &args, | |
413 | std::vector<const char*>::iterator &i, long long *ret, | |
414 | std::ostream &oss, ...); | |
415 | ||
416 | template bool ceph_argparse_witharg<float>(std::vector<const char*> &args, | |
417 | std::vector<const char*>::iterator &i, float *ret, | |
418 | std::ostream &oss, ...); | |
419 | ||
420 | bool ceph_argparse_witharg(std::vector<const char*> &args, | |
421 | std::vector<const char*>::iterator &i, std::string *ret, | |
422 | std::ostream &oss, ...) | |
423 | { | |
424 | int r; | |
425 | va_list ap; | |
426 | va_start(ap, oss); | |
427 | r = va_ceph_argparse_witharg(args, i, ret, oss, ap); | |
428 | va_end(ap); | |
429 | return r != 0; | |
430 | } | |
431 | ||
432 | bool ceph_argparse_witharg(std::vector<const char*> &args, | |
433 | std::vector<const char*>::iterator &i, std::string *ret, ...) | |
434 | { | |
435 | int r; | |
436 | va_list ap; | |
437 | va_start(ap, ret); | |
438 | r = va_ceph_argparse_witharg(args, i, ret, cerr, ap); | |
439 | va_end(ap); | |
440 | if (r < 0) | |
441 | _exit(1); | |
442 | return r != 0; | |
443 | } | |
444 | ||
445 | CephInitParameters ceph_argparse_early_args | |
446 | (std::vector<const char*>& args, uint32_t module_type, | |
447 | std::string *cluster, std::string *conf_file_list) | |
448 | { | |
449 | CephInitParameters iparams(module_type); | |
450 | std::string val; | |
451 | ||
452 | vector<const char *> orig_args = args; | |
453 | ||
454 | for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) { | |
455 | if (strcmp(*i, "--") == 0) { | |
456 | /* Normally we would use ceph_argparse_double_dash. However, in this | |
457 | * function we *don't* want to remove the double dash, because later | |
458 | * argument parses will still need to see it. */ | |
459 | break; | |
460 | } | |
461 | else if (ceph_argparse_flag(args, i, "--version", "-v", (char*)NULL)) { | |
462 | cout << pretty_version_to_str() << std::endl; | |
463 | _exit(0); | |
464 | } | |
465 | else if (ceph_argparse_witharg(args, i, &val, "--conf", "-c", (char*)NULL)) { | |
466 | *conf_file_list = val; | |
467 | } | |
468 | else if (ceph_argparse_witharg(args, i, &val, "--cluster", (char*)NULL)) { | |
469 | *cluster = val; | |
470 | } | |
471 | else if ((module_type != CEPH_ENTITY_TYPE_CLIENT) && | |
472 | (ceph_argparse_witharg(args, i, &val, "-i", (char*)NULL))) { | |
473 | iparams.name.set_id(val); | |
474 | } | |
475 | else if (ceph_argparse_witharg(args, i, &val, "--id", "--user", (char*)NULL)) { | |
476 | iparams.name.set_id(val); | |
477 | } | |
478 | else if (ceph_argparse_witharg(args, i, &val, "--name", "-n", (char*)NULL)) { | |
479 | if (!iparams.name.from_str(val)) { | |
480 | cerr << "error parsing '" << val << "': expected string of the form TYPE.ID, " | |
481 | << "valid types are: " << EntityName::get_valid_types_as_str() | |
482 | << std::endl; | |
483 | _exit(1); | |
484 | } | |
485 | } | |
486 | else if (ceph_argparse_flag(args, i, "--show_args", (char*)NULL)) { | |
487 | cout << "args: "; | |
488 | for (std::vector<const char *>::iterator ci = orig_args.begin(); ci != orig_args.end(); ++ci) { | |
489 | if (ci != orig_args.begin()) | |
490 | cout << " "; | |
491 | cout << *ci; | |
492 | } | |
493 | cout << std::endl; | |
494 | } | |
495 | else { | |
496 | // ignore | |
497 | ++i; | |
498 | } | |
499 | } | |
500 | return iparams; | |
501 | } | |
502 | ||
503 | static void generic_usage(bool is_server) | |
504 | { | |
505 | cout << "\ | |
506 | --conf/-c FILE read configuration from the given configuration file\n\ | |
507 | --id/-i ID set ID portion of my name\n\ | |
508 | --name/-n TYPE.ID set name\n\ | |
509 | --cluster NAME set cluster name (default: ceph)\n\ | |
510 | --setuser USER set uid to user or uid (and gid to user's gid)\n\ | |
511 | --setgroup GROUP set gid to group or gid\n\ | |
512 | --version show version and quit\n\ | |
513 | " << std::endl; | |
514 | ||
515 | if (is_server) { | |
516 | cout << "\ | |
517 | -d run in foreground, log to stderr.\n\ | |
518 | -f run in foreground, log to usual location.\n"; | |
519 | cout << "\ | |
520 | --debug_ms N set message debug level (e.g. 1)\n"; | |
521 | } | |
522 | ||
523 | cout.flush(); | |
524 | } | |
525 | ||
526 | void generic_server_usage() | |
527 | { | |
528 | generic_usage(true); | |
529 | exit(1); | |
530 | } | |
531 | void generic_client_usage() | |
532 | { | |
533 | generic_usage(false); | |
534 | exit(1); | |
535 | } |