initial code repo
[stor4nfv.git] / src / ceph / src / common / ceph_argparse.cc
diff --git a/src/ceph/src/common/ceph_argparse.cc b/src/ceph/src/common/ceph_argparse.cc
new file mode 100644 (file)
index 0000000..355224d
--- /dev/null
@@ -0,0 +1,535 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#include "auth/Auth.h"
+#include "common/ceph_argparse.h"
+#include "common/config.h"
+#include "common/version.h"
+#include "include/str_list.h"
+
+/*
+ * Ceph argument parsing library
+ *
+ * We probably should eventually replace this with something standard like popt.
+ * Until we do that, though, this file is the place for argv parsing
+ * stuff to live.
+ */
+
+#undef dout
+#undef pdout
+#undef derr
+#undef generic_dout
+#undef dendl
+
+struct strict_str_convert {
+  const char *str;
+  std::string *err;
+  strict_str_convert(const char *str,  std::string *err)
+    : str(str), err(err) {}
+
+  inline operator float() const
+  {
+    return strict_strtof(str, err);
+  }
+  inline operator int() const
+  {
+    return strict_strtol(str, 10, err);
+  }
+  inline operator long long() const
+  {
+    return  strict_strtoll(str, 10, err);
+  }
+};
+
+void string_to_vec(std::vector<std::string>& args, std::string argstr)
+{
+  istringstream iss(argstr);
+  while(iss) {
+    string sub;
+    iss >> sub;
+    if (sub == "") break;
+    args.push_back(sub);
+  }
+}
+
+bool split_dashdash(const std::vector<const char*>& args,
+                   std::vector<const char*>& options,
+                   std::vector<const char*>& arguments) {
+  bool dashdash = false;
+  for (std::vector<const char*>::const_iterator i = args.begin();
+       i != args.end();
+       ++i) {
+    if (dashdash) {
+      arguments.push_back(*i);
+    } else {
+      if (strcmp(*i, "--") == 0)
+       dashdash = true;
+      else
+       options.push_back(*i);
+    }
+  }
+  return dashdash;
+}
+
+void env_to_vec(std::vector<const char*>& args, const char *name)
+{
+  if (!name)
+    name = "CEPH_ARGS";
+  char *p = getenv(name);
+  if (!p)
+    return;
+
+  bool dashdash = false;
+  std::vector<const char*> options;
+  std::vector<const char*> arguments;
+  if (split_dashdash(args, options, arguments))
+    dashdash = true;
+
+  std::vector<const char*> env_options;
+  std::vector<const char*> env_arguments;
+  static vector<string> str_vec;
+  std::vector<const char*> env;
+  str_vec.clear();
+  get_str_vec(p, " ", str_vec);
+  for (vector<string>::iterator i = str_vec.begin();
+       i != str_vec.end();
+       ++i)
+    env.push_back(i->c_str());
+  if (split_dashdash(env, env_options, env_arguments))
+    dashdash = true;
+
+  args.clear();
+  args.insert(args.end(), options.begin(), options.end());
+  args.insert(args.end(), env_options.begin(), env_options.end());
+  if (dashdash)
+    args.push_back("--");
+  args.insert(args.end(), arguments.begin(), arguments.end());
+  args.insert(args.end(), env_arguments.begin(), env_arguments.end());
+}
+
+void argv_to_vec(int argc, const char **argv,
+                 std::vector<const char*>& args)
+{
+  args.insert(args.end(), argv + 1, argv + argc);
+}
+
+void vec_to_argv(const char *argv0, std::vector<const char*>& args,
+                 int *argc, const char ***argv)
+{
+  *argv = (const char**)malloc(sizeof(char*) * (args.size() + 1));
+  if (!*argv)
+    throw bad_alloc();
+  *argc = 1;
+  (*argv)[0] = argv0;
+
+  for (unsigned i=0; i<args.size(); i++)
+    (*argv)[(*argc)++] = args[i];
+}
+
+void ceph_arg_value_type(const char * nextargstr, bool *bool_option, bool *bool_numeric)
+{
+  bool is_numeric = true;
+  bool is_float = false;
+  bool is_option;
+
+  if (nextargstr == NULL) {
+    return;
+  }
+
+  if (strlen(nextargstr) < 2) {
+    is_option = false;
+  } else {
+    is_option = (nextargstr[0] == '-') && (nextargstr[1] == '-');
+  }
+
+  for (unsigned int i = 0; i < strlen(nextargstr); i++) {
+    if (!(nextargstr[i] >= '0' && nextargstr[i] <= '9')) {
+      // May be negative numeral value
+      if ((i == 0) && (strlen(nextargstr) >= 2))  {
+       if (nextargstr[0] == '-')
+         continue;
+      }
+      if ( (nextargstr[i] == '.') && (is_float == false) ) {
+        is_float = true;
+        continue;
+      }
+        
+      is_numeric = false;
+      break;
+    }
+  }
+
+  // -<option>
+  if (nextargstr[0] == '-' && is_numeric == false) {
+    is_option = true;
+  }
+
+  *bool_option = is_option;
+  *bool_numeric = is_numeric;
+
+  return;
+}
+
+bool parse_ip_port_vec(const char *s, vector<entity_addr_t>& vec)
+{
+  const char *p = s;
+  const char *end = p + strlen(p);
+  while (p < end) {
+    entity_addr_t a;
+    //cout << " parse at '" << p << "'" << std::endl;
+    if (!a.parse(p, &p)) {
+      //dout(0) << " failed to parse address '" << p << "'" << dendl;
+      return false;
+    }
+    //cout << " got " << a << ", rest is '" << p << "'" << std::endl;
+    vec.push_back(a);
+    while (*p == ',' || *p == ' ' || *p == ';')
+      p++;
+  }
+  return true;
+}
+
+// The defaults for CephInitParameters
+CephInitParameters::CephInitParameters(uint32_t module_type_)
+  : module_type(module_type_)
+{
+  name.set(module_type, "admin");
+}
+
+static void dashes_to_underscores(const char *input, char *output)
+{
+  char c = 0;
+  char *o = output;
+  const char *i = input;
+  // first two characters are copied as-is
+  *o = *i++;
+  if (*o++ == '\0')
+    return;
+  *o = *i++;
+  if (*o++ == '\0')
+    return;
+  for (; ((c = *i)); ++i) {
+    if (c == '=') {
+      strcpy(o, i);
+      return;
+    }
+    if (c == '-')
+      *o++ = '_';
+    else
+      *o++ = c;
+  }
+  *o++ = '\0';
+}
+
+/** Once we see a standalone double dash, '--', we should remove it and stop
+ * looking for any other options and flags. */
+bool ceph_argparse_double_dash(std::vector<const char*> &args,
+       std::vector<const char*>::iterator &i)
+{
+  if (strcmp(*i, "--") == 0) {
+    i = args.erase(i);
+    return true;
+  }
+  return false;
+}
+
+bool ceph_argparse_flag(std::vector<const char*> &args,
+       std::vector<const char*>::iterator &i, ...)
+{
+  const char *first = *i;
+  char tmp[strlen(first)+1];
+  dashes_to_underscores(first, tmp);
+  first = tmp;
+  va_list ap;
+
+  va_start(ap, i);
+  while (1) {
+    const char *a = va_arg(ap, char*);
+    if (a == NULL) {
+      va_end(ap);
+      return false;
+    }
+    char a2[strlen(a)+1];
+    dashes_to_underscores(a, a2);
+    if (strcmp(a2, first) == 0) {
+      i = args.erase(i);
+      va_end(ap);
+      return true;
+    }
+  }
+}
+
+static bool va_ceph_argparse_binary_flag(std::vector<const char*> &args,
+       std::vector<const char*>::iterator &i, int *ret,
+       std::ostream *oss, va_list ap)
+{
+  const char *first = *i;
+  char tmp[strlen(first)+1];
+  dashes_to_underscores(first, tmp);
+  first = tmp;
+
+  // does this argument match any of the possibilities?
+  while (1) {
+    const char *a = va_arg(ap, char*);
+    if (a == NULL)
+      return false;
+    int strlen_a = strlen(a);
+    char a2[strlen_a+1];
+    dashes_to_underscores(a, a2);
+    if (strncmp(a2, first, strlen(a2)) == 0) {
+      if (first[strlen_a] == '=') {
+       i = args.erase(i);
+       const char *val = first + strlen_a + 1;
+       if ((strcmp(val, "true") == 0) || (strcmp(val, "1") == 0)) {
+         *ret = 1;
+         return true;
+       }
+       else if ((strcmp(val, "false") == 0) || (strcmp(val, "0") == 0)) {
+         *ret = 0;
+         return true;
+       }
+       if (oss) {
+         (*oss) << "Parse error parsing binary flag  " << a
+                << ". Expected true or false, but got '" << val << "'\n";
+       }
+       *ret = -EINVAL;
+       return true;
+      }
+      else if (first[strlen_a] == '\0') {
+       i = args.erase(i);
+       *ret = 1;
+       return true;
+      }
+    }
+  }
+}
+
+bool ceph_argparse_binary_flag(std::vector<const char*> &args,
+       std::vector<const char*>::iterator &i, int *ret,
+       std::ostream *oss, ...)
+{
+  bool r;
+  va_list ap;
+  va_start(ap, oss);
+  r = va_ceph_argparse_binary_flag(args, i, ret, oss, ap);
+  va_end(ap);
+  return r;
+}
+
+static int va_ceph_argparse_witharg(std::vector<const char*> &args,
+       std::vector<const char*>::iterator &i, std::string *ret,
+       std::ostream &oss, va_list ap)
+{
+  const char *first = *i;
+  char tmp[strlen(first)+1];
+  dashes_to_underscores(first, tmp);
+  first = tmp;
+
+  // does this argument match any of the possibilities?
+  while (1) {
+    const char *a = va_arg(ap, char*);
+    if (a == NULL)
+      return 0;
+    int strlen_a = strlen(a);
+    char a2[strlen_a+1];
+    dashes_to_underscores(a, a2);
+    if (strncmp(a2, first, strlen(a2)) == 0) {
+      if (first[strlen_a] == '=') {
+       *ret = first + strlen_a + 1;
+       i = args.erase(i);
+       return 1;
+      }
+      else if (first[strlen_a] == '\0') {
+       // find second part (or not)
+       if (i+1 == args.end()) {
+         oss << "Option " << *i << " requires an argument." << std::endl;
+         i = args.erase(i);
+         return -EINVAL;
+       }
+       i = args.erase(i);
+       *ret = *i;
+       i = args.erase(i);
+       return 1;
+      }
+    }
+  }
+}
+
+template<class T>
+bool ceph_argparse_witharg(std::vector<const char*> &args,
+       std::vector<const char*>::iterator &i, T *ret,
+       std::ostream &oss, ...)
+{
+  int r;
+  va_list ap;
+  bool is_option = false;
+  bool is_numeric = true;
+  std::string str;
+  va_start(ap, oss);
+  r = va_ceph_argparse_witharg(args, i, &str, oss, ap);
+  va_end(ap);
+  if (r == 0) {
+    return false;
+  } else if (r < 0) {
+    return true;
+  }
+
+  ceph_arg_value_type(str.c_str(), &is_option, &is_numeric);
+  if ((is_option == true) || (is_numeric == false)) {
+    *ret = EXIT_FAILURE;
+    if (is_option == true) {
+      oss << "Missing option value";
+    } else {
+      oss << "The option value '" << str << "' is invalid";
+    }
+    return true;
+  }
+
+  std::string err;
+  T myret = strict_str_convert(str.c_str(), &err);
+  *ret = myret;
+  if (!err.empty()) {
+    oss << err;
+  }
+  return true;
+}
+
+template bool ceph_argparse_witharg<int>(std::vector<const char*> &args,
+       std::vector<const char*>::iterator &i, int *ret,
+       std::ostream &oss, ...);
+
+template bool ceph_argparse_witharg<long long>(std::vector<const char*> &args,
+       std::vector<const char*>::iterator &i, long long *ret,
+       std::ostream &oss, ...);
+
+template bool ceph_argparse_witharg<float>(std::vector<const char*> &args,
+       std::vector<const char*>::iterator &i, float *ret,
+       std::ostream &oss, ...);
+
+bool ceph_argparse_witharg(std::vector<const char*> &args,
+       std::vector<const char*>::iterator &i, std::string *ret,
+       std::ostream &oss, ...)
+{
+  int r;
+  va_list ap;
+  va_start(ap, oss);
+  r = va_ceph_argparse_witharg(args, i, ret, oss, ap);
+  va_end(ap);
+  return r != 0;
+}
+
+bool ceph_argparse_witharg(std::vector<const char*> &args,
+       std::vector<const char*>::iterator &i, std::string *ret, ...)
+{
+  int r;
+  va_list ap;
+  va_start(ap, ret);
+  r = va_ceph_argparse_witharg(args, i, ret, cerr, ap);
+  va_end(ap);
+  if (r < 0)
+    _exit(1);
+  return r != 0;
+}
+
+CephInitParameters ceph_argparse_early_args
+         (std::vector<const char*>& args, uint32_t module_type,
+          std::string *cluster, std::string *conf_file_list)
+{
+  CephInitParameters iparams(module_type);
+  std::string val;
+
+  vector<const char *> orig_args = args;
+
+  for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
+    if (strcmp(*i, "--") == 0) {
+      /* Normally we would use ceph_argparse_double_dash. However, in this
+       * function we *don't* want to remove the double dash, because later
+       * argument parses will still need to see it. */
+      break;
+    }
+    else if (ceph_argparse_flag(args, i, "--version", "-v", (char*)NULL)) {
+      cout << pretty_version_to_str() << std::endl;
+      _exit(0);
+    }
+    else if (ceph_argparse_witharg(args, i, &val, "--conf", "-c", (char*)NULL)) {
+      *conf_file_list = val;
+    }
+    else if (ceph_argparse_witharg(args, i, &val, "--cluster", (char*)NULL)) {
+      *cluster = val;
+    }
+    else if ((module_type != CEPH_ENTITY_TYPE_CLIENT) &&
+            (ceph_argparse_witharg(args, i, &val, "-i", (char*)NULL))) {
+      iparams.name.set_id(val);
+    }
+    else if (ceph_argparse_witharg(args, i, &val, "--id", "--user", (char*)NULL)) {
+      iparams.name.set_id(val);
+    }
+    else if (ceph_argparse_witharg(args, i, &val, "--name", "-n", (char*)NULL)) {
+      if (!iparams.name.from_str(val)) {
+       cerr << "error parsing '" << val << "': expected string of the form TYPE.ID, "
+            << "valid types are: " << EntityName::get_valid_types_as_str()
+            << std::endl;
+       _exit(1);
+      }
+    }
+    else if (ceph_argparse_flag(args, i, "--show_args", (char*)NULL)) {
+      cout << "args: ";
+      for (std::vector<const char *>::iterator ci = orig_args.begin(); ci != orig_args.end(); ++ci) {
+        if (ci != orig_args.begin())
+          cout << " ";
+        cout << *ci;
+      }
+      cout << std::endl;
+    }
+    else {
+      // ignore
+      ++i;
+    }
+  }
+  return iparams;
+}
+
+static void generic_usage(bool is_server)
+{
+  cout << "\
+  --conf/-c FILE    read configuration from the given configuration file\n\
+  --id/-i ID        set ID portion of my name\n\
+  --name/-n TYPE.ID set name\n\
+  --cluster NAME    set cluster name (default: ceph)\n\
+  --setuser USER    set uid to user or uid (and gid to user's gid)\n\
+  --setgroup GROUP  set gid to group or gid\n\
+  --version         show version and quit\n\
+" << std::endl;
+
+  if (is_server) {
+    cout << "\
+  -d                run in foreground, log to stderr.\n\
+  -f                run in foreground, log to usual location.\n";
+    cout << "\
+  --debug_ms N      set message debug level (e.g. 1)\n";
+  }
+
+  cout.flush();
+}
+
+void generic_server_usage()
+{
+  generic_usage(true);
+  exit(1);
+}
+void generic_client_usage()
+{
+  generic_usage(false);
+  exit(1);
+}