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