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