--- /dev/null
+// -*- 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);
+}