]>
Commit | Line | Data |
---|---|---|
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 | ||
48 | struct 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 | ||
68 | void 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 | ||
79 | bool 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 | ||
98 | void 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 | ||
134 | void 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 | ||
140 | void 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 | ||
153 | void 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 | ||
197 | bool 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 | |
217 | CephInitParameters::CephInitParameters(uint32_t module_type_) | |
218 | : module_type(module_type_) | |
219 | { | |
220 | name.set(module_type, "admin"); | |
221 | } | |
222 | ||
223 | static 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. */ | |
250 | bool 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 | ||
260 | bool 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 | ||
286 | static 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 | ||
331 | bool 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 | ||
343 | static 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 | ||
382 | template<class T> | |
383 | bool 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 | ||
421 | template bool ceph_argparse_witharg<int>(std::vector<const char*> &args, | |
422 | std::vector<const char*>::iterator &i, int *ret, | |
423 | std::ostream &oss, ...); | |
424 | ||
425 | template 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 | ||
429 | template bool ceph_argparse_witharg<float>(std::vector<const char*> &args, | |
430 | std::vector<const char*>::iterator &i, float *ret, | |
431 | std::ostream &oss, ...); | |
432 | ||
433 | bool 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 | ||
445 | bool 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 | ||
458 | CephInitParameters 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 | ||
516 | static 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 | ||
539 | void generic_server_usage() | |
540 | { | |
541 | generic_usage(true); | |
542 | exit(1); | |
543 | } | |
544 | void generic_client_usage() | |
545 | { | |
546 | generic_usage(false); | |
547 | exit(1); | |
548 | } |