]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/ceph_argparse.cc
update sources to v12.1.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 */
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
35struct 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
55void 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
66bool 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
85void 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
121void 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
127void 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
140void 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
184bool 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
204CephInitParameters::CephInitParameters(uint32_t module_type_)
205 : module_type(module_type_)
206{
207 name.set(module_type, "admin");
208}
209
210static 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. */
237bool 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
247bool 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
273static 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
318bool 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
330static 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
369template<class T>
370bool 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
408template bool ceph_argparse_witharg<int>(std::vector<const char*> &args,
409 std::vector<const char*>::iterator &i, int *ret,
410 std::ostream &oss, ...);
411
412template 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
416template bool ceph_argparse_witharg<float>(std::vector<const char*> &args,
417 std::vector<const char*>::iterator &i, float *ret,
418 std::ostream &oss, ...);
419
420bool 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
432bool 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
445CephInitParameters 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
503static 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
526void generic_server_usage()
527{
528 generic_usage(true);
529 exit(1);
530}
531void generic_client_usage()
532{
533 generic_usage(false);
534 exit(1);
535}