]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/ceph_argparse.cc
update ceph source to reef 18.2.0
[ceph.git] / ceph / src / common / ceph_argparse.cc
CommitLineData
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
36struct 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
56void 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
67std::pair<std::vector<const char*>, std::vector<const char*>>
68split_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 81static std::mutex g_str_vec_lock;
f67539c2 82static std::vector<std::string> g_str_vec;
b32b8144
FG
83
84void 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
91void 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 129std::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
135void 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
148void 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 193bool 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
231CephInitParameters::CephInitParameters(uint32_t module_type_)
232 : module_type(module_type_)
233{
234 name.set(module_type, "admin");
235}
236
237static 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. */
264bool 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
274bool 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
300static 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
312static 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
362bool 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
374static 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
413template<class T>
414bool 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
452template bool ceph_argparse_witharg<int>(std::vector<const char*> &args,
453 std::vector<const char*>::iterator &i, int *ret,
454 std::ostream &oss, ...);
455
456template 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
460template bool ceph_argparse_witharg<float>(std::vector<const char*> &args,
461 std::vector<const char*>::iterator &i, float *ret,
462 std::ostream &oss, ...);
463
464bool 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
476bool 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
489CephInitParameters 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
550static 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
575bool 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
589void generic_server_usage()
590{
591 generic_usage(true);
7c673cae 592}
11fdf7f2 593
7c673cae
FG
594void generic_client_usage()
595{
596 generic_usage(false);
7c673cae 597}