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