1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
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.
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"
23 * Ceph argument parsing library
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
36 struct strict_str_convert
{
39 strict_str_convert(const char *str
, std::string
*err
)
40 : str(str
), err(err
) {}
42 inline operator float() const
44 return strict_strtof(str
, err
);
46 inline operator int() const
48 return strict_strtol(str
, 10, err
);
50 inline operator long long() const
52 return strict_strtoll(str
, 10, err
);
56 void string_to_vec(std::vector
<std::string
>& args
, std::string argstr
)
58 std::istringstream
iss(argstr
);
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(),
71 return strcmp(arg
, "--") == 0;
73 std::vector
<const char*> options
{args
.begin(), dashdash
};
74 if (dashdash
!= args
.end()) {
77 std::vector
<const char*> arguments
{dashdash
, args
.end()};
78 return {std::move(options
), std::move(arguments
)};
81 static std::mutex g_str_vec_lock
;
82 static std::vector
<std::string
> g_str_vec
;
84 void clear_g_str_vec()
86 g_str_vec_lock
.lock();
88 g_str_vec_lock
.unlock();
91 void env_to_vec(std::vector
<const char*>& args
, const char *name
)
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.
100 g_str_vec_lock
.lock();
101 if (g_str_vec
.empty()) {
102 char *p
= getenv(name
);
104 g_str_vec_lock
.unlock();
107 get_str_vec(p
, " ", g_str_vec
);
110 std::vector
<const char*> env
;
111 for (const auto& s
: g_str_vec
) {
112 env
.push_back(s
.c_str());
114 g_str_vec_lock
.unlock();
115 auto [env_options
, env_arguments
] = split_dashdash(env
);
117 auto [options
, arguments
] = split_dashdash(args
);
119 args
.insert(args
.end(), env_options
.begin(), env_options
.end());
120 args
.insert(args
.end(), options
.begin(), options
.end());
121 if (arguments
.empty() && env_arguments
.empty()) {
124 args
.push_back("--");
125 args
.insert(args
.end(), env_arguments
.begin(), env_arguments
.end());
126 args
.insert(args
.end(), arguments
.begin(), arguments
.end());
129 void argv_to_vec(int argc
, const char **argv
,
130 std::vector
<const char*>& args
)
132 args
.insert(args
.end(), argv
+ 1, argv
+ argc
);
135 void vec_to_argv(const char *argv0
, std::vector
<const char*>& args
,
136 int *argc
, const char ***argv
)
138 *argv
= (const char**)malloc(sizeof(char*) * (args
.size() + 1));
140 throw std::bad_alloc();
144 for (unsigned i
=0; i
<args
.size(); i
++)
145 (*argv
)[(*argc
)++] = args
[i
];
148 void ceph_arg_value_type(const char * nextargstr
, bool *bool_option
, bool *bool_numeric
)
150 bool is_numeric
= true;
151 bool is_float
= false;
154 if (nextargstr
== NULL
) {
158 if (strlen(nextargstr
) < 2) {
161 is_option
= (nextargstr
[0] == '-') && (nextargstr
[1] == '-');
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] == '-')
171 if ( (nextargstr
[i
] == '.') && (is_float
== false) ) {
182 if (nextargstr
[0] == '-' && is_numeric
== false) {
186 *bool_option
= is_option
;
187 *bool_numeric
= is_numeric
;
193 bool parse_ip_port_vec(const char *s
, std::vector
<entity_addrvec_t
>& vec
, int type
)
195 // first split by [ ;], which are not valid for an addrvec
196 std::list
<std::string
> items
;
197 get_str_list(s
, " ;", items
);
199 for (auto& i
: items
) {
200 const char *s
= i
.c_str();
204 // try parsing as an addr
206 if (a
.parse(s
, &end
, type
)) {
207 vec
.push_back(entity_addrvec_t(a
));
215 // ok, try parsing as an addrvec
217 if (!av
.parse(s
, &end
)) {
230 // The defaults for CephInitParameters
231 CephInitParameters::CephInitParameters(uint32_t module_type_
)
232 : module_type(module_type_
)
234 name
.set(module_type
, "admin");
237 static void dashes_to_underscores(const char *input
, char *output
)
241 const char *i
= input
;
242 // first two characters are copied as-is
249 for (; ((c
= *i
)); ++i
) {
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
)
267 if (strcmp(*i
, "--") == 0) {
274 bool ceph_argparse_flag(std::vector
<const char*> &args
,
275 std::vector
<const char*>::iterator
&i
, ...)
277 const char *first
= *i
;
278 char tmp
[strlen(first
)+1];
279 dashes_to_underscores(first
, tmp
);
285 const char *a
= va_arg(ap
, char*);
290 char a2
[strlen(a
)+1];
291 dashes_to_underscores(a
, a2
);
292 if (strcmp(a2
, first
) == 0) {
300 static bool check_bool_str(const char *val
, int *ret
)
302 if ((strcmp(val
, "true") == 0) || (strcmp(val
, "1") == 0)) {
305 } else if ((strcmp(val
, "false") == 0) || (strcmp(val
, "0") == 0)) {
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
)
316 const char *first
= *i
;
317 char tmp
[strlen(first
)+1];
318 dashes_to_underscores(first
, tmp
);
321 // does this argument match any of the possibilities?
323 const char *a
= va_arg(ap
, char*);
326 int strlen_a
= strlen(a
);
328 dashes_to_underscores(a
, a2
);
329 if (strncmp(a2
, first
, strlen(a2
)) == 0) {
330 if (first
[strlen_a
] == '=') {
332 const char *val
= first
+ strlen_a
+ 1;
333 if (check_bool_str(val
, ret
)) {
337 (*oss
) << "Parse error parsing binary flag " << a
338 << ". Expected true or false, but got '" << val
<< "'\n";
343 else if (first
[strlen_a
] == '\0') {
345 if (next
!= args
.end() &&
348 if (check_bool_str(*next
, ret
)) {
362 bool ceph_argparse_binary_flag(std::vector
<const char*> &args
,
363 std::vector
<const char*>::iterator
&i
, int *ret
,
364 std::ostream
*oss
, ...)
369 r
= va_ceph_argparse_binary_flag(args
, i
, ret
, oss
, ap
);
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
)
378 const char *first
= *i
;
379 char tmp
[strlen(first
)+1];
380 dashes_to_underscores(first
, tmp
);
383 // does this argument match any of the possibilities?
385 const char *a
= va_arg(ap
, char*);
388 int strlen_a
= strlen(a
);
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;
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
;
414 bool ceph_argparse_witharg(std::vector
<const char*> &args
,
415 std::vector
<const char*>::iterator
&i
, T
*ret
,
416 std::ostream
&oss
, ...)
420 bool is_option
= false;
421 bool is_numeric
= true;
424 r
= va_ceph_argparse_witharg(args
, i
, &str
, oss
, ap
);
432 ceph_arg_value_type(str
.c_str(), &is_option
, &is_numeric
);
433 if ((is_option
== true) || (is_numeric
== false)) {
435 if (is_option
== true) {
436 oss
<< "Missing option value";
438 oss
<< "The option value '" << str
<< "' is invalid";
444 T myret
= strict_str_convert(str
.c_str(), &err
);
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
, ...);
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
, ...);
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
, ...);
464 bool ceph_argparse_witharg(std::vector
<const char*> &args
,
465 std::vector
<const char*>::iterator
&i
, std::string
*ret
,
466 std::ostream
&oss
, ...)
471 r
= va_ceph_argparse_witharg(args
, i
, ret
, oss
, ap
);
476 bool ceph_argparse_witharg(std::vector
<const char*> &args
,
477 std::vector
<const char*>::iterator
&i
, std::string
*ret
, ...)
482 r
= va_ceph_argparse_witharg(args
, i
, ret
, std::cerr
, ap
);
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
)
493 CephInitParameters
iparams(module_type
);
496 auto orig_args
= args
;
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. */
505 else if (ceph_argparse_flag(args
, i
, "--version", "-v", (char*)NULL
)) {
506 std::cout
<< pretty_version_to_str() << std::endl
;
509 else if (ceph_argparse_witharg(args
, i
, &val
, "--conf", "-c", (char*)NULL
)) {
510 *conf_file_list
= val
;
512 else if (ceph_argparse_flag(args
, i
, "--no-config-file", (char*)NULL
)) {
513 iparams
.no_config_file
= true;
515 else if (ceph_argparse_witharg(args
, i
, &val
, "--cluster", (char*)NULL
)) {
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
);
522 else if (ceph_argparse_witharg(args
, i
, &val
, "--id", "--user", (char*)NULL
)) {
523 iparams
.name
.set_id(val
);
525 else if (ceph_argparse_witharg(args
, i
, &val
, "--name", "-n", (char*)NULL
)) {
526 if (!iparams
.name
.from_str(val
)) {
527 std::cerr
<< "error parsing '" << val
<< "': expected string of the form TYPE.ID, "
528 << "valid types are: " << EntityName::get_valid_types_as_str()
533 else if (ceph_argparse_flag(args
, i
, "--show_args", (char*)NULL
)) {
534 std::cout
<< "args: ";
535 for (std::vector
<const char *>::iterator ci
= orig_args
.begin(); ci
!= orig_args
.end(); ++ci
) {
536 if (ci
!= orig_args
.begin())
540 std::cout
<< std::endl
;
550 static void generic_usage(bool is_server
)
553 " --conf/-c FILE read configuration from the given configuration file" << std::endl
<<
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
566 " -d run in foreground, log to stderr" << std::endl
<<
567 " -f run in foreground, log to usual location" << std::endl
<<
569 " --debug_ms N set message debug level (e.g. 1)" << std::endl
;
575 bool ceph_argparse_need_usage(const std::vector
<const char*>& args
)
580 for (auto a
: args
) {
581 if (strcmp(a
, "-h") == 0 ||
582 strcmp(a
, "--help") == 0) {
589 void generic_server_usage()
594 void generic_client_usage()
596 generic_usage(false);