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