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