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