]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/config.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / common / config.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
11fdf7f2
TL
15#include <boost/type_traits.hpp>
16
7c673cae
FG
17#include "common/ceph_argparse.h"
18#include "common/common_init.h"
19#include "common/config.h"
11fdf7f2 20#include "common/config_obs.h"
7c673cae 21#include "include/str_list.h"
7c673cae 22#include "include/stringify.h"
7c673cae
FG
23#include "osd/osd_types.h"
24#include "common/errno.h"
25#include "common/hostname.h"
11fdf7f2 26#include "common/dout.h"
7c673cae
FG
27
28/* Don't use standard Ceph logging in this file.
29 * We can't use logging until it's initialized, and a lot of the necessary
30 * initialization happens here.
31 */
32#undef dout
7c673cae
FG
33#undef pdout
34#undef derr
7c673cae 35#undef generic_dout
11fdf7f2
TL
36
37// set set_mon_vals()
38#define dout_subsys ceph_subsys_monc
7c673cae
FG
39
40using std::map;
41using std::list;
7c673cae
FG
42using std::ostringstream;
43using std::pair;
7c673cae
FG
44using std::string;
45
11fdf7f2 46static const char *CEPH_CONF_FILE_DEFAULT = "$data_dir/config, /etc/ceph/$cluster.conf, $home/.ceph/$cluster.conf, $cluster.conf"
7c673cae
FG
47#if defined(__FreeBSD__)
48 ", /usr/local/etc/ceph/$cluster.conf"
49#endif
50 ;
51
52#define _STR(x) #x
53#define STRINGIFY(x) _STR(x)
54
11fdf7f2
TL
55const char *ceph_conf_level_name(int level)
56{
57 switch (level) {
58 case CONF_DEFAULT: return "default"; // built-in default
59 case CONF_MON: return "mon"; // monitor config database
60 case CONF_ENV: return "env"; // process environment (CEPH_ARGS)
61 case CONF_FILE: return "file"; // ceph.conf file
62 case CONF_CMDLINE: return "cmdline"; // process command line args
63 case CONF_OVERRIDE: return "override"; // injectargs or 'config set' at runtime
64 case CONF_FINAL: return "final";
65 default: return "???";
66 }
67}
68
7c673cae
FG
69int ceph_resolve_file_search(const std::string& filename_list,
70 std::string& result)
71{
72 list<string> ls;
73 get_str_list(filename_list, ls);
74
75 int ret = -ENOENT;
76 list<string>::iterator iter;
77 for (iter = ls.begin(); iter != ls.end(); ++iter) {
91327a77 78 int fd = ::open(iter->c_str(), O_RDONLY|O_CLOEXEC);
7c673cae
FG
79 if (fd < 0) {
80 ret = -errno;
81 continue;
82 }
83 close(fd);
84 result = *iter;
85 return 0;
86 }
87
88 return ret;
89}
90
11fdf7f2 91static int conf_stringify(const Option::value_t& v, string *out)
c07f9fc5 92{
11fdf7f2
TL
93 if (boost::get<boost::blank>(&v)) {
94 return -ENOENT;
95 }
96 *out = Option::to_str(v);
97 return 0;
98}
c07f9fc5 99
11fdf7f2
TL
100md_config_t::md_config_t(ConfigValues& values,
101 const ConfigTracker& tracker,
102 bool is_daemon)
103 : is_daemon(is_daemon)
104{
c07f9fc5
FG
105 // Load the compile-time list of Option into
106 // a map so that we can resolve keys quickly.
107 for (const auto &i : ceph_options) {
108 if (schema.count(i.name)) {
109 // We may be instantiated pre-logging so send
110 std::cerr << "Duplicate config key in schema: '" << i.name << "'"
111 << std::endl;
11fdf7f2 112 ceph_abort();
c07f9fc5 113 }
11fdf7f2
TL
114 schema.emplace(std::piecewise_construct,
115 std::forward_as_tuple(i.name),
116 std::forward_as_tuple(i));
117 }
118
119 // Define the debug_* options as well.
120 subsys_options.reserve(values.subsys.get_num());
121 for (unsigned i = 0; i < values.subsys.get_num(); ++i) {
122 string name = string("debug_") + values.subsys.get_name(i);
123 subsys_options.push_back(
124 Option(name, Option::TYPE_STR, Option::LEVEL_ADVANCED));
125 Option& opt = subsys_options.back();
126 opt.set_default(stringify(values.subsys.get_log_level(i)) + "/" +
127 stringify(values.subsys.get_gather_level(i)));
128 string desc = string("Debug level for ") + values.subsys.get_name(i);
129 opt.set_description(desc.c_str());
130 opt.set_flag(Option::FLAG_RUNTIME);
131 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.");
132 opt.set_subsys(i);
133 opt.set_validator([](std::string *value, std::string *error_message) {
134 int m, n;
135 int r = sscanf(value->c_str(), "%d/%d", &m, &n);
136 if (r >= 1) {
137 if (m < 0 || m > 99) {
138 *error_message = "value must be in range [0, 99]";
139 return -ERANGE;
140 }
141 if (r == 2) {
142 if (n < 0 || n > 99) {
143 *error_message = "value must be in range [0, 99]";
144 return -ERANGE;
145 }
146 } else {
147 // normalize to M/N
148 n = m;
149 *value = stringify(m) + "/" + stringify(n);
150 }
151 } else {
152 *error_message = "value must take the form N or N/M, where N and M are integers";
153 return -EINVAL;
154 }
155 return 0;
156 });
157 }
158 for (auto& opt : subsys_options) {
159 schema.emplace(opt.name, opt);
c07f9fc5
FG
160 }
161
162 // Populate list of legacy_values according to the OPTION() definitions
163 // Note that this is just setting up our map of name->member ptr. The
164 // default values etc will get loaded in along with new-style data,
165 // as all loads write to both the values map, and the legacy
166 // members if present.
167 legacy_values = {
168#define OPTION(name, type) \
11fdf7f2 169 {std::string(STRINGIFY(name)), &ConfigValues::name},
c07f9fc5
FG
170#define SAFE_OPTION(name, type) OPTION(name, type)
171#include "common/legacy_config_opts.h"
7c673cae 172#undef OPTION
7c673cae 173#undef SAFE_OPTION
c07f9fc5 174 };
7c673cae 175
c07f9fc5 176 validate_schema();
7c673cae 177
11fdf7f2 178 // Validate default values from the schema
c07f9fc5
FG
179 for (const auto &i : schema) {
180 const Option &opt = i.second;
c07f9fc5 181 if (opt.type == Option::TYPE_STR) {
11fdf7f2
TL
182 bool has_daemon_default = !boost::get<boost::blank>(&opt.daemon_value);
183 Option::value_t default_val;
184 if (is_daemon && has_daemon_default) {
185 default_val = opt.daemon_value;
186 } else {
187 default_val = opt.value;
188 }
c07f9fc5
FG
189 // We call pre_validate as a sanity check, but also to get any
190 // side effect (value modification) from the validator.
191 std::string *def_str = boost::get<std::string>(&default_val);
11fdf7f2 192 std::string val = *def_str;
c07f9fc5 193 std::string err;
11fdf7f2 194 if (opt.pre_validate(&val, &err) != 0) {
c07f9fc5
FG
195 std::cerr << "Default value " << opt.name << "=" << *def_str << " is "
196 "invalid: " << err << std::endl;
197
198 // This is the compiled-in default that is failing its own option's
199 // validation, so this is super-invalid and should never make it
200 // past a pull request: crash out.
11fdf7f2
TL
201 ceph_abort();
202 }
203 if (val != *def_str) {
204 // if the validator normalizes the string into a different form than
205 // what was compiled in, use that.
206 set_val_default(values, tracker, opt.name, val);
c07f9fc5
FG
207 }
208 }
c07f9fc5 209 }
7c673cae 210
c07f9fc5 211 // Copy out values (defaults) into any legacy (C struct member) fields
11fdf7f2
TL
212 update_legacy_vals(values);
213}
7c673cae 214
11fdf7f2
TL
215md_config_t::~md_config_t()
216{
c07f9fc5
FG
217}
218
219/**
220 * Sanity check schema. Assert out on failures, to ensure any bad changes
221 * cannot possibly pass any testing and make it into a release.
222 */
223void md_config_t::validate_schema()
7c673cae 224{
c07f9fc5
FG
225 for (const auto &i : schema) {
226 const auto &opt = i.second;
227 for (const auto &see_also_key : opt.see_also) {
228 if (schema.count(see_also_key) == 0) {
229 std::cerr << "Non-existent see-also key '" << see_also_key
230 << "' on option '" << opt.name << "'" << std::endl;
11fdf7f2 231 ceph_abort();
c07f9fc5
FG
232 }
233 }
234 }
7c673cae 235
c07f9fc5
FG
236 for (const auto &i : legacy_values) {
237 if (schema.count(i.first) == 0) {
238 std::cerr << "Schema is missing legacy field '" << i.first << "'"
239 << std::endl;
11fdf7f2 240 ceph_abort();
c07f9fc5
FG
241 }
242 }
7c673cae
FG
243}
244
11fdf7f2 245const Option *md_config_t::find_option(const string& name) const
7c673cae 246{
11fdf7f2
TL
247 auto p = schema.find(name);
248 if (p != schema.end()) {
249 return &p->second;
250 }
251 return nullptr;
7c673cae
FG
252}
253
11fdf7f2
TL
254void md_config_t::set_val_default(ConfigValues& values,
255 const ConfigTracker& tracker,
256 const string& name, const std::string& val)
7c673cae 257{
11fdf7f2
TL
258 const Option *o = find_option(name);
259 ceph_assert(o);
260 string err;
261 int r = _set_val(values, tracker, val, *o, CONF_DEFAULT, &err);
262 ceph_assert(r >= 0);
7c673cae
FG
263}
264
11fdf7f2
TL
265int md_config_t::set_mon_vals(CephContext *cct,
266 ConfigValues& values,
267 const ConfigTracker& tracker,
268 const map<string,string>& kv,
269 config_callback config_cb)
7c673cae 270{
11fdf7f2 271 ignored_mon_values.clear();
f64942e4 272
11fdf7f2
TL
273 if (!config_cb) {
274 ldout(cct, 4) << __func__ << " no callback set" << dendl;
275 }
f64942e4 276
11fdf7f2
TL
277 for (auto& i : kv) {
278 if (config_cb) {
279 if (config_cb(i.first, i.second)) {
280 ldout(cct, 4) << __func__ << " callback consumed " << i.first << dendl;
281 continue;
282 }
283 ldout(cct, 4) << __func__ << " callback ignored " << i.first << dendl;
7c673cae 284 }
11fdf7f2
TL
285 const Option *o = find_option(i.first);
286 if (!o) {
287 ldout(cct,10) << __func__ << " " << i.first << " = " << i.second
288 << " (unrecognized option)" << dendl;
289 continue;
290 }
291 if (o->has_flag(Option::FLAG_NO_MON_UPDATE)) {
292 ignored_mon_values.emplace(i);
293 continue;
294 }
295 std::string err;
296 int r = _set_val(values, tracker, i.second, *o, CONF_MON, &err);
297 if (r < 0) {
298 lderr(cct) << __func__ << " failed to set " << i.first << " = "
299 << i.second << ": " << err << dendl;
300 ignored_mon_values.emplace(i);
301 } else if (r == ConfigValues::SET_NO_CHANGE ||
302 r == ConfigValues::SET_NO_EFFECT) {
303 ldout(cct,20) << __func__ << " " << i.first << " = " << i.second
304 << " (no change)" << dendl;
305 } else if (r == ConfigValues::SET_HAVE_EFFECT) {
306 ldout(cct,10) << __func__ << " " << i.first << " = " << i.second << dendl;
307 } else {
308 ceph_abort();
7c673cae
FG
309 }
310 }
11fdf7f2
TL
311 values.for_each([&] (auto name, auto configs) {
312 auto config = configs.find(CONF_MON);
313 if (config == configs.end()) {
314 return;
315 }
316 if (kv.find(name) != kv.end()) {
317 return;
318 }
319 ldout(cct,10) << __func__ << " " << name
320 << " cleared (was " << Option::to_str(config->second) << ")"
321 << dendl;
322 values.rm_val(name, CONF_MON);
323 });
324 values_bl.clear();
325 update_legacy_vals(values);
326 return 0;
7c673cae
FG
327}
328
11fdf7f2
TL
329int md_config_t::parse_config_files(ConfigValues& values,
330 const ConfigTracker& tracker,
331 const char *conf_files_str,
7c673cae
FG
332 std::ostream *warnings,
333 int flags)
334{
7c673cae 335
11fdf7f2 336 if (safe_to_start_threads)
7c673cae
FG
337 return -ENOSYS;
338
11fdf7f2 339 if (!values.cluster.size() && !conf_files_str) {
7c673cae
FG
340 /*
341 * set the cluster name to 'ceph' when neither cluster name nor
342 * configuration file are specified.
343 */
11fdf7f2 344 values.cluster = "ceph";
7c673cae
FG
345 }
346
11fdf7f2 347 if (!conf_files_str) {
7c673cae
FG
348 const char *c = getenv("CEPH_CONF");
349 if (c) {
11fdf7f2 350 conf_files_str = c;
7c673cae
FG
351 }
352 else {
353 if (flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE)
354 return 0;
11fdf7f2 355 conf_files_str = CEPH_CONF_FILE_DEFAULT;
7c673cae
FG
356 }
357 }
358
11fdf7f2
TL
359 std::list<std::string> conf_files;
360 get_str_list(conf_files_str, conf_files);
361 auto p = conf_files.begin();
362 while (p != conf_files.end()) {
7c673cae 363 string &s = *p;
11fdf7f2
TL
364 if (s.find("$data_dir") != string::npos &&
365 data_dir_option.empty()) {
366 // useless $data_dir item, skip
367 p = conf_files.erase(p);
7c673cae 368 } else {
11fdf7f2 369 early_expand_meta(values, s, warnings);
7c673cae
FG
370 ++p;
371 }
372 }
7c673cae
FG
373
374 // open new conf
375 list<string>::const_iterator c;
376 for (c = conf_files.begin(); c != conf_files.end(); ++c) {
377 cf.clear();
378 string fn = *c;
11fdf7f2 379
7c673cae
FG
380 int ret = cf.parse_file(fn.c_str(), &parse_errors, warnings);
381 if (ret == 0)
382 break;
383 else if (ret != -ENOENT)
384 return ret;
385 }
386 // it must have been all ENOENTs, that's the only way we got here
387 if (c == conf_files.end())
388 return -ENOENT;
389
11fdf7f2 390 if (values.cluster.size() == 0) {
7c673cae
FG
391 /*
392 * If cluster name is not set yet, use the prefix of the
393 * basename of configuration file as cluster name.
394 */
395 auto start = c->rfind('/') + 1;
396 auto end = c->find(".conf", start);
397 if (end == c->npos) {
398 /*
399 * If the configuration file does not follow $cluster.conf
400 * convention, we do the last try and assign the cluster to
401 * 'ceph'.
402 */
11fdf7f2 403 values.cluster = "ceph";
7c673cae 404 } else {
11fdf7f2 405 values.cluster = c->substr(start, end - start);
7c673cae
FG
406 }
407 }
408
409 std::vector <std::string> my_sections;
11fdf7f2 410 _get_my_sections(values, my_sections);
c07f9fc5
FG
411 for (const auto &i : schema) {
412 const auto &opt = i.second;
7c673cae 413 std::string val;
11fdf7f2 414 int ret = _get_val_from_conf_file(my_sections, opt.name, val);
7c673cae
FG
415 if (ret == 0) {
416 std::string error_message;
11fdf7f2
TL
417 int r = _set_val(values, tracker, val, opt, CONF_FILE, &error_message);
418 if (warnings != nullptr && (r < 0 || !error_message.empty())) {
7c673cae
FG
419 *warnings << "parse error setting '" << opt.name << "' to '" << val
420 << "'";
421 if (!error_message.empty()) {
422 *warnings << " (" << error_message << ")";
423 }
424 *warnings << std::endl;
425 }
426 }
427 }
428
7c673cae
FG
429 // Warn about section names that look like old-style section names
430 std::deque < std::string > old_style_section_names;
431 for (ConfFile::const_section_iter_t s = cf.sections_begin();
432 s != cf.sections_end(); ++s) {
433 const string &str(s->first);
434 if (((str.find("mds") == 0) || (str.find("mon") == 0) ||
435 (str.find("osd") == 0)) && (str.size() > 3) && (str[3] != '.')) {
436 old_style_section_names.push_back(str);
437 }
438 }
439 if (!old_style_section_names.empty()) {
440 ostringstream oss;
441 cerr << "ERROR! old-style section name(s) found: ";
442 string sep;
443 for (std::deque < std::string >::const_iterator os = old_style_section_names.begin();
444 os != old_style_section_names.end(); ++os) {
445 cerr << sep << *os;
446 sep = ", ";
447 }
448 cerr << ". Please use the new style section names that include a period.";
449 }
11fdf7f2
TL
450
451 update_legacy_vals(values);
452
7c673cae
FG
453 return 0;
454}
455
11fdf7f2
TL
456void md_config_t::parse_env(unsigned entity_type,
457 ConfigValues& values,
458 const ConfigTracker& tracker,
459 const char *args_var)
7c673cae 460{
11fdf7f2 461 if (safe_to_start_threads)
7c673cae 462 return;
11fdf7f2
TL
463 if (!args_var) {
464 args_var = "CEPH_ARGS";
465 }
7c673cae 466 if (getenv("CEPH_KEYRING")) {
11fdf7f2
TL
467 _set_val(values, tracker, getenv("CEPH_KEYRING"), *find_option("keyring"),
468 CONF_ENV, nullptr);
469 }
470 if (const char *dir = getenv("CEPH_LIB")) {
471 for (auto name : { "erasure_code_dir", "plugin_dir", "osd_class_dir" }) {
472 std::string err;
473 const Option *o = find_option(name);
474 ceph_assert(o);
475 _set_val(values, tracker, dir, *o, CONF_ENV, &err);
476 }
477 }
478 const char *pod_req = getenv("POD_MEMORY_REQUEST");
479 if (pod_req) {
480 uint64_t v = atoll(pod_req);
481 if (v) {
482 switch (entity_type) {
483 case CEPH_ENTITY_TYPE_OSD:
484 _set_val(values, tracker, stringify(v),
485 *find_option("osd_memory_target"),
486 CONF_ENV, nullptr);
487 break;
488 }
489 }
490 }
491 if (getenv(args_var)) {
492 vector<const char *> env_args;
493 env_to_vec(env_args, args_var);
494 parse_argv(values, tracker, env_args, CONF_ENV);
7c673cae
FG
495 }
496}
497
11fdf7f2
TL
498void md_config_t::show_config(const ConfigValues& values,
499 std::ostream& out) const
7c673cae 500{
11fdf7f2 501 _show_config(values, &out, nullptr);
7c673cae
FG
502}
503
11fdf7f2
TL
504void md_config_t::show_config(const ConfigValues& values,
505 Formatter *f) const
7c673cae 506{
11fdf7f2 507 _show_config(values, nullptr, f);
7c673cae
FG
508}
509
11fdf7f2 510void md_config_t::config_options(Formatter *f) const
1adf2230 511{
1adf2230
AA
512 f->open_array_section("options");
513 for (const auto& i: schema) {
11fdf7f2 514 f->dump_object("option", i.second);
1adf2230
AA
515 }
516 f->close_section();
517}
518
11fdf7f2
TL
519void md_config_t::_show_config(const ConfigValues& values,
520 std::ostream *out, Formatter *f) const
7c673cae
FG
521{
522 if (out) {
11fdf7f2
TL
523 *out << "name = " << values.name << std::endl;
524 *out << "cluster = " << values.cluster << std::endl;
7c673cae
FG
525 }
526 if (f) {
11fdf7f2
TL
527 f->dump_string("name", stringify(values.name));
528 f->dump_string("cluster", values.cluster);
7c673cae 529 }
c07f9fc5
FG
530 for (const auto& i: schema) {
531 const Option &opt = i.second;
11fdf7f2
TL
532 string val;
533 conf_stringify(_get_val(values, opt), &val);
534 if (out) {
535 *out << opt.name << " = " << val << std::endl;
536 }
537 if (f) {
538 f->dump_string(opt.name.c_str(), val);
539 }
7c673cae
FG
540 }
541}
542
11fdf7f2
TL
543int md_config_t::parse_argv(ConfigValues& values,
544 const ConfigTracker& tracker,
545 std::vector<const char*>& args, int level)
7c673cae 546{
11fdf7f2 547 if (safe_to_start_threads) {
7c673cae
FG
548 return -ENOSYS;
549 }
550
7c673cae
FG
551 // In this function, don't change any parts of the configuration directly.
552 // Instead, use set_val to set them. This will allow us to send the proper
553 // observer notifications later.
554 std::string val;
555 for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
556 if (strcmp(*i, "--") == 0) {
557 /* Normally we would use ceph_argparse_double_dash. However, in this
558 * function we *don't* want to remove the double dash, because later
559 * argument parses will still need to see it. */
560 break;
561 }
562 else if (ceph_argparse_flag(args, i, "--show_conf", (char*)NULL)) {
563 cerr << cf << std::endl;
564 _exit(0);
565 }
566 else if (ceph_argparse_flag(args, i, "--show_config", (char*)NULL)) {
11fdf7f2 567 do_show_config = true;
7c673cae
FG
568 }
569 else if (ceph_argparse_witharg(args, i, &val, "--show_config_value", (char*)NULL)) {
11fdf7f2
TL
570 do_show_config_value = val;
571 }
572 else if (ceph_argparse_flag(args, i, "--no-mon-config", (char*)NULL)) {
573 values.no_mon_config = true;
574 }
575 else if (ceph_argparse_flag(args, i, "--log-early", (char*)NULL)) {
576 values.log_early = true;
577 }
578 else if (ceph_argparse_flag(args, i, "--mon-config", (char*)NULL)) {
579 values.no_mon_config = false;
7c673cae
FG
580 }
581 else if (ceph_argparse_flag(args, i, "--foreground", "-f", (char*)NULL)) {
11fdf7f2 582 set_val_or_die(values, tracker, "daemonize", "false");
7c673cae
FG
583 }
584 else if (ceph_argparse_flag(args, i, "-d", (char*)NULL)) {
11fdf7f2
TL
585 set_val_or_die(values, tracker, "daemonize", "false");
586 set_val_or_die(values, tracker, "log_file", "");
587 set_val_or_die(values, tracker, "log_to_stderr", "true");
588 set_val_or_die(values, tracker, "err_to_stderr", "true");
589 set_val_or_die(values, tracker, "log_to_syslog", "false");
7c673cae
FG
590 }
591 // Some stuff that we wanted to give universal single-character options for
592 // Careful: you can burn through the alphabet pretty quickly by adding
593 // to this list.
594 else if (ceph_argparse_witharg(args, i, &val, "--monmap", "-M", (char*)NULL)) {
11fdf7f2 595 set_val_or_die(values, tracker, "monmap", val.c_str());
7c673cae
FG
596 }
597 else if (ceph_argparse_witharg(args, i, &val, "--mon_host", "-m", (char*)NULL)) {
11fdf7f2 598 set_val_or_die(values, tracker, "mon_host", val.c_str());
7c673cae
FG
599 }
600 else if (ceph_argparse_witharg(args, i, &val, "--bind", (char*)NULL)) {
11fdf7f2 601 set_val_or_die(values, tracker, "public_addr", val.c_str());
7c673cae
FG
602 }
603 else if (ceph_argparse_witharg(args, i, &val, "--keyfile", "-K", (char*)NULL)) {
11fdf7f2
TL
604 bufferlist bl;
605 string err;
606 int r;
607 if (val == "-") {
608 r = bl.read_fd(STDIN_FILENO, 1024);
609 } else {
610 r = bl.read_file(val.c_str(), &err);
611 }
612 if (r >= 0) {
613 string k(bl.c_str(), bl.length());
614 set_val_or_die(values, tracker, "key", k.c_str());
615 }
7c673cae
FG
616 }
617 else if (ceph_argparse_witharg(args, i, &val, "--keyring", "-k", (char*)NULL)) {
11fdf7f2 618 set_val_or_die(values, tracker, "keyring", val.c_str());
7c673cae
FG
619 }
620 else if (ceph_argparse_witharg(args, i, &val, "--client_mountpoint", "-r", (char*)NULL)) {
11fdf7f2 621 set_val_or_die(values, tracker, "client_mountpoint", val.c_str());
7c673cae
FG
622 }
623 else {
11fdf7f2 624 int r = parse_option(values, tracker, args, i, NULL, level);
3efd9988
FG
625 if (r < 0) {
626 return r;
627 }
7c673cae
FG
628 }
629 }
11fdf7f2
TL
630 // meta expands could have modified anything. Copy it all out again.
631 update_legacy_vals(values);
632 return 0;
633}
634
635void md_config_t::do_argv_commands(const ConfigValues& values) const
636{
7c673cae 637
11fdf7f2
TL
638 if (do_show_config) {
639 _show_config(values, &cout, NULL);
7c673cae
FG
640 _exit(0);
641 }
642
11fdf7f2
TL
643 if (do_show_config_value.size()) {
644 string val;
645 int r = conf_stringify(_get_val(values, do_show_config_value, 0, &cerr),
646 &val);
7c673cae
FG
647 if (r < 0) {
648 if (r == -ENOENT)
11fdf7f2
TL
649 std::cerr << "failed to get config option '"
650 << do_show_config_value << "': option not found" << std::endl;
7c673cae 651 else
11fdf7f2
TL
652 std::cerr << "failed to get config option '"
653 << do_show_config_value << "': " << cpp_strerror(r)
654 << std::endl;
7c673cae
FG
655 _exit(1);
656 }
11fdf7f2 657 std::cout << val << std::endl;
7c673cae
FG
658 _exit(0);
659 }
7c673cae
FG
660}
661
11fdf7f2
TL
662int md_config_t::parse_option(ConfigValues& values,
663 const ConfigTracker& tracker,
664 std::vector<const char*>& args,
665 std::vector<const char*>::iterator& i,
666 ostream *oss,
667 int level)
7c673cae
FG
668{
669 int ret = 0;
c07f9fc5 670 size_t o = 0;
7c673cae
FG
671 std::string val;
672
c07f9fc5 673 std::string option_name;
7c673cae
FG
674 std::string error_message;
675 o = 0;
c07f9fc5
FG
676 for (const auto& opt_iter: schema) {
677 const Option &opt = opt_iter.second;
7c673cae 678 ostringstream err;
7c673cae 679 std::string as_option("--");
c07f9fc5
FG
680 as_option += opt.name;
681 option_name = opt.name;
11fdf7f2
TL
682 if (ceph_argparse_witharg(
683 args, i, &val, err,
684 string(string("--default-") + opt.name).c_str(), (char*)NULL)) {
685 if (!err.str().empty()) {
686 error_message = err.str();
687 ret = -EINVAL;
688 break;
689 }
690 ret = _set_val(values, tracker, val, opt, CONF_DEFAULT, &error_message);
691 break;
692 } else if (opt.type == Option::TYPE_BOOL) {
7c673cae
FG
693 int res;
694 if (ceph_argparse_binary_flag(args, i, &res, oss, as_option.c_str(),
695 (char*)NULL)) {
696 if (res == 0)
11fdf7f2 697 ret = _set_val(values, tracker, "false", opt, level, &error_message);
7c673cae 698 else if (res == 1)
11fdf7f2 699 ret = _set_val(values, tracker, "true", opt, level, &error_message);
7c673cae
FG
700 else
701 ret = res;
702 break;
703 } else {
704 std::string no("--no-");
c07f9fc5 705 no += opt.name;
7c673cae 706 if (ceph_argparse_flag(args, i, no.c_str(), (char*)NULL)) {
11fdf7f2 707 ret = _set_val(values, tracker, "false", opt, level, &error_message);
7c673cae
FG
708 break;
709 }
710 }
711 } else if (ceph_argparse_witharg(args, i, &val, err,
712 as_option.c_str(), (char*)NULL)) {
713 if (!err.str().empty()) {
714 error_message = err.str();
715 ret = -EINVAL;
716 break;
717 }
11fdf7f2 718 ret = _set_val(values, tracker, val, opt, level, &error_message);
7c673cae
FG
719 break;
720 }
721 ++o;
722 }
723
11fdf7f2
TL
724 if (ret < 0 || !error_message.empty()) {
725 ceph_assert(!option_name.empty());
7c673cae
FG
726 if (oss) {
727 *oss << "Parse error setting " << option_name << " to '"
728 << val << "' using injectargs";
729 if (!error_message.empty()) {
730 *oss << " (" << error_message << ")";
731 }
732 *oss << ".\n";
733 } else {
734 cerr << "parse error setting '" << option_name << "' to '"
735 << val << "'";
736 if (!error_message.empty()) {
737 cerr << " (" << error_message << ")";
738 }
739 cerr << "\n" << std::endl;
740 }
741 }
742
c07f9fc5 743 if (o == schema.size()) {
7c673cae
FG
744 // ignore
745 ++i;
746 }
11fdf7f2 747 return ret >= 0 ? 0 : ret;
7c673cae
FG
748}
749
11fdf7f2
TL
750int md_config_t::parse_injectargs(ConfigValues& values,
751 const ConfigTracker& tracker,
752 std::vector<const char*>& args,
7c673cae
FG
753 std::ostream *oss)
754{
7c673cae
FG
755 int ret = 0;
756 for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
11fdf7f2 757 int r = parse_option(values, tracker, args, i, oss, CONF_OVERRIDE);
7c673cae
FG
758 if (r < 0)
759 ret = r;
760 }
761 return ret;
762}
763
11fdf7f2 764void md_config_t::set_safe_to_start_threads()
7c673cae 765{
11fdf7f2 766 safe_to_start_threads = true;
7c673cae
FG
767}
768
11fdf7f2 769void md_config_t::_clear_safe_to_start_threads()
7c673cae 770{
11fdf7f2 771 safe_to_start_threads = false;
7c673cae
FG
772}
773
11fdf7f2
TL
774int md_config_t::injectargs(ConfigValues& values,
775 const ConfigTracker& tracker,
776 const std::string& s, std::ostream *oss)
7c673cae
FG
777{
778 int ret;
11fdf7f2
TL
779 char b[s.length()+1];
780 strcpy(b, s.c_str());
781 std::vector<const char*> nargs;
782 char *p = b;
783 while (*p) {
784 nargs.push_back(p);
785 while (*p && *p != ' ') p++;
786 if (!*p)
787 break;
788 *p++ = 0;
789 while (*p && *p == ' ') p++;
790 }
791 ret = parse_injectargs(values, tracker, nargs, oss);
792 if (!nargs.empty()) {
793 *oss << " failed to parse arguments: ";
794 std::string prefix;
795 for (std::vector<const char*>::const_iterator i = nargs.begin();
796 i != nargs.end(); ++i) {
797 *oss << prefix << *i;
798 prefix = ",";
7c673cae 799 }
11fdf7f2
TL
800 *oss << "\n";
801 ret = -EINVAL;
7c673cae 802 }
11fdf7f2 803 update_legacy_vals(values);
7c673cae
FG
804 return ret;
805}
806
11fdf7f2
TL
807void md_config_t::set_val_or_die(ConfigValues& values,
808 const ConfigTracker& tracker,
809 const std::string &key,
810 const std::string &val)
7c673cae 811{
c07f9fc5 812 std::stringstream err;
11fdf7f2 813 int ret = set_val(values, tracker, key, val, &err);
c07f9fc5
FG
814 if (ret != 0) {
815 std::cerr << "set_val_or_die(" << key << "): " << err.str();
7c673cae 816 }
11fdf7f2 817 ceph_assert(ret == 0);
7c673cae
FG
818}
819
11fdf7f2
TL
820int md_config_t::set_val(ConfigValues& values,
821 const ConfigTracker& tracker,
822 const std::string &key, const char *val,
823 std::stringstream *err_ss)
7c673cae 824{
c07f9fc5
FG
825 if (key.empty()) {
826 if (err_ss) *err_ss << "No key specified";
7c673cae 827 return -EINVAL;
c07f9fc5
FG
828 }
829 if (!val) {
7c673cae 830 return -EINVAL;
c07f9fc5 831 }
7c673cae
FG
832
833 std::string v(val);
7c673cae
FG
834
835 string k(ConfFile::normalize_key_name(key));
836
c07f9fc5
FG
837 const auto &opt_iter = schema.find(k);
838 if (opt_iter != schema.end()) {
839 const Option &opt = opt_iter->second;
7c673cae 840 std::string error_message;
11fdf7f2
TL
841 int r = _set_val(values, tracker, v, opt, CONF_OVERRIDE, &error_message);
842 if (r >= 0) {
c07f9fc5 843 if (err_ss) *err_ss << "Set " << opt.name << " to " << v;
11fdf7f2 844 r = 0;
c07f9fc5
FG
845 } else {
846 if (err_ss) *err_ss << error_message;
847 }
7c673cae
FG
848 return r;
849 }
850
c07f9fc5 851 if (err_ss) *err_ss << "Configuration option not found: '" << key << "'";
7c673cae
FG
852 return -ENOENT;
853}
854
11fdf7f2
TL
855int md_config_t::rm_val(ConfigValues& values, const std::string& key)
856{
857 return _rm_val(values, key, CONF_OVERRIDE);
858}
859
860void md_config_t::get_defaults_bl(const ConfigValues& values,
861 bufferlist *bl)
862{
863 if (defaults_bl.length() == 0) {
864 uint32_t n = 0;
865 bufferlist bl;
866 for (const auto &i : schema) {
867 ++n;
868 encode(i.second.name, bl);
869 auto [value, found] = values.get_value(i.second.name, CONF_DEFAULT);
870 if (found) {
871 encode(Option::to_str(value), bl);
872 } else {
873 string val;
874 conf_stringify(_get_val_default(i.second), &val);
875 encode(val, bl);
876 }
877 }
878 encode(n, defaults_bl);
879 defaults_bl.claim_append(bl);
880 }
881 *bl = defaults_bl;
882}
883
884void md_config_t::get_config_bl(
885 const ConfigValues& values,
886 uint64_t have_version,
887 bufferlist *bl,
888 uint64_t *got_version)
889{
890 if (values_bl.length() == 0) {
891 uint32_t n = 0;
892 bufferlist bl;
893 values.for_each([&](auto& name, auto& configs) {
894 if (name == "fsid" ||
895 name == "host") {
896 return;
897 }
898 ++n;
899 encode(name, bl);
900 encode((uint32_t)configs.size(), bl);
901 for (auto& j : configs) {
902 encode(j.first, bl);
903 encode(Option::to_str(j.second), bl);
904 }
905 });
906 // make sure overridden items appear, and include the default value
907 for (auto& i : ignored_mon_values) {
908 if (values.contains(i.first)) {
909 continue;
910 }
911 if (i.first == "fsid" ||
912 i.first == "host") {
913 continue;
914 }
915 const Option *opt = find_option(i.first);
916 if (!opt) {
917 continue;
918 }
919 ++n;
920 encode(i.first, bl);
921 encode((uint32_t)1, bl);
922 encode((int32_t)CONF_DEFAULT, bl);
923 string val;
924 conf_stringify(_get_val_default(*opt), &val);
925 encode(val, bl);
926 }
927 encode(n, values_bl);
928 values_bl.claim_append(bl);
929 encode(ignored_mon_values, values_bl);
930 ++values_bl_version;
931 }
932 if (have_version != values_bl_version) {
933 *bl = values_bl;
934 *got_version = values_bl_version;
935 }
936}
7c673cae 937
11fdf7f2
TL
938int md_config_t::get_val(const ConfigValues& values,
939 const std::string &key, char **buf, int len) const
7c673cae 940{
11fdf7f2
TL
941 string k(ConfFile::normalize_key_name(key));
942 return _get_val_cstr(values, k, buf, len);
7c673cae
FG
943}
944
11fdf7f2
TL
945int md_config_t::get_val(
946 const ConfigValues& values,
947 const std::string &key,
948 std::string *val) const
7c673cae 949{
11fdf7f2 950 return conf_stringify(get_val_generic(values, key), val);
7c673cae
FG
951}
952
11fdf7f2
TL
953Option::value_t md_config_t::get_val_generic(
954 const ConfigValues& values,
955 const std::string &key) const
7c673cae 956{
11fdf7f2
TL
957 string k(ConfFile::normalize_key_name(key));
958 return _get_val(values, k);
959}
7c673cae 960
11fdf7f2
TL
961Option::value_t md_config_t::_get_val(
962 const ConfigValues& values,
963 const std::string &key,
964 expand_stack_t *stack,
965 std::ostream *err) const
966{
c07f9fc5
FG
967 if (key.empty()) {
968 return Option::value_t(boost::blank());
969 }
7c673cae
FG
970
971 // In key names, leading and trailing whitespace are not significant.
972 string k(ConfFile::normalize_key_name(key));
973
11fdf7f2
TL
974 const Option *o = find_option(key);
975 if (!o) {
976 // not a valid config option
c07f9fc5 977 return Option::value_t(boost::blank());
7c673cae 978 }
11fdf7f2
TL
979
980 return _get_val(values, *o, stack, err);
7c673cae
FG
981}
982
11fdf7f2
TL
983Option::value_t md_config_t::_get_val(
984 const ConfigValues& values,
985 const Option& o,
986 expand_stack_t *stack,
987 std::ostream *err) const
988{
989 expand_stack_t a_stack;
990 if (!stack) {
991 stack = &a_stack;
992 }
993 return _expand_meta(values,
994 _get_val_nometa(values, o),
995 &o, stack, err);
996}
7c673cae 997
11fdf7f2
TL
998Option::value_t md_config_t::_get_val_nometa(const ConfigValues& values,
999 const Option& o) const
1000{
1001 if (auto [value, found] = values.get_value(o.name, -1); found) {
1002 return value;
1003 } else {
1004 return _get_val_default(o);
1005 }
1006}
1007
1008const Option::value_t& md_config_t::_get_val_default(const Option& o) const
1009{
1010 bool has_daemon_default = !boost::get<boost::blank>(&o.daemon_value);
1011 if (is_daemon && has_daemon_default) {
1012 return o.daemon_value;
1013 } else {
1014 return o.value;
1015 }
1016}
1017
1018void md_config_t::early_expand_meta(
1019 const ConfigValues& values,
1020 std::string &val,
1021 std::ostream *err) const
1022{
1023 expand_stack_t stack;
1024 Option::value_t v = _expand_meta(values,
1025 Option::value_t(val),
1026 nullptr, &stack, err);
1027 conf_stringify(v, &val);
1028}
1029
1030bool md_config_t::finalize_reexpand_meta(ConfigValues& values,
1031 const ConfigTracker& tracker)
1032{
1033 for (auto& [name, value] : may_reexpand_meta) {
1034 set_val(values, tracker, name, value);
1035 }
1036
1037 if (!may_reexpand_meta.empty()) {
1038 // meta expands could have modified anything. Copy it all out again.
1039 update_legacy_vals(values);
1040 return true;
1041 } else {
1042 return false;
1043 }
1044}
1045
1046Option::value_t md_config_t::_expand_meta(
1047 const ConfigValues& values,
1048 const Option::value_t& in,
1049 const Option *o,
1050 expand_stack_t *stack,
1051 std::ostream *err) const
1052{
1053 //cout << __func__ << " in '" << in << "' stack " << stack << std::endl;
1054 if (!stack) {
1055 return in;
1056 }
1057 const std::string *str = boost::get<const std::string>(&in);
1058 if (!str) {
1059 // strings only!
1060 return in;
1061 }
1062
1063 auto pos = str->find('$');
1064 if (pos == std::string::npos) {
1065 // no substitutions!
1066 return in;
1067 }
1068
1069 if (o) {
1070 stack->push_back(make_pair(o, &in));
1071 }
1072 string out;
1073 decltype(pos) last_pos = 0;
1074 while (pos != std::string::npos) {
1075 ceph_assert((*str)[pos] == '$');
1076 if (pos > last_pos) {
1077 out += str->substr(last_pos, pos - last_pos);
1078 }
1079
1080 // try to parse the variable name into var, either \$\{(.+)\} or
1081 // \$[a-z\_]+
1082 const char *valid_chars = "abcdefghijklmnopqrstuvwxyz_";
1083 string var;
1084 size_t endpos = 0;
1085 if ((*str)[pos+1] == '{') {
1086 // ...${foo_bar}...
1087 endpos = str->find_first_not_of(valid_chars, pos + 2);
1088 if (endpos != std::string::npos &&
1089 (*str)[endpos] == '}') {
1090 var = str->substr(pos + 2, endpos - pos - 2);
1091 endpos++;
1092 }
7c673cae 1093 } else {
11fdf7f2
TL
1094 // ...$foo...
1095 endpos = str->find_first_not_of(valid_chars, pos + 1);
1096 if (endpos != std::string::npos)
1097 var = str->substr(pos + 1, endpos - pos - 1);
1098 else
1099 var = str->substr(pos + 1);
7c673cae 1100 }
11fdf7f2
TL
1101 last_pos = endpos;
1102
1103 if (!var.size()) {
1104 out += '$';
1105 } else {
1106 //cout << " found var " << var << std::endl;
1107 // special metavariable?
1108 if (var == "type") {
1109 out += values.name.get_type_name();
1110 } else if (var == "cluster") {
1111 out += values.cluster;
1112 } else if (var == "name") {
1113 out += values.name.to_cstr();
1114 } else if (var == "host") {
1115 if (values.host == "") {
1116 out += ceph_get_short_hostname();
1117 } else {
1118 out += values.host;
1119 }
1120 } else if (var == "num") {
1121 out += values.name.get_id().c_str();
1122 } else if (var == "id") {
1123 out += values.name.get_id();
1124 } else if (var == "pid") {
1125 out += stringify(getpid());
1126 if (o) {
1127 may_reexpand_meta[o->name] = *str;
1128 }
1129 } else if (var == "cctid") {
1130 out += stringify((unsigned long long)this);
1131 } else if (var == "home") {
1132 const char *home = getenv("HOME");
1133 out = home ? std::string(home) : std::string();
1134 } else {
1135 if (var == "data_dir") {
1136 var = data_dir_option;
1137 }
1138 const Option *o = find_option(var);
1139 if (!o) {
1140 out += str->substr(pos, endpos - pos);
1141 } else {
1142 auto match = std::find_if(
1143 stack->begin(), stack->end(),
1144 [o](pair<const Option *,const Option::value_t*>& item) {
1145 return item.first == o;
1146 });
1147 if (match != stack->end()) {
1148 // substitution loop; break the cycle
1149 if (err) {
1150 *err << "variable expansion loop at " << var << "="
1151 << Option::to_str(*match->second) << "\n"
1152 << "expansion stack:\n";
1153 for (auto i = stack->rbegin(); i != stack->rend(); ++i) {
1154 *err << i->first->name << "="
1155 << Option::to_str(*i->second) << "\n";
1156 }
1157 }
1158 return Option::value_t(std::string("$") + o->name);
1159 } else {
1160 // recursively evaluate!
1161 string n;
1162 conf_stringify(_get_val(values, *o, stack, err), &n);
1163 out += n;
1164 }
1165 }
1166 }
1167 }
1168 pos = str->find('$', last_pos);
7c673cae 1169 }
11fdf7f2
TL
1170 if (last_pos != std::string::npos) {
1171 out += str->substr(last_pos);
1172 }
1173 if (o) {
1174 stack->pop_back();
1175 }
1176
1177 return Option::value_t(out);
7c673cae
FG
1178}
1179
11fdf7f2
TL
1180int md_config_t::_get_val_cstr(
1181 const ConfigValues& values,
1182 const std::string &key, char **buf, int len) const
7c673cae 1183{
c07f9fc5 1184 if (key.empty())
7c673cae
FG
1185 return -EINVAL;
1186
11fdf7f2
TL
1187 string val;
1188 if (conf_stringify(_get_val(values, key), &val) == 0) {
224ce89b 1189 int l = val.length() + 1;
7c673cae
FG
1190 if (len == -1) {
1191 *buf = (char*)malloc(l);
1192 if (!*buf)
1193 return -ENOMEM;
224ce89b 1194 strncpy(*buf, val.c_str(), l);
7c673cae
FG
1195 return 0;
1196 }
224ce89b 1197 snprintf(*buf, len, "%s", val.c_str());
7c673cae
FG
1198 return (l > len) ? -ENAMETOOLONG : 0;
1199 }
1200
224ce89b 1201 string k(ConfFile::normalize_key_name(key));
7c673cae
FG
1202
1203 // couldn't find a configuration option with key 'k'
1204 return -ENOENT;
1205}
1206
1207void md_config_t::get_all_keys(std::vector<std::string> *keys) const {
1208 const std::string negative_flag_prefix("no_");
1209
1210 keys->clear();
c07f9fc5
FG
1211 keys->reserve(schema.size());
1212 for (const auto &i: schema) {
1213 const Option &opt = i.second;
7c673cae 1214 keys->push_back(opt.name);
c07f9fc5 1215 if (opt.type == Option::TYPE_BOOL) {
7c673cae
FG
1216 keys->push_back(negative_flag_prefix + opt.name);
1217 }
1218 }
7c673cae
FG
1219}
1220
1221/* The order of the sections here is important. The first section in the
1222 * vector is the "highest priority" section; if we find it there, we'll stop
1223 * looking. The lowest priority section is the one we look in only if all
1224 * others had nothing. This should always be the global section.
1225 */
11fdf7f2
TL
1226void md_config_t::get_my_sections(const ConfigValues& values,
1227 std::vector <std::string> &sections) const
7c673cae 1228{
11fdf7f2 1229 _get_my_sections(values, sections);
7c673cae
FG
1230}
1231
11fdf7f2
TL
1232void md_config_t::_get_my_sections(const ConfigValues& values,
1233 std::vector <std::string> &sections) const
7c673cae 1234{
11fdf7f2 1235 sections.push_back(values.name.to_str());
7c673cae 1236
11fdf7f2 1237 sections.push_back(values.name.get_type_name());
7c673cae
FG
1238
1239 sections.push_back("global");
1240}
1241
1242// Return a list of all sections
1243int md_config_t::get_all_sections(std::vector <std::string> &sections) const
1244{
7c673cae
FG
1245 for (ConfFile::const_section_iter_t s = cf.sections_begin();
1246 s != cf.sections_end(); ++s) {
1247 sections.push_back(s->first);
1248 }
1249 return 0;
1250}
1251
11fdf7f2
TL
1252int md_config_t::get_val_from_conf_file(
1253 const ConfigValues& values,
1254 const std::vector <std::string> &sections,
1255 const std::string &key,
1256 std::string &out,
1257 bool emeta) const
7c673cae 1258{
11fdf7f2
TL
1259 int r = _get_val_from_conf_file(sections, key, out);
1260 if (r < 0) {
1261 return r;
1262 }
1263 if (emeta) {
1264 expand_stack_t stack;
1265 auto v = _expand_meta(values, Option::value_t(out), nullptr, &stack, nullptr);
1266 conf_stringify(v, &out);
1267 }
1268 return 0;
7c673cae
FG
1269}
1270
11fdf7f2
TL
1271int md_config_t::_get_val_from_conf_file(
1272 const std::vector <std::string> &sections,
1273 const std::string &key,
1274 std::string &out) const
7c673cae 1275{
7c673cae
FG
1276 std::vector <std::string>::const_iterator s = sections.begin();
1277 std::vector <std::string>::const_iterator s_end = sections.end();
1278 for (; s != s_end; ++s) {
1279 int ret = cf.read(s->c_str(), key, out);
1280 if (ret == 0) {
7c673cae 1281 return 0;
11fdf7f2 1282 } else if (ret != -ENOENT) {
7c673cae 1283 return ret;
11fdf7f2 1284 }
7c673cae
FG
1285 }
1286 return -ENOENT;
1287}
1288
11fdf7f2
TL
1289int md_config_t::_set_val(
1290 ConfigValues& values,
1291 const ConfigTracker& observers,
1292 const std::string &raw_val,
1293 const Option &opt,
1294 int level,
1295 std::string *error_message)
7c673cae 1296{
11fdf7f2
TL
1297 Option::value_t new_value;
1298 int r = opt.parse_value(raw_val, &new_value, error_message);
1299 if (r < 0) {
c07f9fc5
FG
1300 return r;
1301 }
7c673cae 1302
11fdf7f2
TL
1303 // unsafe runtime change?
1304 if (!opt.can_update_at_runtime() &&
1305 safe_to_start_threads &&
1306 !observers.is_tracking(opt.name)) {
1307 // accept value if it is not actually a change
1308 if (new_value != _get_val_nometa(values, opt)) {
1309 *error_message = string("Configuration option '") + opt.name +
1310 "' may not be modified at runtime";
1311 return -ENOSYS;
c07f9fc5 1312 }
7c673cae 1313 }
7c673cae 1314
c07f9fc5 1315 // Apply the value to its entry in the `values` map
11fdf7f2
TL
1316 auto result = values.set_value(opt.name, std::move(new_value), level);
1317 switch (result) {
1318 case ConfigValues::SET_NO_CHANGE:
1319 break;
1320 case ConfigValues::SET_NO_EFFECT:
1321 values_bl.clear();
1322 break;
1323 case ConfigValues::SET_HAVE_EFFECT:
1324 values_bl.clear();
1325 _refresh(values, opt);
1326 break;
1327 }
1328 return result;
1329}
7c673cae 1330
11fdf7f2
TL
1331void md_config_t::_refresh(ConfigValues& values, const Option& opt)
1332{
c07f9fc5
FG
1333 // Apply the value to its legacy field, if it has one
1334 auto legacy_ptr_iter = legacy_values.find(std::string(opt.name));
1335 if (legacy_ptr_iter != legacy_values.end()) {
11fdf7f2
TL
1336 update_legacy_val(values, opt, legacy_ptr_iter->second);
1337 }
1338 // Was this a debug_* option update?
1339 if (opt.subsys >= 0) {
1340 string actual_val;
1341 conf_stringify(_get_val(values, opt), &actual_val);
1342 values.set_logging(opt.subsys, actual_val.c_str());
1343 } else {
1344 // normal option, advertise the change.
1345 values.changed.insert(opt.name);
7c673cae 1346 }
11fdf7f2 1347}
7c673cae 1348
11fdf7f2
TL
1349int md_config_t::_rm_val(ConfigValues& values,
1350 const std::string& key,
1351 int level)
1352{
1353 if (schema.count(key) == 0) {
1354 return -EINVAL;
1355 }
1356 auto ret = values.rm_val(key, level);
1357 if (ret < 0) {
1358 return ret;
1359 }
1360 if (ret == ConfigValues::SET_HAVE_EFFECT) {
1361 _refresh(values, *find_option(key));
1362 }
1363 values_bl.clear();
c07f9fc5
FG
1364 return 0;
1365}
7c673cae 1366
11fdf7f2
TL
1367namespace {
1368template<typename Size>
1369struct get_size_visitor : public boost::static_visitor<Size>
1370{
1371 get_size_visitor() {}
1372
1373 template<typename T>
1374 Size operator()(const T&) const {
1375 return -1;
1376 }
1377 Size operator()(const Option::size_t& sz) const {
1378 return static_cast<Size>(sz.value);
1379 }
1380 Size operator()(const Size& v) const {
1381 return v;
1382 }
1383};
1384
c07f9fc5
FG
1385/**
1386 * Handles assigning from a variant-of-types to a variant-of-pointers-to-types
1387 */
11fdf7f2 1388template<class Config>
c07f9fc5
FG
1389class assign_visitor : public boost::static_visitor<>
1390{
11fdf7f2 1391 Config *conf;
c07f9fc5
FG
1392 Option::value_t val;
1393 public:
7c673cae 1394
11fdf7f2 1395 assign_visitor(Config *conf_, Option::value_t val_)
c07f9fc5
FG
1396 : conf(conf_), val(val_)
1397 {}
1398
1399 template <typename T>
11fdf7f2 1400 void operator()(T Config::* ptr) const
c07f9fc5 1401 {
11fdf7f2 1402 T *member = const_cast<T *>(&(conf->*(boost::get<const T Config::*>(ptr))));
c07f9fc5
FG
1403
1404 *member = boost::get<T>(val);
7c673cae 1405 }
11fdf7f2
TL
1406 void operator()(uint64_t Config::* ptr) const
1407 {
1408 using T = uint64_t;
1409 auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr))));
1410 *member = boost::apply_visitor(get_size_visitor<T>{}, val);
1411 }
1412 void operator()(int64_t Config::* ptr) const
1413 {
1414 using T = int64_t;
1415 auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr))));
1416 *member = boost::apply_visitor(get_size_visitor<T>{}, val);
c07f9fc5 1417 }
7c673cae 1418};
11fdf7f2 1419} // anonymous namespace
7c673cae 1420
11fdf7f2 1421void md_config_t::update_legacy_vals(ConfigValues& values)
7c673cae 1422{
11fdf7f2
TL
1423 for (const auto &i : legacy_values) {
1424 const auto &name = i.first;
1425 const auto &option = schema.at(name);
1426 auto ptr = i.second;
1427 update_legacy_val(values, option, ptr);
7c673cae 1428 }
7c673cae
FG
1429}
1430
11fdf7f2
TL
1431void md_config_t::update_legacy_val(ConfigValues& values,
1432 const Option &opt,
1433 md_config_t::member_ptr_t member_ptr)
7c673cae 1434{
11fdf7f2
TL
1435 Option::value_t v = _get_val(values, opt);
1436 boost::apply_visitor(assign_visitor(&values, v), member_ptr);
7c673cae
FG
1437}
1438
11fdf7f2 1439static void dump(Formatter *f, int level, Option::value_t in)
7c673cae 1440{
11fdf7f2
TL
1441 if (const bool *v = boost::get<const bool>(&in)) {
1442 f->dump_bool(ceph_conf_level_name(level), *v);
1443 } else if (const int64_t *v = boost::get<const int64_t>(&in)) {
1444 f->dump_int(ceph_conf_level_name(level), *v);
1445 } else if (const uint64_t *v = boost::get<const uint64_t>(&in)) {
1446 f->dump_unsigned(ceph_conf_level_name(level), *v);
1447 } else if (const double *v = boost::get<const double>(&in)) {
1448 f->dump_float(ceph_conf_level_name(level), *v);
1449 } else {
1450 f->dump_stream(ceph_conf_level_name(level)) << Option::to_str(in);
7c673cae 1451 }
7c673cae
FG
1452}
1453
1454void md_config_t::diff(
11fdf7f2
TL
1455 const ConfigValues& values,
1456 Formatter *f,
1457 string name) const
7c673cae 1458{
11fdf7f2
TL
1459 values.for_each([this, f, &values] (auto& name, auto& configs) {
1460 if (configs.size() == 1 &&
1461 configs.begin()->first == CONF_DEFAULT) {
1462 // we only have a default value; exclude from diff
1463 return;
7c673cae 1464 }
11fdf7f2
TL
1465 f->open_object_section(name.c_str());
1466 const Option *o = find_option(name);
1467 dump(f, CONF_DEFAULT, _get_val_default(*o));
1468 for (auto& j : configs) {
1469 dump(f, j.first, j.second);
31f18b77 1470 }
11fdf7f2
TL
1471 dump(f, CONF_FINAL, _get_val(values, *o));
1472 f->close_section();
1473 });
7c673cae
FG
1474}
1475
1476void md_config_t::complain_about_parse_errors(CephContext *cct)
1477{
1478 ::complain_about_parse_errors(cct, &parse_errors);
1479}