]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/config.cc
53007f925146d65489b0840b6905f528266312aa
[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 std::vector<std::string> reexpands;
1111 reexpands.swap(may_reexpand_meta);
1112 for (auto& name : reexpands) {
1113 // always refresh the options if they are in the may_reexpand_meta
1114 // map, because the options may have already been expanded with old
1115 // meta.
1116 const auto &opt_iter = schema.find(name);
1117 ceph_assert(opt_iter != schema.end());
1118 const Option &opt = opt_iter->second;
1119 _refresh(values, opt);
1120 }
1121
1122 return !may_reexpand_meta.empty();
1123 }
1124
1125 Option::value_t md_config_t::_expand_meta(
1126 const ConfigValues& values,
1127 const Option::value_t& in,
1128 const Option *o,
1129 expand_stack_t *stack,
1130 std::ostream *err) const
1131 {
1132 //cout << __func__ << " in '" << in << "' stack " << stack << std::endl;
1133 if (!stack) {
1134 return in;
1135 }
1136 const std::string *str = boost::get<const std::string>(&in);
1137 if (!str) {
1138 // strings only!
1139 return in;
1140 }
1141
1142 auto pos = str->find('$');
1143 if (pos == std::string::npos) {
1144 // no substitutions!
1145 return in;
1146 }
1147
1148 if (o) {
1149 stack->push_back(make_pair(o, &in));
1150 }
1151 string out;
1152 decltype(pos) last_pos = 0;
1153 while (pos != std::string::npos) {
1154 ceph_assert((*str)[pos] == '$');
1155 if (pos > last_pos) {
1156 out += str->substr(last_pos, pos - last_pos);
1157 }
1158
1159 // try to parse the variable name into var, either \$\{(.+)\} or
1160 // \$[a-z\_]+
1161 const char *valid_chars = "abcdefghijklmnopqrstuvwxyz_";
1162 string var;
1163 size_t endpos = 0;
1164 if ((*str)[pos+1] == '{') {
1165 // ...${foo_bar}...
1166 endpos = str->find_first_not_of(valid_chars, pos + 2);
1167 if (endpos != std::string::npos &&
1168 (*str)[endpos] == '}') {
1169 var = str->substr(pos + 2, endpos - pos - 2);
1170 endpos++;
1171 }
1172 } else {
1173 // ...$foo...
1174 endpos = str->find_first_not_of(valid_chars, pos + 1);
1175 if (endpos != std::string::npos)
1176 var = str->substr(pos + 1, endpos - pos - 1);
1177 else
1178 var = str->substr(pos + 1);
1179 }
1180 last_pos = endpos;
1181
1182 if (!var.size()) {
1183 out += '$';
1184 } else {
1185 //cout << " found var " << var << std::endl;
1186 // special metavariable?
1187 if (var == "type") {
1188 out += values.name.get_type_name();
1189 } else if (var == "cluster") {
1190 out += values.cluster;
1191 } else if (var == "name") {
1192 out += values.name.to_cstr();
1193 } else if (var == "host") {
1194 if (values.host == "") {
1195 out += ceph_get_short_hostname();
1196 } else {
1197 out += values.host;
1198 }
1199 } else if (var == "num") {
1200 out += values.name.get_id().c_str();
1201 } else if (var == "id") {
1202 out += values.name.get_id();
1203 } else if (var == "pid") {
1204 out += stringify(getpid());
1205 if (o) {
1206 may_reexpand_meta.push_back(o->name);
1207 }
1208 } else if (var == "cctid") {
1209 out += stringify((unsigned long long)this);
1210 } else if (var == "home") {
1211 const char *home = getenv("HOME");
1212 out = home ? std::string(home) : std::string();
1213 } else {
1214 if (var == "data_dir") {
1215 var = data_dir_option;
1216 }
1217 const Option *o = find_option(var);
1218 if (!o) {
1219 out += str->substr(pos, endpos - pos);
1220 } else {
1221 auto match = std::find_if(
1222 stack->begin(), stack->end(),
1223 [o](pair<const Option *,const Option::value_t*>& item) {
1224 return item.first == o;
1225 });
1226 if (match != stack->end()) {
1227 // substitution loop; break the cycle
1228 if (err) {
1229 *err << "variable expansion loop at " << var << "="
1230 << Option::to_str(*match->second) << "\n"
1231 << "expansion stack:\n";
1232 for (auto i = stack->rbegin(); i != stack->rend(); ++i) {
1233 *err << i->first->name << "="
1234 << Option::to_str(*i->second) << "\n";
1235 }
1236 }
1237 return Option::value_t(std::string("$") + o->name);
1238 } else {
1239 // recursively evaluate!
1240 string n;
1241 conf_stringify(_get_val(values, *o, stack, err), &n);
1242 out += n;
1243 }
1244 }
1245 }
1246 }
1247 pos = str->find('$', last_pos);
1248 }
1249 if (last_pos != std::string::npos) {
1250 out += str->substr(last_pos);
1251 }
1252 if (o) {
1253 stack->pop_back();
1254 }
1255
1256 return Option::value_t(out);
1257 }
1258
1259 int md_config_t::_get_val_cstr(
1260 const ConfigValues& values,
1261 const std::string& key, char **buf, int len) const
1262 {
1263 if (key.empty())
1264 return -EINVAL;
1265
1266 string val;
1267 if (conf_stringify(_get_val(values, key), &val) == 0) {
1268 int l = val.length() + 1;
1269 if (len == -1) {
1270 *buf = (char*)malloc(l);
1271 if (!*buf)
1272 return -ENOMEM;
1273 strncpy(*buf, val.c_str(), l);
1274 return 0;
1275 }
1276 snprintf(*buf, len, "%s", val.c_str());
1277 return (l > len) ? -ENAMETOOLONG : 0;
1278 }
1279
1280 // couldn't find a configuration option with key 'k'
1281 return -ENOENT;
1282 }
1283
1284 void md_config_t::get_all_keys(std::vector<std::string> *keys) const {
1285 const std::string negative_flag_prefix("no_");
1286
1287 keys->clear();
1288 keys->reserve(schema.size());
1289 for (const auto &i: schema) {
1290 const Option &opt = i.second;
1291 keys->push_back(opt.name);
1292 if (opt.type == Option::TYPE_BOOL) {
1293 keys->push_back(negative_flag_prefix + opt.name);
1294 }
1295 }
1296 }
1297
1298 /* The order of the sections here is important. The first section in the
1299 * vector is the "highest priority" section; if we find it there, we'll stop
1300 * looking. The lowest priority section is the one we look in only if all
1301 * others had nothing. This should always be the global section.
1302 */
1303 void md_config_t::get_my_sections(const ConfigValues& values,
1304 std::vector <std::string> &sections) const
1305 {
1306 _get_my_sections(values, sections);
1307 }
1308
1309 void md_config_t::_get_my_sections(const ConfigValues& values,
1310 std::vector <std::string> &sections) const
1311 {
1312 sections.push_back(values.name.to_str());
1313
1314 sections.push_back(values.name.get_type_name());
1315
1316 sections.push_back("global");
1317 }
1318
1319 // Return a list of all sections
1320 int md_config_t::get_all_sections(std::vector <std::string> &sections) const
1321 {
1322 for (auto [section_name, section] : cf) {
1323 sections.push_back(section_name);
1324 std::ignore = section;
1325 }
1326 return 0;
1327 }
1328
1329 int md_config_t::get_val_from_conf_file(
1330 const ConfigValues& values,
1331 const std::vector <std::string> &sections,
1332 const std::string_view key,
1333 std::string &out,
1334 bool emeta) const
1335 {
1336 int r = _get_val_from_conf_file(sections, key, out);
1337 if (r < 0) {
1338 return r;
1339 }
1340 if (emeta) {
1341 expand_stack_t stack;
1342 auto v = _expand_meta(values, Option::value_t(out), nullptr, &stack, nullptr);
1343 conf_stringify(v, &out);
1344 }
1345 return 0;
1346 }
1347
1348 int md_config_t::_get_val_from_conf_file(
1349 const std::vector <std::string> &sections,
1350 const std::string_view key,
1351 std::string &out) const
1352 {
1353 for (auto &s : sections) {
1354 int ret = cf.read(s.c_str(), std::string{key}, out);
1355 if (ret == 0) {
1356 return 0;
1357 } else if (ret != -ENOENT) {
1358 return ret;
1359 }
1360 }
1361 return -ENOENT;
1362 }
1363
1364 int md_config_t::_set_val(
1365 ConfigValues& values,
1366 const ConfigTracker& observers,
1367 const std::string &raw_val,
1368 const Option &opt,
1369 int level,
1370 std::string *error_message)
1371 {
1372 Option::value_t new_value;
1373 ceph_assert(error_message);
1374 int r = opt.parse_value(raw_val, &new_value, error_message);
1375 if (r < 0) {
1376 return r;
1377 }
1378
1379 // unsafe runtime change?
1380 if (!opt.can_update_at_runtime() &&
1381 safe_to_start_threads &&
1382 !observers.is_tracking(opt.name)) {
1383 // accept value if it is not actually a change
1384 if (new_value != _get_val_nometa(values, opt)) {
1385 *error_message = string("Configuration option '") + opt.name +
1386 "' may not be modified at runtime";
1387 return -EPERM;
1388 }
1389 }
1390
1391 // Apply the value to its entry in the `values` map
1392 auto result = values.set_value(opt.name, std::move(new_value), level);
1393 switch (result) {
1394 case ConfigValues::SET_NO_CHANGE:
1395 break;
1396 case ConfigValues::SET_NO_EFFECT:
1397 values_bl.clear();
1398 break;
1399 case ConfigValues::SET_HAVE_EFFECT:
1400 values_bl.clear();
1401 _refresh(values, opt);
1402 break;
1403 }
1404 return result;
1405 }
1406
1407 void md_config_t::_refresh(ConfigValues& values, const Option& opt)
1408 {
1409 // Apply the value to its legacy field, if it has one
1410 auto legacy_ptr_iter = legacy_values.find(std::string(opt.name));
1411 if (legacy_ptr_iter != legacy_values.end()) {
1412 update_legacy_val(values, opt, legacy_ptr_iter->second);
1413 }
1414 // Was this a debug_* option update?
1415 if (opt.subsys >= 0) {
1416 string actual_val;
1417 conf_stringify(_get_val(values, opt), &actual_val);
1418 values.set_logging(opt.subsys, actual_val.c_str());
1419 } else {
1420 // normal option, advertise the change.
1421 values.changed.insert(opt.name);
1422 }
1423 }
1424
1425 int md_config_t::_rm_val(ConfigValues& values,
1426 const std::string_view key,
1427 int level)
1428 {
1429 if (schema.count(key) == 0) {
1430 return -EINVAL;
1431 }
1432 auto ret = values.rm_val(std::string{key}, level);
1433 if (ret < 0) {
1434 return ret;
1435 }
1436 if (ret == ConfigValues::SET_HAVE_EFFECT) {
1437 _refresh(values, *find_option(key));
1438 }
1439 values_bl.clear();
1440 return 0;
1441 }
1442
1443 namespace {
1444 template<typename Size>
1445 struct get_size_visitor : public boost::static_visitor<Size>
1446 {
1447 get_size_visitor() {}
1448
1449 template<typename T>
1450 Size operator()(const T&) const {
1451 return -1;
1452 }
1453 Size operator()(const Option::size_t& sz) const {
1454 return static_cast<Size>(sz.value);
1455 }
1456 Size operator()(const Size& v) const {
1457 return v;
1458 }
1459 };
1460
1461 /**
1462 * Handles assigning from a variant-of-types to a variant-of-pointers-to-types
1463 */
1464 template<class Config>
1465 class assign_visitor : public boost::static_visitor<>
1466 {
1467 Config *conf;
1468 Option::value_t val;
1469 public:
1470
1471 assign_visitor(Config *conf_, Option::value_t val_)
1472 : conf(conf_), val(val_)
1473 {}
1474
1475 template <typename T>
1476 void operator()(T Config::* ptr) const
1477 {
1478 T *member = const_cast<T *>(&(conf->*(boost::get<const T Config::*>(ptr))));
1479
1480 *member = boost::get<T>(val);
1481 }
1482 void operator()(uint64_t Config::* ptr) const
1483 {
1484 using T = uint64_t;
1485 auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr))));
1486 *member = boost::apply_visitor(get_size_visitor<T>{}, val);
1487 }
1488 void operator()(int64_t Config::* ptr) const
1489 {
1490 using T = int64_t;
1491 auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr))));
1492 *member = boost::apply_visitor(get_size_visitor<T>{}, val);
1493 }
1494 };
1495 } // anonymous namespace
1496
1497 void md_config_t::update_legacy_vals(ConfigValues& values)
1498 {
1499 for (const auto &i : legacy_values) {
1500 const auto &name = i.first;
1501 const auto &option = schema.at(name);
1502 auto ptr = i.second;
1503 update_legacy_val(values, option, ptr);
1504 }
1505 }
1506
1507 void md_config_t::update_legacy_val(ConfigValues& values,
1508 const Option &opt,
1509 md_config_t::member_ptr_t member_ptr)
1510 {
1511 Option::value_t v = _get_val(values, opt);
1512 boost::apply_visitor(assign_visitor(&values, v), member_ptr);
1513 }
1514
1515 static void dump(Formatter *f, int level, Option::value_t in)
1516 {
1517 if (const bool *v = boost::get<const bool>(&in)) {
1518 f->dump_bool(ceph_conf_level_name(level), *v);
1519 } else if (const int64_t *v = boost::get<const int64_t>(&in)) {
1520 f->dump_int(ceph_conf_level_name(level), *v);
1521 } else if (const uint64_t *v = boost::get<const uint64_t>(&in)) {
1522 f->dump_unsigned(ceph_conf_level_name(level), *v);
1523 } else if (const double *v = boost::get<const double>(&in)) {
1524 f->dump_float(ceph_conf_level_name(level), *v);
1525 } else {
1526 f->dump_stream(ceph_conf_level_name(level)) << Option::to_str(in);
1527 }
1528 }
1529
1530 void md_config_t::diff(
1531 const ConfigValues& values,
1532 Formatter *f,
1533 string name) const
1534 {
1535 values.for_each([this, f, &values] (auto& name, auto& configs) {
1536 if (configs.empty()) {
1537 return;
1538 }
1539 f->open_object_section(std::string{name}.c_str());
1540 const Option *o = find_option(name);
1541 if (configs.size() &&
1542 configs.begin()->first != CONF_DEFAULT) {
1543 // show compiled-in default only if an override default wasn't provided
1544 dump(f, CONF_DEFAULT, _get_val_default(*o));
1545 }
1546 for (auto& j : configs) {
1547 dump(f, j.first, j.second);
1548 }
1549 dump(f, CONF_FINAL, _get_val(values, *o));
1550 f->close_section();
1551 });
1552 }
1553
1554 void md_config_t::complain_about_parse_error(CephContext *cct)
1555 {
1556 ::complain_about_parse_error(cct, parse_error);
1557 }