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