summaryrefslogtreecommitdiffstats
path: root/src/common/ceph_argparse.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/ceph_argparse.cc')
-rw-r--r--src/common/ceph_argparse.cc597
1 files changed, 597 insertions, 0 deletions
diff --git a/src/common/ceph_argparse.cc b/src/common/ceph_argparse.cc
new file mode 100644
index 000000000..72d97f046
--- /dev/null
+++ b/src/common/ceph_argparse.cc
@@ -0,0 +1,597 @@
+// -*- 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 <stdarg.h>
+
+#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)
+{
+ std::istringstream iss(argstr);
+ while(iss) {
+ std::string sub;
+ iss >> sub;
+ if (sub == "") break;
+ args.push_back(sub);
+ }
+}
+
+std::pair<std::vector<const char*>, std::vector<const char*>>
+split_dashdash(const std::vector<const char*>& args) {
+ auto dashdash = std::find_if(args.begin(), args.end(),
+ [](const char* arg) {
+ return strcmp(arg, "--") == 0;
+ });
+ std::vector<const char*> options{args.begin(), dashdash};
+ if (dashdash != args.end()) {
+ ++dashdash;
+ }
+ std::vector<const char*> arguments{dashdash, args.end()};
+ return {std::move(options), std::move(arguments)};
+}
+
+static std::mutex g_str_vec_lock;
+static std::vector<std::string> g_str_vec;
+
+void clear_g_str_vec()
+{
+ g_str_vec_lock.lock();
+ g_str_vec.clear();
+ g_str_vec_lock.unlock();
+}
+
+void env_to_vec(std::vector<const char*>& args, const char *name)
+{
+ if (!name)
+ name = "CEPH_ARGS";
+
+ /*
+ * We can only populate str_vec once. Other threads could hold pointers into
+ * it, so clearing it out and replacing it is not currently safe.
+ */
+ g_str_vec_lock.lock();
+ if (g_str_vec.empty()) {
+ char *p = getenv(name);
+ if (!p) {
+ g_str_vec_lock.unlock();
+ return;
+ }
+ get_str_vec(p, " ", g_str_vec);
+ }
+
+ std::vector<const char*> env;
+ for (const auto& s : g_str_vec) {
+ env.push_back(s.c_str());
+ }
+ g_str_vec_lock.unlock();
+ auto [env_options, env_arguments] = split_dashdash(env);
+
+ auto [options, arguments] = split_dashdash(args);
+ args.clear();
+ args.insert(args.end(), env_options.begin(), env_options.end());
+ args.insert(args.end(), options.begin(), options.end());
+ if (arguments.empty() && env_arguments.empty()) {
+ return;
+ }
+ args.push_back("--");
+ args.insert(args.end(), env_arguments.begin(), env_arguments.end());
+ args.insert(args.end(), arguments.begin(), 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 std::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, std::vector<entity_addrvec_t>& vec, int type)
+{
+ // first split by [ ;], which are not valid for an addrvec
+ std::list<std::string> items;
+ get_str_list(s, " ;", items);
+
+ for (auto& i : items) {
+ const char *s = i.c_str();
+ while (*s) {
+ const char *end;
+
+ // try parsing as an addr
+ entity_addr_t a;
+ if (a.parse(s, &end, type)) {
+ vec.push_back(entity_addrvec_t(a));
+ s = end;
+ if (*s == ',') {
+ ++s;
+ }
+ continue;
+ }
+
+ // ok, try parsing as an addrvec
+ entity_addrvec_t av;
+ if (!av.parse(s, &end)) {
+ return false;
+ }
+ vec.push_back(av);
+ s = end;
+ if (*s == ',') {
+ ++s;
+ }
+ }
+ }
+ 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 check_bool_str(const char *val, int *ret)
+{
+ 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;
+ }
+ return false;
+}
+
+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 (check_bool_str(val, ret)) {
+ 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') {
+ auto next = i+1;
+ if (next != args.end() &&
+ *next &&
+ (*next)[0] != '-') {
+ if (check_bool_str(*next, ret)) {
+ i = args.erase(i);
+ i = args.erase(i);
+ return true;
+ }
+ }
+ 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, std::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;
+
+ auto 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)) {
+ std::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_flag(args, i, "--no-config-file", (char*)NULL)) {
+ iparams.no_config_file = true;
+ }
+ 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)) {
+ std::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)) {
+ std::cout << "args: ";
+ for (std::vector<const char *>::iterator ci = orig_args.begin(); ci != orig_args.end(); ++ci) {
+ if (ci != orig_args.begin())
+ std::cout << " ";
+ std::cout << *ci;
+ }
+ std::cout << std::endl;
+ }
+ else {
+ // ignore
+ ++i;
+ }
+ }
+ return iparams;
+}
+
+static void generic_usage(bool is_server)
+{
+ std::cout <<
+ " --conf/-c FILE read configuration from the given configuration file" << std::endl <<
+ (is_server ?
+ " --id/-i ID set ID portion of my name" :
+ " --id ID set ID portion of my name") << std::endl <<
+ " --name/-n TYPE.ID set name" << std::endl <<
+ " --cluster NAME set cluster name (default: ceph)" << std::endl <<
+ " --setuser USER set uid to user or uid (and gid to user's gid)" << std::endl <<
+ " --setgroup GROUP set gid to group or gid" << std::endl <<
+ " --version show version and quit" << std::endl
+ << std::endl;
+
+ if (is_server) {
+ std::cout <<
+ " -d run in foreground, log to stderr" << std::endl <<
+ " -f run in foreground, log to usual location" << std::endl <<
+ std::endl <<
+ " --debug_ms N set message debug level (e.g. 1)" << std::endl;
+ }
+
+ std::cout.flush();
+}
+
+bool ceph_argparse_need_usage(const std::vector<const char*>& args)
+{
+ if (args.empty()) {
+ return true;
+ }
+ for (auto a : args) {
+ if (strcmp(a, "-h") == 0 ||
+ strcmp(a, "--help") == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void generic_server_usage()
+{
+ generic_usage(true);
+}
+
+void generic_client_usage()
+{
+ generic_usage(false);
+}