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