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