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