*
*/
+#include <boost/type_traits.hpp>
+
#include "common/ceph_argparse.h"
#include "common/common_init.h"
#include "common/config.h"
+#include "common/config_obs.h"
#include "include/str_list.h"
#include "include/stringify.h"
#include "osd/osd_types.h"
#include "common/errno.h"
#include "common/hostname.h"
-#include "common/backport14.h"
-
-#include <boost/type_traits.hpp>
+#include "common/dout.h"
/* Don't use standard Ceph logging in this file.
* We can't use logging until it's initialized, and a lot of the necessary
* initialization happens here.
*/
#undef dout
-#undef ldout
#undef pdout
#undef derr
-#undef lderr
#undef generic_dout
-#undef dendl
+
+// set set_mon_vals()
+#define dout_subsys ceph_subsys_monc
using std::map;
using std::list;
using std::pair;
using std::string;
-const char *CEPH_CONF_FILE_DEFAULT = "$data_dir/config, /etc/ceph/$cluster.conf, ~/.ceph/$cluster.conf, $cluster.conf"
+static const char *CEPH_CONF_FILE_DEFAULT = "$data_dir/config, /etc/ceph/$cluster.conf, $home/.ceph/$cluster.conf, $cluster.conf"
#if defined(__FreeBSD__)
", /usr/local/etc/ceph/$cluster.conf"
#endif
#define _STR(x) #x
#define STRINGIFY(x) _STR(x)
+const char *ceph_conf_level_name(int level)
+{
+ switch (level) {
+ case CONF_DEFAULT: return "default"; // built-in default
+ case CONF_MON: return "mon"; // monitor config database
+ case CONF_ENV: return "env"; // process environment (CEPH_ARGS)
+ case CONF_FILE: return "file"; // ceph.conf file
+ case CONF_CMDLINE: return "cmdline"; // process command line args
+ case CONF_OVERRIDE: return "override"; // injectargs or 'config set' at runtime
+ case CONF_FINAL: return "final";
+ default: return "???";
+ }
+}
+
int ceph_resolve_file_search(const std::string& filename_list,
std::string& result)
{
return ret;
}
-
-
-md_config_t::md_config_t(bool is_daemon)
- : cluster(""),
- lock("md_config_t", true, false)
+static int conf_stringify(const Option::value_t& v, string *out)
{
- init_subsys();
+ if (boost::get<boost::blank>(&v)) {
+ return -ENOENT;
+ }
+ *out = Option::to_str(v);
+ return 0;
+}
+md_config_t::md_config_t(ConfigValues& values,
+ const ConfigTracker& tracker,
+ bool is_daemon)
+ : is_daemon(is_daemon)
+{
// Load the compile-time list of Option into
// a map so that we can resolve keys quickly.
for (const auto &i : ceph_options) {
// We may be instantiated pre-logging so send
std::cerr << "Duplicate config key in schema: '" << i.name << "'"
<< std::endl;
- assert(false);
+ ceph_abort();
}
- schema.insert({i.name, i});
+ schema.emplace(std::piecewise_construct,
+ std::forward_as_tuple(i.name),
+ std::forward_as_tuple(i));
+ }
+
+ // Define the debug_* options as well.
+ subsys_options.reserve(values.subsys.get_num());
+ for (unsigned i = 0; i < values.subsys.get_num(); ++i) {
+ string name = string("debug_") + values.subsys.get_name(i);
+ subsys_options.push_back(
+ Option(name, Option::TYPE_STR, Option::LEVEL_ADVANCED));
+ Option& opt = subsys_options.back();
+ opt.set_default(stringify(values.subsys.get_log_level(i)) + "/" +
+ stringify(values.subsys.get_gather_level(i)));
+ string desc = string("Debug level for ") + values.subsys.get_name(i);
+ opt.set_description(desc.c_str());
+ opt.set_flag(Option::FLAG_RUNTIME);
+ opt.set_long_description("The value takes the form 'N' or 'N/M' where N and M are values between 0 and 99. N is the debug level to log (all values below this are included), and M is the level to gather and buffer in memory. In the event of a crash, the most recent items <= M are dumped to the log file.");
+ opt.set_subsys(i);
+ opt.set_validator([](std::string *value, std::string *error_message) {
+ int m, n;
+ int r = sscanf(value->c_str(), "%d/%d", &m, &n);
+ if (r >= 1) {
+ if (m < 0 || m > 99) {
+ *error_message = "value must be in range [0, 99]";
+ return -ERANGE;
+ }
+ if (r == 2) {
+ if (n < 0 || n > 99) {
+ *error_message = "value must be in range [0, 99]";
+ return -ERANGE;
+ }
+ } else {
+ // normalize to M/N
+ n = m;
+ *value = stringify(m) + "/" + stringify(n);
+ }
+ } else {
+ *error_message = "value must take the form N or N/M, where N and M are integers";
+ return -EINVAL;
+ }
+ return 0;
+ });
+ }
+ for (auto& opt : subsys_options) {
+ schema.emplace(opt.name, opt);
}
// Populate list of legacy_values according to the OPTION() definitions
// members if present.
legacy_values = {
#define OPTION(name, type) \
- {std::string(STRINGIFY(name)), &md_config_t::name},
+ {std::string(STRINGIFY(name)), &ConfigValues::name},
#define SAFE_OPTION(name, type) OPTION(name, type)
#include "common/legacy_config_opts.h"
#undef OPTION
validate_schema();
- // Load default values from the schema
+ // Validate default values from the schema
for (const auto &i : schema) {
const Option &opt = i.second;
- bool has_daemon_default = !boost::get<boost::blank>(&opt.daemon_value);
- Option::value_t default_val;
- if (is_daemon && has_daemon_default) {
- default_val = opt.daemon_value;
- } else {
- default_val = opt.value;
- }
-
if (opt.type == Option::TYPE_STR) {
+ bool has_daemon_default = !boost::get<boost::blank>(&opt.daemon_value);
+ Option::value_t default_val;
+ if (is_daemon && has_daemon_default) {
+ default_val = opt.daemon_value;
+ } else {
+ default_val = opt.value;
+ }
// We call pre_validate as a sanity check, but also to get any
// side effect (value modification) from the validator.
std::string *def_str = boost::get<std::string>(&default_val);
+ std::string val = *def_str;
std::string err;
- if (opt.pre_validate(def_str, &err) != 0) {
+ if (opt.pre_validate(&val, &err) != 0) {
std::cerr << "Default value " << opt.name << "=" << *def_str << " is "
"invalid: " << err << std::endl;
// This is the compiled-in default that is failing its own option's
// validation, so this is super-invalid and should never make it
// past a pull request: crash out.
- assert(false);
+ ceph_abort();
+ }
+ if (val != *def_str) {
+ // if the validator normalizes the string into a different form than
+ // what was compiled in, use that.
+ set_val_default(values, tracker, opt.name, val);
}
}
-
- values[i.first] = default_val;
}
// Copy out values (defaults) into any legacy (C struct member) fields
- for (const auto &i : legacy_values) {
- const auto &name = i.first;
- const auto &option = schema.at(name);
- auto ptr = i.second;
+ update_legacy_vals(values);
+}
- update_legacy_val(option, ptr);
- }
+md_config_t::~md_config_t()
+{
}
/**
if (schema.count(see_also_key) == 0) {
std::cerr << "Non-existent see-also key '" << see_also_key
<< "' on option '" << opt.name << "'" << std::endl;
- assert(false);
+ ceph_abort();
}
}
}
if (schema.count(i.first) == 0) {
std::cerr << "Schema is missing legacy field '" << i.first << "'"
<< std::endl;
- assert(false);
+ ceph_abort();
}
}
}
-void md_config_t::init_subsys()
-{
-#define SUBSYS(name, log, gather) \
- subsys.add(ceph_subsys_##name, STRINGIFY(name), log, gather);
-#define DEFAULT_SUBSYS(log, gather) \
- subsys.add(ceph_subsys_, "none", log, gather);
-#include "common/subsys.h"
-#undef SUBSYS
-#undef DEFAULT_SUBSYS
-}
-
-md_config_t::~md_config_t()
+const Option *md_config_t::find_option(const string& name) const
{
+ auto p = schema.find(name);
+ if (p != schema.end()) {
+ return &p->second;
+ }
+ return nullptr;
}
-void md_config_t::add_observer(md_config_obs_t* observer_)
+void md_config_t::set_val_default(ConfigValues& values,
+ const ConfigTracker& tracker,
+ const string& name, const std::string& val)
{
- Mutex::Locker l(lock);
- const char **keys = observer_->get_tracked_conf_keys();
- for (const char ** k = keys; *k; ++k) {
- obs_map_t::value_type val(*k, observer_);
- observers.insert(val);
- }
- obs_call_gate.emplace(observer_, ceph::make_unique<CallGate>());
+ const Option *o = find_option(name);
+ ceph_assert(o);
+ string err;
+ int r = _set_val(values, tracker, val, *o, CONF_DEFAULT, &err);
+ ceph_assert(r >= 0);
}
-void md_config_t::remove_observer(md_config_obs_t* observer_)
+int md_config_t::set_mon_vals(CephContext *cct,
+ ConfigValues& values,
+ const ConfigTracker& tracker,
+ const map<string,string>& kv,
+ config_callback config_cb)
{
- Mutex::Locker l(lock);
+ ignored_mon_values.clear();
- call_gate_close(observer_);
- obs_call_gate.erase(observer_);
+ if (!config_cb) {
+ ldout(cct, 4) << __func__ << " no callback set" << dendl;
+ }
- bool found_obs = false;
- for (obs_map_t::iterator o = observers.begin(); o != observers.end(); ) {
- if (o->second == observer_) {
- observers.erase(o++);
- found_obs = true;
+ for (auto& i : kv) {
+ if (config_cb) {
+ if (config_cb(i.first, i.second)) {
+ ldout(cct, 4) << __func__ << " callback consumed " << i.first << dendl;
+ continue;
+ }
+ ldout(cct, 4) << __func__ << " callback ignored " << i.first << dendl;
}
- else {
- ++o;
+ const Option *o = find_option(i.first);
+ if (!o) {
+ ldout(cct,10) << __func__ << " " << i.first << " = " << i.second
+ << " (unrecognized option)" << dendl;
+ continue;
+ }
+ if (o->has_flag(Option::FLAG_NO_MON_UPDATE)) {
+ ignored_mon_values.emplace(i);
+ continue;
+ }
+ std::string err;
+ int r = _set_val(values, tracker, i.second, *o, CONF_MON, &err);
+ if (r < 0) {
+ lderr(cct) << __func__ << " failed to set " << i.first << " = "
+ << i.second << ": " << err << dendl;
+ ignored_mon_values.emplace(i);
+ } else if (r == ConfigValues::SET_NO_CHANGE ||
+ r == ConfigValues::SET_NO_EFFECT) {
+ ldout(cct,20) << __func__ << " " << i.first << " = " << i.second
+ << " (no change)" << dendl;
+ } else if (r == ConfigValues::SET_HAVE_EFFECT) {
+ ldout(cct,10) << __func__ << " " << i.first << " = " << i.second << dendl;
+ } else {
+ ceph_abort();
}
}
- assert(found_obs);
+ values.for_each([&] (auto name, auto configs) {
+ auto config = configs.find(CONF_MON);
+ if (config == configs.end()) {
+ return;
+ }
+ if (kv.find(name) != kv.end()) {
+ return;
+ }
+ ldout(cct,10) << __func__ << " " << name
+ << " cleared (was " << Option::to_str(config->second) << ")"
+ << dendl;
+ values.rm_val(name, CONF_MON);
+ });
+ values_bl.clear();
+ update_legacy_vals(values);
+ return 0;
}
-int md_config_t::parse_config_files(const char *conf_files,
+int md_config_t::parse_config_files(ConfigValues& values,
+ const ConfigTracker& tracker,
+ const char *conf_files_str,
std::ostream *warnings,
int flags)
{
- Mutex::Locker l(lock);
- if (internal_safe_to_start_threads)
+ if (safe_to_start_threads)
return -ENOSYS;
- if (!cluster.size() && !conf_files) {
+ if (!values.cluster.size() && !conf_files_str) {
/*
* set the cluster name to 'ceph' when neither cluster name nor
* configuration file are specified.
*/
- cluster = "ceph";
+ values.cluster = "ceph";
}
- if (!conf_files) {
+ if (!conf_files_str) {
const char *c = getenv("CEPH_CONF");
if (c) {
- conf_files = c;
+ conf_files_str = c;
}
else {
if (flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE)
return 0;
- conf_files = CEPH_CONF_FILE_DEFAULT;
+ conf_files_str = CEPH_CONF_FILE_DEFAULT;
}
}
- std::list<std::string> cfl;
- get_str_list(conf_files, cfl);
-
- auto p = cfl.begin();
- while (p != cfl.end()) {
- // expand $data_dir?
+ std::list<std::string> conf_files;
+ get_str_list(conf_files_str, conf_files);
+ auto p = conf_files.begin();
+ while (p != conf_files.end()) {
string &s = *p;
- if (s.find("$data_dir") != string::npos) {
- if (data_dir_option.length()) {
- list<const Option *> stack;
- expand_meta(s, NULL, stack, warnings);
- p++;
- } else {
- cfl.erase(p++); // ignore this item
- }
+ if (s.find("$data_dir") != string::npos &&
+ data_dir_option.empty()) {
+ // useless $data_dir item, skip
+ p = conf_files.erase(p);
} else {
+ early_expand_meta(values, s, warnings);
++p;
}
}
- return parse_config_files_impl(cfl, warnings);
-}
-
-int md_config_t::parse_config_files_impl(const std::list<std::string> &conf_files,
- std::ostream *warnings)
-{
- assert(lock.is_locked());
// open new conf
list<string>::const_iterator c;
for (c = conf_files.begin(); c != conf_files.end(); ++c) {
cf.clear();
string fn = *c;
- expand_meta(fn, warnings);
+
int ret = cf.parse_file(fn.c_str(), &parse_errors, warnings);
if (ret == 0)
break;
if (c == conf_files.end())
return -ENOENT;
- if (cluster.size() == 0) {
+ if (values.cluster.size() == 0) {
/*
* If cluster name is not set yet, use the prefix of the
* basename of configuration file as cluster name.
* convention, we do the last try and assign the cluster to
* 'ceph'.
*/
- cluster = "ceph";
+ values.cluster = "ceph";
} else {
- cluster = c->substr(start, end - start);
+ values.cluster = c->substr(start, end - start);
}
}
std::vector <std::string> my_sections;
- _get_my_sections(my_sections);
+ _get_my_sections(values, my_sections);
for (const auto &i : schema) {
const auto &opt = i.second;
std::string val;
- int ret = _get_val_from_conf_file(my_sections, opt.name, val, false);
+ int ret = _get_val_from_conf_file(my_sections, opt.name, val);
if (ret == 0) {
std::string error_message;
- int r = set_val_impl(val, opt, &error_message);
- if (warnings != nullptr && (r != 0 || !error_message.empty())) {
+ int r = _set_val(values, tracker, val, opt, CONF_FILE, &error_message);
+ if (warnings != nullptr && (r < 0 || !error_message.empty())) {
*warnings << "parse error setting '" << opt.name << "' to '" << val
<< "'";
if (!error_message.empty()) {
}
}
- // subsystems?
- for (size_t o = 0; o < subsys.get_num(); o++) {
- std::string as_option("debug_");
- as_option += subsys.get_name(o);
- std::string val;
- int ret = _get_val_from_conf_file(my_sections, as_option.c_str(), val, false);
- if (ret == 0) {
- int log, gather;
- int r = sscanf(val.c_str(), "%d/%d", &log, &gather);
- if (r >= 1) {
- if (r < 2)
- gather = log;
- // cout << "config subsys " << subsys.get_name(o) << " log " << log << " gather " << gather << std::endl;
- subsys.set_log_level(o, log);
- subsys.set_gather_level(o, gather);
- }
- }
- }
-
// Warn about section names that look like old-style section names
std::deque < std::string > old_style_section_names;
for (ConfFile::const_section_iter_t s = cf.sections_begin();
}
cerr << ". Please use the new style section names that include a period.";
}
+
+ update_legacy_vals(values);
+
return 0;
}
-void md_config_t::parse_env()
+void md_config_t::parse_env(unsigned entity_type,
+ ConfigValues& values,
+ const ConfigTracker& tracker,
+ const char *args_var)
{
- Mutex::Locker l(lock);
- if (internal_safe_to_start_threads)
+ if (safe_to_start_threads)
return;
+ if (!args_var) {
+ args_var = "CEPH_ARGS";
+ }
if (getenv("CEPH_KEYRING")) {
- set_val_or_die("keyring", getenv("CEPH_KEYRING"));
+ _set_val(values, tracker, getenv("CEPH_KEYRING"), *find_option("keyring"),
+ CONF_ENV, nullptr);
+ }
+ if (const char *dir = getenv("CEPH_LIB")) {
+ for (auto name : { "erasure_code_dir", "plugin_dir", "osd_class_dir" }) {
+ std::string err;
+ const Option *o = find_option(name);
+ ceph_assert(o);
+ _set_val(values, tracker, dir, *o, CONF_ENV, &err);
+ }
+ }
+ const char *pod_req = getenv("POD_MEMORY_REQUEST");
+ if (pod_req) {
+ uint64_t v = atoll(pod_req);
+ if (v) {
+ switch (entity_type) {
+ case CEPH_ENTITY_TYPE_OSD:
+ _set_val(values, tracker, stringify(v),
+ *find_option("osd_memory_target"),
+ CONF_ENV, nullptr);
+ break;
+ }
+ }
+ }
+ if (getenv(args_var)) {
+ vector<const char *> env_args;
+ env_to_vec(env_args, args_var);
+ parse_argv(values, tracker, env_args, CONF_ENV);
}
}
-void md_config_t::show_config(std::ostream& out)
+void md_config_t::show_config(const ConfigValues& values,
+ std::ostream& out) const
{
- Mutex::Locker l(lock);
- _show_config(&out, NULL);
+ _show_config(values, &out, nullptr);
}
-void md_config_t::show_config(Formatter *f)
+void md_config_t::show_config(const ConfigValues& values,
+ Formatter *f) const
{
- Mutex::Locker l(lock);
- _show_config(NULL, f);
+ _show_config(values, nullptr, f);
}
-void md_config_t::config_options(Formatter *f)
+void md_config_t::config_options(Formatter *f) const
{
- Mutex::Locker l(lock);
f->open_array_section("options");
for (const auto& i: schema) {
- const Option &opt = i.second;
- opt.dump(f);
+ f->dump_object("option", i.second);
}
f->close_section();
}
-void md_config_t::_show_config(std::ostream *out, Formatter *f)
+void md_config_t::_show_config(const ConfigValues& values,
+ std::ostream *out, Formatter *f) const
{
if (out) {
- *out << "name = " << name << std::endl;
- *out << "cluster = " << cluster << std::endl;
+ *out << "name = " << values.name << std::endl;
+ *out << "cluster = " << values.cluster << std::endl;
}
if (f) {
- f->dump_string("name", stringify(name));
- f->dump_string("cluster", cluster);
- }
- for (size_t o = 0; o < subsys.get_num(); o++) {
- if (out)
- *out << "debug_" << subsys.get_name(o)
- << " = " << subsys.get_log_level(o)
- << "/" << subsys.get_gather_level(o) << std::endl;
- if (f) {
- ostringstream ss;
- std::string debug_name = "debug_";
- debug_name += subsys.get_name(o);
- ss << subsys.get_log_level(o)
- << "/" << subsys.get_gather_level(o);
- f->dump_string(debug_name.c_str(), ss.str());
- }
+ f->dump_string("name", stringify(values.name));
+ f->dump_string("cluster", values.cluster);
}
for (const auto& i: schema) {
const Option &opt = i.second;
- char *buf;
- _get_val(opt.name, &buf, -1);
- if (out)
- *out << opt.name << " = " << buf << std::endl;
- if (f)
- f->dump_string(opt.name.c_str(), buf);
- free(buf);
+ string val;
+ conf_stringify(_get_val(values, opt), &val);
+ if (out) {
+ *out << opt.name << " = " << val << std::endl;
+ }
+ if (f) {
+ f->dump_string(opt.name.c_str(), val);
+ }
}
}
-int md_config_t::parse_argv(std::vector<const char*>& args)
+int md_config_t::parse_argv(ConfigValues& values,
+ const ConfigTracker& tracker,
+ std::vector<const char*>& args, int level)
{
- Mutex::Locker l(lock);
- if (internal_safe_to_start_threads) {
+ if (safe_to_start_threads) {
return -ENOSYS;
}
- bool show_config = false;
- bool show_config_value = false;
- string show_config_value_arg;
-
// In this function, don't change any parts of the configuration directly.
// Instead, use set_val to set them. This will allow us to send the proper
// observer notifications later.
_exit(0);
}
else if (ceph_argparse_flag(args, i, "--show_config", (char*)NULL)) {
- show_config = true;
+ do_show_config = true;
}
else if (ceph_argparse_witharg(args, i, &val, "--show_config_value", (char*)NULL)) {
- show_config_value = true;
- show_config_value_arg = val;
+ do_show_config_value = val;
+ }
+ else if (ceph_argparse_flag(args, i, "--no-mon-config", (char*)NULL)) {
+ values.no_mon_config = true;
+ }
+ else if (ceph_argparse_flag(args, i, "--log-early", (char*)NULL)) {
+ values.log_early = true;
+ }
+ else if (ceph_argparse_flag(args, i, "--mon-config", (char*)NULL)) {
+ values.no_mon_config = false;
}
else if (ceph_argparse_flag(args, i, "--foreground", "-f", (char*)NULL)) {
- set_val_or_die("daemonize", "false");
+ set_val_or_die(values, tracker, "daemonize", "false");
}
else if (ceph_argparse_flag(args, i, "-d", (char*)NULL)) {
- set_val_or_die("daemonize", "false");
- set_val_or_die("log_file", "");
- set_val_or_die("log_to_stderr", "true");
- set_val_or_die("err_to_stderr", "true");
- set_val_or_die("log_to_syslog", "false");
+ set_val_or_die(values, tracker, "daemonize", "false");
+ set_val_or_die(values, tracker, "log_file", "");
+ set_val_or_die(values, tracker, "log_to_stderr", "true");
+ set_val_or_die(values, tracker, "err_to_stderr", "true");
+ set_val_or_die(values, tracker, "log_to_syslog", "false");
}
// Some stuff that we wanted to give universal single-character options for
// Careful: you can burn through the alphabet pretty quickly by adding
// to this list.
else if (ceph_argparse_witharg(args, i, &val, "--monmap", "-M", (char*)NULL)) {
- set_val_or_die("monmap", val.c_str());
+ set_val_or_die(values, tracker, "monmap", val.c_str());
}
else if (ceph_argparse_witharg(args, i, &val, "--mon_host", "-m", (char*)NULL)) {
- set_val_or_die("mon_host", val.c_str());
+ set_val_or_die(values, tracker, "mon_host", val.c_str());
}
else if (ceph_argparse_witharg(args, i, &val, "--bind", (char*)NULL)) {
- set_val_or_die("public_addr", val.c_str());
+ set_val_or_die(values, tracker, "public_addr", val.c_str());
}
else if (ceph_argparse_witharg(args, i, &val, "--keyfile", "-K", (char*)NULL)) {
- set_val_or_die("keyfile", val.c_str());
+ bufferlist bl;
+ string err;
+ int r;
+ if (val == "-") {
+ r = bl.read_fd(STDIN_FILENO, 1024);
+ } else {
+ r = bl.read_file(val.c_str(), &err);
+ }
+ if (r >= 0) {
+ string k(bl.c_str(), bl.length());
+ set_val_or_die(values, tracker, "key", k.c_str());
+ }
}
else if (ceph_argparse_witharg(args, i, &val, "--keyring", "-k", (char*)NULL)) {
- set_val_or_die("keyring", val.c_str());
+ set_val_or_die(values, tracker, "keyring", val.c_str());
}
else if (ceph_argparse_witharg(args, i, &val, "--client_mountpoint", "-r", (char*)NULL)) {
- set_val_or_die("client_mountpoint", val.c_str());
+ set_val_or_die(values, tracker, "client_mountpoint", val.c_str());
}
else {
- int r = parse_option(args, i, NULL);
+ int r = parse_option(values, tracker, args, i, NULL, level);
if (r < 0) {
return r;
}
}
}
+ // meta expands could have modified anything. Copy it all out again.
+ update_legacy_vals(values);
+ return 0;
+}
+
+void md_config_t::do_argv_commands(const ConfigValues& values) const
+{
- if (show_config) {
- expand_all_meta();
- _show_config(&cout, NULL);
+ if (do_show_config) {
+ _show_config(values, &cout, NULL);
_exit(0);
}
- if (show_config_value) {
- char *buf = 0;
- int r = _get_val(show_config_value_arg.c_str(), &buf, -1);
+ if (do_show_config_value.size()) {
+ string val;
+ int r = conf_stringify(_get_val(values, do_show_config_value, 0, &cerr),
+ &val);
if (r < 0) {
if (r == -ENOENT)
- std::cerr << "failed to get config option '" <<
- show_config_value_arg << "': option not found" << std::endl;
+ std::cerr << "failed to get config option '"
+ << do_show_config_value << "': option not found" << std::endl;
else
- std::cerr << "failed to get config option '" <<
- show_config_value_arg << "': " << cpp_strerror(r) << std::endl;
+ std::cerr << "failed to get config option '"
+ << do_show_config_value << "': " << cpp_strerror(r)
+ << std::endl;
_exit(1);
}
- string s = buf;
- expand_meta(s, &std::cerr);
- std::cout << s << std::endl;
+ std::cout << val << std::endl;
_exit(0);
}
-
- return 0;
}
-int md_config_t::parse_option(std::vector<const char*>& args,
- std::vector<const char*>::iterator& i,
- ostream *oss)
+int md_config_t::parse_option(ConfigValues& values,
+ const ConfigTracker& tracker,
+ std::vector<const char*>& args,
+ std::vector<const char*>::iterator& i,
+ ostream *oss,
+ int level)
{
int ret = 0;
size_t o = 0;
std::string val;
- // subsystems?
- for (o = 0; o < subsys.get_num(); o++) {
- std::string as_option("--");
- as_option += "debug_";
- as_option += subsys.get_name(o);
- ostringstream err;
- if (ceph_argparse_witharg(args, i, &val, err,
- as_option.c_str(), (char*)NULL)) {
- if (err.tellp()) {
- if (oss) {
- *oss << err.str();
- }
- ret = -EINVAL;
- break;
- }
- int log, gather;
- int r = sscanf(val.c_str(), "%d/%d", &log, &gather);
- if (r >= 1) {
- if (r < 2)
- gather = log;
- // cout << "subsys " << subsys.get_name(o) << " log " << log << " gather " << gather << std::endl;
- subsys.set_log_level(o, log);
- subsys.set_gather_level(o, gather);
- if (oss)
- *oss << "debug_" << subsys.get_name(o) << "=" << log << "/" << gather << " ";
- }
- break;
- }
- }
- if (o < subsys.get_num()) {
- return ret;
- }
-
std::string option_name;
std::string error_message;
o = 0;
std::string as_option("--");
as_option += opt.name;
option_name = opt.name;
- if (opt.type == Option::TYPE_BOOL) {
+ if (ceph_argparse_witharg(
+ args, i, &val, err,
+ string(string("--default-") + opt.name).c_str(), (char*)NULL)) {
+ if (!err.str().empty()) {
+ error_message = err.str();
+ ret = -EINVAL;
+ break;
+ }
+ ret = _set_val(values, tracker, val, opt, CONF_DEFAULT, &error_message);
+ break;
+ } else if (opt.type == Option::TYPE_BOOL) {
int res;
if (ceph_argparse_binary_flag(args, i, &res, oss, as_option.c_str(),
(char*)NULL)) {
if (res == 0)
- ret = set_val_impl("false", opt, &error_message);
+ ret = _set_val(values, tracker, "false", opt, level, &error_message);
else if (res == 1)
- ret = set_val_impl("true", opt, &error_message);
+ ret = _set_val(values, tracker, "true", opt, level, &error_message);
else
ret = res;
break;
std::string no("--no-");
no += opt.name;
if (ceph_argparse_flag(args, i, no.c_str(), (char*)NULL)) {
- ret = set_val_impl("false", opt, &error_message);
+ ret = _set_val(values, tracker, "false", opt, level, &error_message);
break;
}
}
ret = -EINVAL;
break;
}
- if (oss && ((!opt.is_safe()) &&
- (observers.find(opt.name) == observers.end()))) {
- *oss << "You cannot change " << opt.name << " using injectargs.\n";
- return -ENOSYS;
- }
- ret = set_val_impl(val, opt, &error_message);
+ ret = _set_val(values, tracker, val, opt, level, &error_message);
break;
}
++o;
}
- if (ret != 0 || !error_message.empty()) {
- assert(!option_name.empty());
+ if (ret < 0 || !error_message.empty()) {
+ ceph_assert(!option_name.empty());
if (oss) {
*oss << "Parse error setting " << option_name << " to '"
<< val << "' using injectargs";
// ignore
++i;
}
- return ret;
+ return ret >= 0 ? 0 : ret;
}
-int md_config_t::parse_injectargs(std::vector<const char*>& args,
+int md_config_t::parse_injectargs(ConfigValues& values,
+ const ConfigTracker& tracker,
+ std::vector<const char*>& args,
std::ostream *oss)
{
- assert(lock.is_locked());
int ret = 0;
for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
- int r = parse_option(args, i, oss);
+ int r = parse_option(values, tracker, args, i, oss, CONF_OVERRIDE);
if (r < 0)
ret = r;
}
return ret;
}
-void md_config_t::apply_changes(std::ostream *oss)
+void md_config_t::set_safe_to_start_threads()
{
- rev_obs_map_t rev_obs;
- {
- Mutex::Locker l(lock);
- /*
- * apply changes until the cluster name is assigned
- */
- if (cluster.size()) {
- for_each_change(
- oss, [this, &rev_obs](md_config_obs_t *obs, const std::string &key) {
- map_observer_changes(obs, key, &rev_obs);
- });
- }
- }
-
- call_observers(rev_obs);
+ safe_to_start_threads = true;
}
-bool md_config_t::_internal_field(const string& s)
+void md_config_t::_clear_safe_to_start_threads()
{
- if (s == "internal_safe_to_start_threads")
- return true;
- return false;
+ safe_to_start_threads = false;
}
-void md_config_t::for_each_change(std::ostream *oss, config_gather_cb callback)
-{
- expand_all_meta();
-
- // expand_all_meta could have modified anything. Copy it all out again.
- for (const auto &i : legacy_values) {
- const auto &name = i.first;
- const auto &option = schema.at(name);
- auto ptr = i.second;
-
- update_legacy_val(option, ptr);
- }
-
- std::set <std::string> empty_set;
- char buf[128];
- char *bufptr = (char*)buf;
- for (changed_set_t::const_iterator c = changed.begin();
- c != changed.end(); ++c) {
- const std::string &key(*c);
- pair < obs_map_t::iterator, obs_map_t::iterator >
- range(observers.equal_range(key));
- if ((oss) &&
- (!_get_val(key.c_str(), &bufptr, sizeof(buf))) &&
- !_internal_field(key)) {
- (*oss) << key << " = '" << buf << "' ";
- if (range.first == range.second) {
- (*oss) << "(not observed, change may require restart) ";
- }
- }
- for (obs_map_t::iterator r = range.first; r != range.second; ++r) {
- callback(r->second, key);
- }
- }
-
- changed.clear();
-}
-
-void md_config_t::call_all_observers()
-{
- rev_obs_map_t rev_obs;
- {
- Mutex::Locker l(lock);
-
- expand_all_meta();
-
- for (auto r = observers.begin(); r != observers.end(); ++r) {
- map_observer_changes(r->second, r->first, &rev_obs);
- }
- }
-
- call_observers(rev_obs);
-}
-
-int md_config_t::injectargs(const std::string& s, std::ostream *oss)
+int md_config_t::injectargs(ConfigValues& values,
+ const ConfigTracker& tracker,
+ const std::string& s, std::ostream *oss)
{
int ret;
- rev_obs_map_t rev_obs;
- {
- Mutex::Locker l(lock);
-
- char b[s.length()+1];
- strcpy(b, s.c_str());
- std::vector<const char*> nargs;
- char *p = b;
- while (*p) {
- nargs.push_back(p);
- while (*p && *p != ' ') p++;
- if (!*p)
- break;
- *p++ = 0;
- while (*p && *p == ' ') p++;
- }
- ret = parse_injectargs(nargs, oss);
- if (!nargs.empty()) {
- *oss << " failed to parse arguments: ";
- std::string prefix;
- for (std::vector<const char*>::const_iterator i = nargs.begin();
- i != nargs.end(); ++i) {
- *oss << prefix << *i;
- prefix = ",";
- }
- *oss << "\n";
- ret = -EINVAL;
+ char b[s.length()+1];
+ strcpy(b, s.c_str());
+ std::vector<const char*> nargs;
+ char *p = b;
+ while (*p) {
+ nargs.push_back(p);
+ while (*p && *p != ' ') p++;
+ if (!*p)
+ break;
+ *p++ = 0;
+ while (*p && *p == ' ') p++;
+ }
+ ret = parse_injectargs(values, tracker, nargs, oss);
+ if (!nargs.empty()) {
+ *oss << " failed to parse arguments: ";
+ std::string prefix;
+ for (std::vector<const char*>::const_iterator i = nargs.begin();
+ i != nargs.end(); ++i) {
+ *oss << prefix << *i;
+ prefix = ",";
}
-
- for_each_change(
- oss, [this, &rev_obs](md_config_obs_t *obs, const std::string &key) {
- map_observer_changes(obs, key, &rev_obs);
- });
+ *oss << "\n";
+ ret = -EINVAL;
}
-
- call_observers(rev_obs);
+ update_legacy_vals(values);
return ret;
}
-void md_config_t::set_val_or_die(const std::string &key,
- const std::string &val,
- bool meta)
+void md_config_t::set_val_or_die(ConfigValues& values,
+ const ConfigTracker& tracker,
+ const std::string &key,
+ const std::string &val)
{
std::stringstream err;
- int ret = set_val(key, val, meta, &err);
+ int ret = set_val(values, tracker, key, val, &err);
if (ret != 0) {
std::cerr << "set_val_or_die(" << key << "): " << err.str();
}
- assert(ret == 0);
+ ceph_assert(ret == 0);
}
-int md_config_t::set_val(const std::string &key, const char *val,
- bool meta, std::stringstream *err_ss)
+int md_config_t::set_val(ConfigValues& values,
+ const ConfigTracker& tracker,
+ const std::string &key, const char *val,
+ std::stringstream *err_ss)
{
- Mutex::Locker l(lock);
if (key.empty()) {
if (err_ss) *err_ss << "No key specified";
return -EINVAL;
}
std::string v(val);
- if (meta)
- expand_meta(v, &std::cerr);
string k(ConfFile::normalize_key_name(key));
- // subsystems?
- if (strncmp(k.c_str(), "debug_", 6) == 0) {
- for (size_t o = 0; o < subsys.get_num(); o++) {
- std::string as_option = "debug_" + subsys.get_name(o);
- if (k == as_option) {
- int log, gather;
- int r = sscanf(v.c_str(), "%d/%d", &log, &gather);
- if (r >= 1) {
- if (r < 2) {
- gather = log;
- }
- subsys.set_log_level(o, log);
- subsys.set_gather_level(o, gather);
- if (err_ss) *err_ss << "Set " << k << " to " << log << "/" << gather;
- return 0;
- }
- if (err_ss) {
- *err_ss << "Invalid debug level, should be <int> or <int>/<int>";
- }
- return -EINVAL;
- }
- }
- }
-
const auto &opt_iter = schema.find(k);
if (opt_iter != schema.end()) {
const Option &opt = opt_iter->second;
- if ((!opt.is_safe()) && internal_safe_to_start_threads) {
- // If threads have been started and the option is not thread safe
- if (observers.find(opt.name) == observers.end()) {
- // And there is no observer to safely change it...
- // You lose.
- if (err_ss) *err_ss << "Configuration option '" << key << "' may "
- "not be modified at runtime";
- return -ENOSYS;
- }
- }
-
std::string error_message;
- int r = set_val_impl(v, opt, &error_message);
- if (r == 0) {
+ int r = _set_val(values, tracker, v, opt, CONF_OVERRIDE, &error_message);
+ if (r >= 0) {
if (err_ss) *err_ss << "Set " << opt.name << " to " << v;
+ r = 0;
} else {
if (err_ss) *err_ss << error_message;
}
return -ENOENT;
}
+int md_config_t::rm_val(ConfigValues& values, const std::string& key)
+{
+ return _rm_val(values, key, CONF_OVERRIDE);
+}
+
+void md_config_t::get_defaults_bl(const ConfigValues& values,
+ bufferlist *bl)
+{
+ if (defaults_bl.length() == 0) {
+ uint32_t n = 0;
+ bufferlist bl;
+ for (const auto &i : schema) {
+ ++n;
+ encode(i.second.name, bl);
+ auto [value, found] = values.get_value(i.second.name, CONF_DEFAULT);
+ if (found) {
+ encode(Option::to_str(value), bl);
+ } else {
+ string val;
+ conf_stringify(_get_val_default(i.second), &val);
+ encode(val, bl);
+ }
+ }
+ encode(n, defaults_bl);
+ defaults_bl.claim_append(bl);
+ }
+ *bl = defaults_bl;
+}
+
+void md_config_t::get_config_bl(
+ const ConfigValues& values,
+ uint64_t have_version,
+ bufferlist *bl,
+ uint64_t *got_version)
+{
+ if (values_bl.length() == 0) {
+ uint32_t n = 0;
+ bufferlist bl;
+ values.for_each([&](auto& name, auto& configs) {
+ if (name == "fsid" ||
+ name == "host") {
+ return;
+ }
+ ++n;
+ encode(name, bl);
+ encode((uint32_t)configs.size(), bl);
+ for (auto& j : configs) {
+ encode(j.first, bl);
+ encode(Option::to_str(j.second), bl);
+ }
+ });
+ // make sure overridden items appear, and include the default value
+ for (auto& i : ignored_mon_values) {
+ if (values.contains(i.first)) {
+ continue;
+ }
+ if (i.first == "fsid" ||
+ i.first == "host") {
+ continue;
+ }
+ const Option *opt = find_option(i.first);
+ if (!opt) {
+ continue;
+ }
+ ++n;
+ encode(i.first, bl);
+ encode((uint32_t)1, bl);
+ encode((int32_t)CONF_DEFAULT, bl);
+ string val;
+ conf_stringify(_get_val_default(*opt), &val);
+ encode(val, bl);
+ }
+ encode(n, values_bl);
+ values_bl.claim_append(bl);
+ encode(ignored_mon_values, values_bl);
+ ++values_bl_version;
+ }
+ if (have_version != values_bl_version) {
+ *bl = values_bl;
+ *got_version = values_bl_version;
+ }
+}
-int md_config_t::get_val(const std::string &key, char **buf, int len) const
+int md_config_t::get_val(const ConfigValues& values,
+ const std::string &key, char **buf, int len) const
{
- Mutex::Locker l(lock);
- return _get_val(key, buf,len);
+ string k(ConfFile::normalize_key_name(key));
+ return _get_val_cstr(values, k, buf, len);
}
-Option::value_t md_config_t::get_val_generic(const std::string &key) const
+int md_config_t::get_val(
+ const ConfigValues& values,
+ const std::string &key,
+ std::string *val) const
{
- Mutex::Locker l(lock);
- return _get_val(key);
+ return conf_stringify(get_val_generic(values, key), val);
}
-Option::value_t md_config_t::_get_val(const std::string &key) const
+Option::value_t md_config_t::get_val_generic(
+ const ConfigValues& values,
+ const std::string &key) const
{
- assert(lock.is_locked());
+ string k(ConfFile::normalize_key_name(key));
+ return _get_val(values, k);
+}
+Option::value_t md_config_t::_get_val(
+ const ConfigValues& values,
+ const std::string &key,
+ expand_stack_t *stack,
+ std::ostream *err) const
+{
if (key.empty()) {
return Option::value_t(boost::blank());
}
// In key names, leading and trailing whitespace are not significant.
string k(ConfFile::normalize_key_name(key));
- const auto &opt_iter = schema.find(k);
- if (opt_iter != schema.end()) {
- // Using .at() is safe because all keys in the schema always have
- // entries in ::values
- return values.at(k);
- } else {
+ const Option *o = find_option(key);
+ if (!o) {
+ // not a valid config option
return Option::value_t(boost::blank());
}
+
+ return _get_val(values, *o, stack, err);
}
-int md_config_t::_get_val(const std::string &key, std::string *value) const {
- assert(lock.is_locked());
+Option::value_t md_config_t::_get_val(
+ const ConfigValues& values,
+ const Option& o,
+ expand_stack_t *stack,
+ std::ostream *err) const
+{
+ expand_stack_t a_stack;
+ if (!stack) {
+ stack = &a_stack;
+ }
+ return _expand_meta(values,
+ _get_val_nometa(values, o),
+ &o, stack, err);
+}
- std::string normalized_key(ConfFile::normalize_key_name(key));
- Option::value_t config_value = _get_val(normalized_key.c_str());
- if (!boost::get<boost::blank>(&config_value)) {
- ostringstream oss;
- if (bool *flag = boost::get<bool>(&config_value)) {
- oss << (*flag ? "true" : "false");
- } else if (double *dp = boost::get<double>(&config_value)) {
- oss << std::fixed << *dp;
+Option::value_t md_config_t::_get_val_nometa(const ConfigValues& values,
+ const Option& o) const
+{
+ if (auto [value, found] = values.get_value(o.name, -1); found) {
+ return value;
+ } else {
+ return _get_val_default(o);
+ }
+}
+
+const Option::value_t& md_config_t::_get_val_default(const Option& o) const
+{
+ bool has_daemon_default = !boost::get<boost::blank>(&o.daemon_value);
+ if (is_daemon && has_daemon_default) {
+ return o.daemon_value;
+ } else {
+ return o.value;
+ }
+}
+
+void md_config_t::early_expand_meta(
+ const ConfigValues& values,
+ std::string &val,
+ std::ostream *err) const
+{
+ expand_stack_t stack;
+ Option::value_t v = _expand_meta(values,
+ Option::value_t(val),
+ nullptr, &stack, err);
+ conf_stringify(v, &val);
+}
+
+bool md_config_t::finalize_reexpand_meta(ConfigValues& values,
+ const ConfigTracker& tracker)
+{
+ for (auto& [name, value] : may_reexpand_meta) {
+ set_val(values, tracker, name, value);
+ }
+
+ if (!may_reexpand_meta.empty()) {
+ // meta expands could have modified anything. Copy it all out again.
+ update_legacy_vals(values);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+Option::value_t md_config_t::_expand_meta(
+ const ConfigValues& values,
+ const Option::value_t& in,
+ const Option *o,
+ expand_stack_t *stack,
+ std::ostream *err) const
+{
+ //cout << __func__ << " in '" << in << "' stack " << stack << std::endl;
+ if (!stack) {
+ return in;
+ }
+ const std::string *str = boost::get<const std::string>(&in);
+ if (!str) {
+ // strings only!
+ return in;
+ }
+
+ auto pos = str->find('$');
+ if (pos == std::string::npos) {
+ // no substitutions!
+ return in;
+ }
+
+ if (o) {
+ stack->push_back(make_pair(o, &in));
+ }
+ string out;
+ decltype(pos) last_pos = 0;
+ while (pos != std::string::npos) {
+ ceph_assert((*str)[pos] == '$');
+ if (pos > last_pos) {
+ out += str->substr(last_pos, pos - last_pos);
+ }
+
+ // try to parse the variable name into var, either \$\{(.+)\} or
+ // \$[a-z\_]+
+ const char *valid_chars = "abcdefghijklmnopqrstuvwxyz_";
+ string var;
+ size_t endpos = 0;
+ if ((*str)[pos+1] == '{') {
+ // ...${foo_bar}...
+ endpos = str->find_first_not_of(valid_chars, pos + 2);
+ if (endpos != std::string::npos &&
+ (*str)[endpos] == '}') {
+ var = str->substr(pos + 2, endpos - pos - 2);
+ endpos++;
+ }
} else {
- oss << config_value;
+ // ...$foo...
+ endpos = str->find_first_not_of(valid_chars, pos + 1);
+ if (endpos != std::string::npos)
+ var = str->substr(pos + 1, endpos - pos - 1);
+ else
+ var = str->substr(pos + 1);
}
- *value = oss.str();
- return 0;
+ last_pos = endpos;
+
+ if (!var.size()) {
+ out += '$';
+ } else {
+ //cout << " found var " << var << std::endl;
+ // special metavariable?
+ if (var == "type") {
+ out += values.name.get_type_name();
+ } else if (var == "cluster") {
+ out += values.cluster;
+ } else if (var == "name") {
+ out += values.name.to_cstr();
+ } else if (var == "host") {
+ if (values.host == "") {
+ out += ceph_get_short_hostname();
+ } else {
+ out += values.host;
+ }
+ } else if (var == "num") {
+ out += values.name.get_id().c_str();
+ } else if (var == "id") {
+ out += values.name.get_id();
+ } else if (var == "pid") {
+ out += stringify(getpid());
+ if (o) {
+ may_reexpand_meta[o->name] = *str;
+ }
+ } else if (var == "cctid") {
+ out += stringify((unsigned long long)this);
+ } else if (var == "home") {
+ const char *home = getenv("HOME");
+ out = home ? std::string(home) : std::string();
+ } else {
+ if (var == "data_dir") {
+ var = data_dir_option;
+ }
+ const Option *o = find_option(var);
+ if (!o) {
+ out += str->substr(pos, endpos - pos);
+ } else {
+ auto match = std::find_if(
+ stack->begin(), stack->end(),
+ [o](pair<const Option *,const Option::value_t*>& item) {
+ return item.first == o;
+ });
+ if (match != stack->end()) {
+ // substitution loop; break the cycle
+ if (err) {
+ *err << "variable expansion loop at " << var << "="
+ << Option::to_str(*match->second) << "\n"
+ << "expansion stack:\n";
+ for (auto i = stack->rbegin(); i != stack->rend(); ++i) {
+ *err << i->first->name << "="
+ << Option::to_str(*i->second) << "\n";
+ }
+ }
+ return Option::value_t(std::string("$") + o->name);
+ } else {
+ // recursively evaluate!
+ string n;
+ conf_stringify(_get_val(values, *o, stack, err), &n);
+ out += n;
+ }
+ }
+ }
+ }
+ pos = str->find('$', last_pos);
}
- return -ENOENT;
+ if (last_pos != std::string::npos) {
+ out += str->substr(last_pos);
+ }
+ if (o) {
+ stack->pop_back();
+ }
+
+ return Option::value_t(out);
}
-int md_config_t::_get_val(const std::string &key, char **buf, int len) const
+int md_config_t::_get_val_cstr(
+ const ConfigValues& values,
+ const std::string &key, char **buf, int len) const
{
- assert(lock.is_locked());
-
if (key.empty())
return -EINVAL;
- string val ;
- if (_get_val(key, &val) == 0) {
+ string val;
+ if (conf_stringify(_get_val(values, key), &val) == 0) {
int l = val.length() + 1;
if (len == -1) {
*buf = (char*)malloc(l);
}
string k(ConfFile::normalize_key_name(key));
- // subsys?
- for (size_t o = 0; o < subsys.get_num(); o++) {
- std::string as_option = "debug_" + subsys.get_name(o);
- if (k == as_option) {
- if (len == -1) {
- *buf = (char*)malloc(20);
- len = 20;
- }
- int l = snprintf(*buf, len, "%d/%d", subsys.get_log_level(o), subsys.get_gather_level(o));
- return (l == len) ? -ENAMETOOLONG : 0;
- }
- }
// couldn't find a configuration option with key 'k'
return -ENOENT;
keys->push_back(negative_flag_prefix + opt.name);
}
}
- for (size_t i = 0; i < subsys.get_num(); ++i) {
- keys->push_back("debug_" + subsys.get_name(i));
- }
}
/* The order of the sections here is important. The first section in the
* looking. The lowest priority section is the one we look in only if all
* others had nothing. This should always be the global section.
*/
-void md_config_t::get_my_sections(std::vector <std::string> §ions) const
+void md_config_t::get_my_sections(const ConfigValues& values,
+ std::vector <std::string> §ions) const
{
- Mutex::Locker l(lock);
- _get_my_sections(sections);
+ _get_my_sections(values, sections);
}
-void md_config_t::_get_my_sections(std::vector <std::string> §ions) const
+void md_config_t::_get_my_sections(const ConfigValues& values,
+ std::vector <std::string> §ions) const
{
- assert(lock.is_locked());
- sections.push_back(name.to_str());
+ sections.push_back(values.name.to_str());
- sections.push_back(name.get_type_name());
+ sections.push_back(values.name.get_type_name());
sections.push_back("global");
}
// Return a list of all sections
int md_config_t::get_all_sections(std::vector <std::string> §ions) const
{
- Mutex::Locker l(lock);
for (ConfFile::const_section_iter_t s = cf.sections_begin();
s != cf.sections_end(); ++s) {
sections.push_back(s->first);
return 0;
}
-int md_config_t::get_val_from_conf_file(const std::vector <std::string> §ions,
- const std::string &key, std::string &out, bool emeta) const
+int md_config_t::get_val_from_conf_file(
+ const ConfigValues& values,
+ const std::vector <std::string> §ions,
+ const std::string &key,
+ std::string &out,
+ bool emeta) const
{
- Mutex::Locker l(lock);
- return _get_val_from_conf_file(sections, key, out, emeta);
+ int r = _get_val_from_conf_file(sections, key, out);
+ if (r < 0) {
+ return r;
+ }
+ if (emeta) {
+ expand_stack_t stack;
+ auto v = _expand_meta(values, Option::value_t(out), nullptr, &stack, nullptr);
+ conf_stringify(v, &out);
+ }
+ return 0;
}
-int md_config_t::_get_val_from_conf_file(const std::vector <std::string> §ions,
- const std::string &key, std::string &out, bool emeta) const
+int md_config_t::_get_val_from_conf_file(
+ const std::vector <std::string> §ions,
+ const std::string &key,
+ std::string &out) const
{
- assert(lock.is_locked());
std::vector <std::string>::const_iterator s = sections.begin();
std::vector <std::string>::const_iterator s_end = sections.end();
for (; s != s_end; ++s) {
int ret = cf.read(s->c_str(), key, out);
if (ret == 0) {
- if (emeta)
- expand_meta(out, &std::cerr);
return 0;
- }
- else if (ret != -ENOENT)
+ } else if (ret != -ENOENT) {
return ret;
+ }
}
return -ENOENT;
}
-int md_config_t::set_val_impl(const std::string &raw_val, const Option &opt,
- std::string *error_message)
+int md_config_t::_set_val(
+ ConfigValues& values,
+ const ConfigTracker& observers,
+ const std::string &raw_val,
+ const Option &opt,
+ int level,
+ std::string *error_message)
{
- assert(lock.is_locked());
-
- std::string val = raw_val;
-
- int r = opt.pre_validate(&val, error_message);
- if (r != 0) {
+ Option::value_t new_value;
+ int r = opt.parse_value(raw_val, &new_value, error_message);
+ if (r < 0) {
return r;
}
- Option::value_t new_value;
- if (opt.type == Option::TYPE_INT) {
- int64_t f = strict_si_cast<int64_t>(val.c_str(), error_message);
- if (!error_message->empty()) {
- return -EINVAL;
- }
- new_value = f;
- } else if (opt.type == Option::TYPE_UINT) {
- uint64_t f = strict_si_cast<uint64_t>(val.c_str(), error_message);
- if (!error_message->empty()) {
- return -EINVAL;
- }
- new_value = f;
- } else if (opt.type == Option::TYPE_STR) {
- new_value = val;
- } else if (opt.type == Option::TYPE_FLOAT) {
- double f = strict_strtod(val.c_str(), error_message);
- if (!error_message->empty()) {
- return -EINVAL;
- } else {
- new_value = f;
- }
- } else if (opt.type == Option::TYPE_BOOL) {
- if (strcasecmp(val.c_str(), "false") == 0) {
- new_value = false;
- } else if (strcasecmp(val.c_str(), "true") == 0) {
- new_value = true;
- } else {
- int b = strict_strtol(val.c_str(), 10, error_message);
- if (!error_message->empty()) {
- return -EINVAL;
- }
- new_value = !!b;
- }
- } else if (opt.type == Option::TYPE_ADDR) {
- entity_addr_t addr;
- if (!addr.parse(val.c_str())){
- return -EINVAL;
+ // unsafe runtime change?
+ if (!opt.can_update_at_runtime() &&
+ safe_to_start_threads &&
+ !observers.is_tracking(opt.name)) {
+ // accept value if it is not actually a change
+ if (new_value != _get_val_nometa(values, opt)) {
+ *error_message = string("Configuration option '") + opt.name +
+ "' may not be modified at runtime";
+ return -ENOSYS;
}
- new_value = addr;
- } else if (opt.type == Option::TYPE_UUID) {
- uuid_d uuid;
- if (!uuid.parse(val.c_str())) {
- return -EINVAL;
- }
- new_value = uuid;
- } else {
- ceph_abort();
- }
-
- r = opt.validate(new_value, error_message);
- if (r != 0) {
- return r;
}
-
// Apply the value to its entry in the `values` map
- values[opt.name] = new_value;
+ auto result = values.set_value(opt.name, std::move(new_value), level);
+ switch (result) {
+ case ConfigValues::SET_NO_CHANGE:
+ break;
+ case ConfigValues::SET_NO_EFFECT:
+ values_bl.clear();
+ break;
+ case ConfigValues::SET_HAVE_EFFECT:
+ values_bl.clear();
+ _refresh(values, opt);
+ break;
+ }
+ return result;
+}
+void md_config_t::_refresh(ConfigValues& values, const Option& opt)
+{
// Apply the value to its legacy field, if it has one
auto legacy_ptr_iter = legacy_values.find(std::string(opt.name));
if (legacy_ptr_iter != legacy_values.end()) {
- update_legacy_val(opt, legacy_ptr_iter->second);
+ update_legacy_val(values, opt, legacy_ptr_iter->second);
+ }
+ // Was this a debug_* option update?
+ if (opt.subsys >= 0) {
+ string actual_val;
+ conf_stringify(_get_val(values, opt), &actual_val);
+ values.set_logging(opt.subsys, actual_val.c_str());
+ } else {
+ // normal option, advertise the change.
+ values.changed.insert(opt.name);
}
+}
- changed.insert(opt.name);
+int md_config_t::_rm_val(ConfigValues& values,
+ const std::string& key,
+ int level)
+{
+ if (schema.count(key) == 0) {
+ return -EINVAL;
+ }
+ auto ret = values.rm_val(key, level);
+ if (ret < 0) {
+ return ret;
+ }
+ if (ret == ConfigValues::SET_HAVE_EFFECT) {
+ _refresh(values, *find_option(key));
+ }
+ values_bl.clear();
return 0;
}
+namespace {
+template<typename Size>
+struct get_size_visitor : public boost::static_visitor<Size>
+{
+ get_size_visitor() {}
+
+ template<typename T>
+ Size operator()(const T&) const {
+ return -1;
+ }
+ Size operator()(const Option::size_t& sz) const {
+ return static_cast<Size>(sz.value);
+ }
+ Size operator()(const Size& v) const {
+ return v;
+ }
+};
+
/**
* Handles assigning from a variant-of-types to a variant-of-pointers-to-types
*/
+template<class Config>
class assign_visitor : public boost::static_visitor<>
{
- md_config_t *conf;
+ Config *conf;
Option::value_t val;
public:
- assign_visitor(md_config_t *conf_, Option::value_t val_)
+ assign_visitor(Config *conf_, Option::value_t val_)
: conf(conf_), val(val_)
{}
template <typename T>
- void operator()( T md_config_t::* ptr) const
+ void operator()(T Config::* ptr) const
{
- T *member = const_cast<T *>(&(conf->*(boost::get<const T md_config_t::*>(ptr))));
+ T *member = const_cast<T *>(&(conf->*(boost::get<const T Config::*>(ptr))));
*member = boost::get<T>(val);
}
-};
-
-void md_config_t::update_legacy_val(const Option &opt,
- md_config_t::member_ptr_t member_ptr)
-{
- if (boost::get<boost::blank>(&values.at(opt.name))) {
- // This shouldn't happen, but if it does then just don't even
- // try to assign to the legacy field.
- return;
+ void operator()(uint64_t Config::* ptr) const
+ {
+ using T = uint64_t;
+ auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr))));
+ *member = boost::apply_visitor(get_size_visitor<T>{}, val);
+ }
+ void operator()(int64_t Config::* ptr) const
+ {
+ using T = int64_t;
+ auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr))));
+ *member = boost::apply_visitor(get_size_visitor<T>{}, val);
}
-
- boost::apply_visitor(assign_visitor(this, values.at(opt.name)), member_ptr);
-}
-
-
-static const char *CONF_METAVARIABLES[] = {
- "data_dir", // put this first: it may contain some of the others
- "cluster", "type", "name", "host", "num", "id", "pid", "cctid"
};
-static const int NUM_CONF_METAVARIABLES =
- (sizeof(CONF_METAVARIABLES) / sizeof(CONF_METAVARIABLES[0]));
+} // anonymous namespace
-void md_config_t::expand_all_meta()
+void md_config_t::update_legacy_vals(ConfigValues& values)
{
- // Expand all metavariables
- ostringstream oss;
- for (const auto &i : schema) {
- const Option &opt = i.second;
-
- if (opt.type == Option::TYPE_STR) {
- list<const Option*> stack;
- std::string *str = boost::get<std::string>(&(values.at(opt.name)));
- assert(str != nullptr); // Non-string values should never get in
- expand_meta(*str, &opt, stack, &oss);
- }
+ for (const auto &i : legacy_values) {
+ const auto &name = i.first;
+ const auto &option = schema.at(name);
+ auto ptr = i.second;
+ update_legacy_val(values, option, ptr);
}
- cerr << oss.str();
}
-bool md_config_t::expand_meta(std::string &val,
- std::ostream *oss) const
+void md_config_t::update_legacy_val(ConfigValues& values,
+ const Option &opt,
+ md_config_t::member_ptr_t member_ptr)
{
- list<const Option*> stack;
- return expand_meta(val, NULL, stack, oss);
+ Option::value_t v = _get_val(values, opt);
+ boost::apply_visitor(assign_visitor(&values, v), member_ptr);
}
-bool md_config_t::expand_meta(std::string &origval,
- const Option *opt,
- std::list<const Option *> stack,
- std::ostream *oss) const
+static void dump(Formatter *f, int level, Option::value_t in)
{
- assert(lock.is_locked());
-
- // no $ means no variable expansion is necessary
- if (origval.find("$") == string::npos)
- return false;
-
- // ignore an expansion loop and create a human readable
- // message about it
- if (opt) {
- for (const auto stack_ptr : stack) {
- if (opt->name == stack_ptr->name) {
- *oss << "variable expansion loop at "
- << opt->name << "=" << origval << std::endl;
- *oss << "expansion stack: " << std::endl;
- for (const auto j : stack) {
- std::string val;
- _get_val(j->name, &val);
- *oss << j->name << "=" << val << std::endl;
- }
- return false;
- }
- }
-
- stack.push_front(opt);
- }
-
- bool found_meta = false;
- string out;
- string val = origval;
- for (string::size_type s = 0; s < val.size(); ) {
- if (val[s] != '$') {
- out += val[s++];
- continue;
- }
-
- // try to parse the variable name into var, either \$\{(.+)\} or
- // \$[a-z\_]+
- const char *valid_chars = "abcdefghijklmnopqrstuvwxyz_";
- string var;
- size_t endpos = 0;
- if (val[s+1] == '{') {
- // ...${foo_bar}...
- endpos = val.find_first_not_of(valid_chars, s+2);
- if (endpos != std::string::npos &&
- val[endpos] == '}') {
- var = val.substr(s+2, endpos-s-2);
- endpos++;
- }
- } else {
- // ...$foo...
- endpos = val.find_first_not_of(valid_chars, s+1);
- if (endpos != std::string::npos)
- var = val.substr(s+1, endpos-s-1);
- else
- var = val.substr(s+1);
- }
-
- bool expanded = false;
- if (var.length()) {
- // special metavariable?
- for (int i = 0; i < NUM_CONF_METAVARIABLES; ++i) {
- if (var != CONF_METAVARIABLES[i])
- continue;
- //cout << " meta match of " << var << " " << CONF_METAVARIABLES[i] << std::endl;
- if (var == "type")
- out += name.get_type_name();
- else if (var == "cluster")
- out += cluster;
- else if (var == "name")
- out += name.to_cstr();
- else if (var == "host")
- {
- if (host == "")
- out += ceph_get_short_hostname();
- else
- out += host;
- }
- else if (var == "num")
- out += name.get_id().c_str();
- else if (var == "id")
- out += name.get_id().c_str();
- else if (var == "pid")
- out += stringify(getpid());
- else if (var == "cctid")
- out += stringify((unsigned long long)this);
- else if (var == "data_dir") {
- if (data_dir_option.length()) {
- char *vv = NULL;
- _get_val(data_dir_option.c_str(), &vv, -1);
- string tmp = vv;
- free(vv);
- expand_meta(tmp, NULL, stack, oss);
- out += tmp;
- } else {
- // this isn't really right, but it'll result in a mangled
- // non-existent path that will fail any search list
- out += "$data_dir";
- }
- } else
- ceph_abort(); // unreachable
- expanded = true;
- }
-
- if (!expanded) {
- // config option?
- const auto other_opt_iter = schema.find(var);
- if (other_opt_iter != schema.end()) {
- const Option &other_opt = other_opt_iter->second;
- if (other_opt.type == Option::TYPE_STR) {
- // The referenced option is a string, it may need substitution
- // before inserting.
- Option::value_t *other_val_ptr = const_cast<Option::value_t*>(&(values.at(other_opt.name)));
- std::string *other_opt_val = boost::get<std::string>(other_val_ptr);
- expand_meta(*other_opt_val, &other_opt, stack, oss);
- out += *other_opt_val;
- } else {
- // The referenced option is not a string: retrieve and insert
- // its stringized form.
- char *vv = NULL;
- _get_val(other_opt.name, &vv, -1);
- out += vv;
- free(vv);
- }
- expanded = true;
- }
- }
- }
-
- if (expanded) {
- found_meta = true;
- s = endpos;
- } else {
- out += val[s++];
- }
+ if (const bool *v = boost::get<const bool>(&in)) {
+ f->dump_bool(ceph_conf_level_name(level), *v);
+ } else if (const int64_t *v = boost::get<const int64_t>(&in)) {
+ f->dump_int(ceph_conf_level_name(level), *v);
+ } else if (const uint64_t *v = boost::get<const uint64_t>(&in)) {
+ f->dump_unsigned(ceph_conf_level_name(level), *v);
+ } else if (const double *v = boost::get<const double>(&in)) {
+ f->dump_float(ceph_conf_level_name(level), *v);
+ } else {
+ f->dump_stream(ceph_conf_level_name(level)) << Option::to_str(in);
}
- // override the original value with the expanded value
- origval = out;
- return found_meta;
}
void md_config_t::diff(
- const md_config_t *other,
- map<string, pair<string, string> > *diff,
- set<string> *unknown)
-{
- diff_helper(other, diff, unknown);
-}
-void md_config_t::diff(
- const md_config_t *other,
- map<string, pair<string, string> > *diff,
- set<string> *unknown, const string& setting)
-{
- diff_helper(other, diff, unknown, setting);
-}
-
-void md_config_t::diff_helper(
- const md_config_t *other,
- map<string,pair<string,string> > *diff,
- set<string> *unknown, const string& setting)
+ const ConfigValues& values,
+ Formatter *f,
+ string name) const
{
- Mutex::Locker l(lock);
-
- char local_buf[4096];
- char other_buf[4096];
- for (const auto &i : schema) {
- const Option &opt = i.second;
- if (!setting.empty()) {
- if (setting != opt.name) {
- continue;
- }
- }
- memset(local_buf, 0, sizeof(local_buf));
- memset(other_buf, 0, sizeof(other_buf));
-
- char *other_val = other_buf;
- int err = other->get_val(opt.name, &other_val, sizeof(other_buf));
- if (err < 0) {
- if (err == -ENOENT) {
- unknown->insert(opt.name);
- }
- continue;
+ values.for_each([this, f, &values] (auto& name, auto& configs) {
+ if (configs.size() == 1 &&
+ configs.begin()->first == CONF_DEFAULT) {
+ // we only have a default value; exclude from diff
+ return;
}
-
- char *local_val = local_buf;
- err = _get_val(opt.name, &local_val, sizeof(local_buf));
- if (err != 0)
- continue;
-
- if (strcmp(local_val, other_val))
- diff->insert(make_pair(opt.name, make_pair(local_val, other_val)));
- else if (!setting.empty()) {
- diff->insert(make_pair(opt.name, make_pair(local_val, other_val)));
- break;
+ f->open_object_section(name.c_str());
+ const Option *o = find_option(name);
+ dump(f, CONF_DEFAULT, _get_val_default(*o));
+ for (auto& j : configs) {
+ dump(f, j.first, j.second);
}
- }
+ dump(f, CONF_FINAL, _get_val(values, *o));
+ f->close_section();
+ });
}
void md_config_t::complain_about_parse_errors(CephContext *cct)
{
::complain_about_parse_errors(cct, &parse_errors);
}
-
-void md_config_t::call_observers(rev_obs_map_t &rev_obs) {
- for (auto p : rev_obs) {
- p.first->handle_conf_change(this, p.second);
- // this can be done outside the lock as call_gate_enter()
- // and remove_observer() are serialized via lock
- call_gate_leave(p.first);
- }
-}
-
-void md_config_t::map_observer_changes(md_config_obs_t *obs, const std::string &key,
- rev_obs_map_t *rev_obs) {
- ceph_assert(lock.is_locked());
-
- auto p = rev_obs->emplace(obs, std::set<std::string>{});
-
- p.first->second.emplace(key);
- if (p.second) {
- // this needs to be done under lock as once this lock is
- // dropped (before calling observers) a remove_observer()
- // can sneak in and cause havoc.
- call_gate_enter(p.first->first);
- }
-}