1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
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.
15 #include <boost/type_traits.hpp>
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"
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.
38 #define dout_subsys ceph_subsys_monc
42 using std::ostringstream
;
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"
53 #define STRINGIFY(x) _STR(x)
55 const char *ceph_conf_level_name(int 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 "???";
69 int ceph_resolve_file_search(const std::string
& filename_list
,
73 get_str_list(filename_list
, ls
);
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
);
91 static int conf_stringify(const Option::value_t
& v
, string
*out
)
93 if (boost::get
<boost::blank
>(&v
)) {
96 *out
= Option::to_str(v
);
100 md_config_t::md_config_t(ConfigValues
& values
,
101 const ConfigTracker
& tracker
,
103 : is_daemon(is_daemon
)
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
<< "'"
114 schema
.emplace(std::piecewise_construct
,
115 std::forward_as_tuple(i
.name
),
116 std::forward_as_tuple(i
));
119 // Define the debug_* options as well.
120 subsys_options
.reserve(values
.subsys
.get_num());
121 for (unsigned i
= 0; i
< values
.subsys
.get_num(); ++i
) {
122 string name
= string("debug_") + values
.subsys
.get_name(i
);
123 subsys_options
.push_back(
124 Option(name
, Option::TYPE_STR
, Option::LEVEL_ADVANCED
));
125 Option
& opt
= subsys_options
.back();
126 opt
.set_default(stringify(values
.subsys
.get_log_level(i
)) + "/" +
127 stringify(values
.subsys
.get_gather_level(i
)));
128 string desc
= string("Debug level for ") + values
.subsys
.get_name(i
);
129 opt
.set_description(desc
.c_str());
130 opt
.set_flag(Option::FLAG_RUNTIME
);
131 opt
.set_long_description("The value takes the form 'N' or 'N/M' where N and M are values between 0 and 99. N is the debug level to log (all values below this are included), and M is the level to gather and buffer in memory. In the event of a crash, the most recent items <= M are dumped to the log file.");
133 opt
.set_validator([](std::string
*value
, std::string
*error_message
) {
135 int r
= sscanf(value
->c_str(), "%d/%d", &m
, &n
);
137 if (m
< 0 || m
> 99) {
138 *error_message
= "value must be in range [0, 99]";
142 if (n
< 0 || n
> 99) {
143 *error_message
= "value must be in range [0, 99]";
149 *value
= stringify(m
) + "/" + stringify(n
);
152 *error_message
= "value must take the form N or N/M, where N and M are integers";
158 for (auto& opt
: subsys_options
) {
159 schema
.emplace(opt
.name
, opt
);
162 // Populate list of legacy_values according to the OPTION() definitions
163 // Note that this is just setting up our map of name->member ptr. The
164 // default values etc will get loaded in along with new-style data,
165 // as all loads write to both the values map, and the legacy
166 // members if present.
168 #define OPTION(name, type) \
169 {std::string(STRINGIFY(name)), &ConfigValues::name},
170 #define SAFE_OPTION(name, type) OPTION(name, type)
171 #include "common/legacy_config_opts.h"
178 // Validate default values from the schema
179 for (const auto &i
: schema
) {
180 const Option
&opt
= i
.second
;
181 if (opt
.type
== Option::TYPE_STR
) {
182 bool has_daemon_default
= !boost::get
<boost::blank
>(&opt
.daemon_value
);
183 Option::value_t default_val
;
184 if (is_daemon
&& has_daemon_default
) {
185 default_val
= opt
.daemon_value
;
187 default_val
= opt
.value
;
189 // We call pre_validate as a sanity check, but also to get any
190 // side effect (value modification) from the validator.
191 std::string
*def_str
= boost::get
<std::string
>(&default_val
);
192 std::string val
= *def_str
;
194 if (opt
.pre_validate(&val
, &err
) != 0) {
195 std::cerr
<< "Default value " << opt
.name
<< "=" << *def_str
<< " is "
196 "invalid: " << err
<< std::endl
;
198 // This is the compiled-in default that is failing its own option's
199 // validation, so this is super-invalid and should never make it
200 // past a pull request: crash out.
203 if (val
!= *def_str
) {
204 // if the validator normalizes the string into a different form than
205 // what was compiled in, use that.
206 set_val_default(values
, tracker
, opt
.name
, val
);
211 // Copy out values (defaults) into any legacy (C struct member) fields
212 update_legacy_vals(values
);
215 md_config_t::~md_config_t()
220 * Sanity check schema. Assert out on failures, to ensure any bad changes
221 * cannot possibly pass any testing and make it into a release.
223 void md_config_t::validate_schema()
225 for (const auto &i
: schema
) {
226 const auto &opt
= i
.second
;
227 for (const auto &see_also_key
: opt
.see_also
) {
228 if (schema
.count(see_also_key
) == 0) {
229 std::cerr
<< "Non-existent see-also key '" << see_also_key
230 << "' on option '" << opt
.name
<< "'" << std::endl
;
236 for (const auto &i
: legacy_values
) {
237 if (schema
.count(i
.first
) == 0) {
238 std::cerr
<< "Schema is missing legacy field '" << i
.first
<< "'"
245 const Option
*md_config_t::find_option(const string
& name
) const
247 auto p
= schema
.find(name
);
248 if (p
!= schema
.end()) {
254 void md_config_t::set_val_default(ConfigValues
& values
,
255 const ConfigTracker
& tracker
,
256 const string
& name
, const std::string
& val
)
258 const Option
*o
= find_option(name
);
261 int r
= _set_val(values
, tracker
, val
, *o
, CONF_DEFAULT
, &err
);
265 int md_config_t::set_mon_vals(CephContext
*cct
,
266 ConfigValues
& values
,
267 const ConfigTracker
& tracker
,
268 const map
<string
,string
>& kv
,
269 config_callback config_cb
)
271 ignored_mon_values
.clear();
274 ldout(cct
, 4) << __func__
<< " no callback set" << dendl
;
279 if (config_cb(i
.first
, i
.second
)) {
280 ldout(cct
, 4) << __func__
<< " callback consumed " << i
.first
<< dendl
;
283 ldout(cct
, 4) << __func__
<< " callback ignored " << i
.first
<< dendl
;
285 const Option
*o
= find_option(i
.first
);
287 ldout(cct
,10) << __func__
<< " " << i
.first
<< " = " << i
.second
288 << " (unrecognized option)" << dendl
;
291 if (o
->has_flag(Option::FLAG_NO_MON_UPDATE
)) {
292 ignored_mon_values
.emplace(i
);
296 int r
= _set_val(values
, tracker
, i
.second
, *o
, CONF_MON
, &err
);
298 lderr(cct
) << __func__
<< " failed to set " << i
.first
<< " = "
299 << i
.second
<< ": " << err
<< dendl
;
300 ignored_mon_values
.emplace(i
);
301 } else if (r
== ConfigValues::SET_NO_CHANGE
||
302 r
== ConfigValues::SET_NO_EFFECT
) {
303 ldout(cct
,20) << __func__
<< " " << i
.first
<< " = " << i
.second
304 << " (no change)" << dendl
;
305 } else if (r
== ConfigValues::SET_HAVE_EFFECT
) {
306 ldout(cct
,10) << __func__
<< " " << i
.first
<< " = " << i
.second
<< dendl
;
311 values
.for_each([&] (auto name
, auto configs
) {
312 auto config
= configs
.find(CONF_MON
);
313 if (config
== configs
.end()) {
316 if (kv
.find(name
) != kv
.end()) {
319 ldout(cct
,10) << __func__
<< " " << name
320 << " cleared (was " << Option::to_str(config
->second
) << ")"
322 values
.rm_val(name
, CONF_MON
);
325 update_legacy_vals(values
);
329 int md_config_t::parse_config_files(ConfigValues
& values
,
330 const ConfigTracker
& tracker
,
331 const char *conf_files_str
,
332 std::ostream
*warnings
,
336 if (safe_to_start_threads
)
339 if (!values
.cluster
.size() && !conf_files_str
) {
341 * set the cluster name to 'ceph' when neither cluster name nor
342 * configuration file are specified.
344 values
.cluster
= "ceph";
347 if (!conf_files_str
) {
348 const char *c
= getenv("CEPH_CONF");
353 if (flags
& CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
)
355 conf_files_str
= CEPH_CONF_FILE_DEFAULT
;
359 std::list
<std::string
> conf_files
;
360 get_str_list(conf_files_str
, conf_files
);
361 auto p
= conf_files
.begin();
362 while (p
!= conf_files
.end()) {
364 if (s
.find("$data_dir") != string::npos
&&
365 data_dir_option
.empty()) {
366 // useless $data_dir item, skip
367 p
= conf_files
.erase(p
);
369 early_expand_meta(values
, s
, warnings
);
375 list
<string
>::const_iterator c
;
376 for (c
= conf_files
.begin(); c
!= conf_files
.end(); ++c
) {
380 int ret
= cf
.parse_file(fn
.c_str(), &parse_errors
, warnings
);
383 else if (ret
!= -ENOENT
)
386 // it must have been all ENOENTs, that's the only way we got here
387 if (c
== conf_files
.end())
390 if (values
.cluster
.size() == 0) {
392 * If cluster name is not set yet, use the prefix of the
393 * basename of configuration file as cluster name.
395 auto start
= c
->rfind('/') + 1;
396 auto end
= c
->find(".conf", start
);
397 if (end
== c
->npos
) {
399 * If the configuration file does not follow $cluster.conf
400 * convention, we do the last try and assign the cluster to
403 values
.cluster
= "ceph";
405 values
.cluster
= c
->substr(start
, end
- start
);
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
;
414 int ret
= _get_val_from_conf_file(my_sections
, opt
.name
, val
);
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
421 if (!error_message
.empty()) {
422 *warnings
<< " (" << error_message
<< ")";
424 *warnings
<< std::endl
;
429 // Warn about section names that look like old-style section names
430 std::deque
< std::string
> old_style_section_names
;
431 for (ConfFile::const_section_iter_t s
= cf
.sections_begin();
432 s
!= cf
.sections_end(); ++s
) {
433 const string
&str(s
->first
);
434 if (((str
.find("mds") == 0) || (str
.find("mon") == 0) ||
435 (str
.find("osd") == 0)) && (str
.size() > 3) && (str
[3] != '.')) {
436 old_style_section_names
.push_back(str
);
439 if (!old_style_section_names
.empty()) {
441 cerr
<< "ERROR! old-style section name(s) found: ";
443 for (std::deque
< std::string
>::const_iterator os
= old_style_section_names
.begin();
444 os
!= old_style_section_names
.end(); ++os
) {
448 cerr
<< ". Please use the new style section names that include a period.";
451 update_legacy_vals(values
);
456 void md_config_t::parse_env(unsigned entity_type
,
457 ConfigValues
& values
,
458 const ConfigTracker
& tracker
,
459 const char *args_var
)
461 if (safe_to_start_threads
)
464 args_var
= "CEPH_ARGS";
466 if (auto s
= getenv("CEPH_KEYRING"); s
) {
468 _set_val(values
, tracker
, s
, *find_option("keyring"), CONF_ENV
, &err
);
470 if (auto dir
= getenv("CEPH_LIB"); dir
) {
471 for (auto name
: { "erasure_code_dir", "plugin_dir", "osd_class_dir" }) {
473 const Option
*o
= find_option(name
);
475 _set_val(values
, tracker
, dir
, *o
, CONF_ENV
, &err
);
478 if (auto pod_req
= getenv("POD_MEMORY_REQUEST"); pod_req
) {
480 uint64_t v
= atoll(pod_req
);
482 switch (entity_type
) {
483 case CEPH_ENTITY_TYPE_OSD
:
484 _set_val(values
, tracker
, stringify(v
),
485 *find_option("osd_memory_target"),
491 if (getenv(args_var
)) {
492 vector
<const char *> env_args
;
493 env_to_vec(env_args
, args_var
);
494 parse_argv(values
, tracker
, env_args
, CONF_ENV
);
498 void md_config_t::show_config(const ConfigValues
& values
,
499 std::ostream
& out
) const
501 _show_config(values
, &out
, nullptr);
504 void md_config_t::show_config(const ConfigValues
& values
,
507 _show_config(values
, nullptr, f
);
510 void md_config_t::config_options(Formatter
*f
) const
512 f
->open_array_section("options");
513 for (const auto& i
: schema
) {
514 f
->dump_object("option", i
.second
);
519 void md_config_t::_show_config(const ConfigValues
& values
,
520 std::ostream
*out
, Formatter
*f
) const
523 *out
<< "name = " << values
.name
<< std::endl
;
524 *out
<< "cluster = " << values
.cluster
<< std::endl
;
527 f
->dump_string("name", stringify(values
.name
));
528 f
->dump_string("cluster", values
.cluster
);
530 for (const auto& i
: schema
) {
531 const Option
&opt
= i
.second
;
533 conf_stringify(_get_val(values
, opt
), &val
);
535 *out
<< opt
.name
<< " = " << val
<< std::endl
;
538 f
->dump_string(opt
.name
.c_str(), val
);
543 int md_config_t::parse_argv(ConfigValues
& values
,
544 const ConfigTracker
& tracker
,
545 std::vector
<const char*>& args
, int level
)
547 if (safe_to_start_threads
) {
551 // In this function, don't change any parts of the configuration directly.
552 // Instead, use set_val to set them. This will allow us to send the proper
553 // observer notifications later.
555 for (std::vector
<const char*>::iterator i
= args
.begin(); i
!= args
.end(); ) {
556 if (strcmp(*i
, "--") == 0) {
557 /* Normally we would use ceph_argparse_double_dash. However, in this
558 * function we *don't* want to remove the double dash, because later
559 * argument parses will still need to see it. */
562 else if (ceph_argparse_flag(args
, i
, "--show_conf", (char*)NULL
)) {
563 cerr
<< cf
<< std::endl
;
566 else if (ceph_argparse_flag(args
, i
, "--show_config", (char*)NULL
)) {
567 do_show_config
= true;
569 else if (ceph_argparse_witharg(args
, i
, &val
, "--show_config_value", (char*)NULL
)) {
570 do_show_config_value
= val
;
572 else if (ceph_argparse_flag(args
, i
, "--no-mon-config", (char*)NULL
)) {
573 values
.no_mon_config
= true;
575 else if (ceph_argparse_flag(args
, i
, "--log-early", (char*)NULL
)) {
576 values
.log_early
= true;
578 else if (ceph_argparse_flag(args
, i
, "--mon-config", (char*)NULL
)) {
579 values
.no_mon_config
= false;
581 else if (ceph_argparse_flag(args
, i
, "--foreground", "-f", (char*)NULL
)) {
582 set_val_or_die(values
, tracker
, "daemonize", "false");
584 else if (ceph_argparse_flag(args
, i
, "-d", (char*)NULL
)) {
585 set_val_or_die(values
, tracker
, "daemonize", "false");
586 set_val_or_die(values
, tracker
, "log_file", "");
587 set_val_or_die(values
, tracker
, "log_to_stderr", "true");
588 set_val_or_die(values
, tracker
, "err_to_stderr", "true");
589 set_val_or_die(values
, tracker
, "log_to_syslog", "false");
591 // Some stuff that we wanted to give universal single-character options for
592 // Careful: you can burn through the alphabet pretty quickly by adding
594 else if (ceph_argparse_witharg(args
, i
, &val
, "--monmap", "-M", (char*)NULL
)) {
595 set_val_or_die(values
, tracker
, "monmap", val
.c_str());
597 else if (ceph_argparse_witharg(args
, i
, &val
, "--mon_host", "-m", (char*)NULL
)) {
598 set_val_or_die(values
, tracker
, "mon_host", val
.c_str());
600 else if (ceph_argparse_witharg(args
, i
, &val
, "--bind", (char*)NULL
)) {
601 set_val_or_die(values
, tracker
, "public_addr", val
.c_str());
603 else if (ceph_argparse_witharg(args
, i
, &val
, "--keyfile", "-K", (char*)NULL
)) {
608 r
= bl
.read_fd(STDIN_FILENO
, 1024);
610 r
= bl
.read_file(val
.c_str(), &err
);
613 string
k(bl
.c_str(), bl
.length());
614 set_val_or_die(values
, tracker
, "key", k
.c_str());
617 else if (ceph_argparse_witharg(args
, i
, &val
, "--keyring", "-k", (char*)NULL
)) {
618 set_val_or_die(values
, tracker
, "keyring", val
.c_str());
620 else if (ceph_argparse_witharg(args
, i
, &val
, "--client_mountpoint", "-r", (char*)NULL
)) {
621 set_val_or_die(values
, tracker
, "client_mountpoint", val
.c_str());
624 int r
= parse_option(values
, tracker
, args
, i
, NULL
, level
);
630 // meta expands could have modified anything. Copy it all out again.
631 update_legacy_vals(values
);
635 void md_config_t::do_argv_commands(const ConfigValues
& values
) const
638 if (do_show_config
) {
639 _show_config(values
, &cout
, NULL
);
643 if (do_show_config_value
.size()) {
645 int r
= conf_stringify(_get_val(values
, do_show_config_value
, 0, &cerr
),
649 std::cerr
<< "failed to get config option '"
650 << do_show_config_value
<< "': option not found" << std::endl
;
652 std::cerr
<< "failed to get config option '"
653 << do_show_config_value
<< "': " << cpp_strerror(r
)
657 std::cout
<< val
<< std::endl
;
662 int md_config_t::parse_option(ConfigValues
& values
,
663 const ConfigTracker
& tracker
,
664 std::vector
<const char*>& args
,
665 std::vector
<const char*>::iterator
& i
,
673 std::string option_name
;
674 std::string error_message
;
676 for (const auto& opt_iter
: schema
) {
677 const Option
&opt
= opt_iter
.second
;
679 std::string
as_option("--");
680 as_option
+= opt
.name
;
681 option_name
= opt
.name
;
682 if (ceph_argparse_witharg(
684 string(string("--default-") + opt
.name
).c_str(), (char*)NULL
)) {
685 if (!err
.str().empty()) {
686 error_message
= err
.str();
690 ret
= _set_val(values
, tracker
, val
, opt
, CONF_DEFAULT
, &error_message
);
692 } else if (opt
.type
== Option::TYPE_BOOL
) {
694 if (ceph_argparse_binary_flag(args
, i
, &res
, oss
, as_option
.c_str(),
697 ret
= _set_val(values
, tracker
, "false", opt
, level
, &error_message
);
699 ret
= _set_val(values
, tracker
, "true", opt
, level
, &error_message
);
704 std::string
no("--no-");
706 if (ceph_argparse_flag(args
, i
, no
.c_str(), (char*)NULL
)) {
707 ret
= _set_val(values
, tracker
, "false", opt
, level
, &error_message
);
711 } else if (ceph_argparse_witharg(args
, i
, &val
, err
,
712 as_option
.c_str(), (char*)NULL
)) {
713 if (!err
.str().empty()) {
714 error_message
= err
.str();
718 ret
= _set_val(values
, tracker
, val
, opt
, level
, &error_message
);
724 if (ret
< 0 || !error_message
.empty()) {
725 ceph_assert(!option_name
.empty());
727 *oss
<< "Parse error setting " << option_name
<< " to '"
728 << val
<< "' using injectargs";
729 if (!error_message
.empty()) {
730 *oss
<< " (" << error_message
<< ")";
734 cerr
<< "parse error setting '" << option_name
<< "' to '"
736 if (!error_message
.empty()) {
737 cerr
<< " (" << error_message
<< ")";
739 cerr
<< "\n" << std::endl
;
743 if (o
== schema
.size()) {
747 return ret
>= 0 ? 0 : ret
;
750 int md_config_t::parse_injectargs(ConfigValues
& values
,
751 const ConfigTracker
& tracker
,
752 std::vector
<const char*>& args
,
756 for (std::vector
<const char*>::iterator i
= args
.begin(); i
!= args
.end(); ) {
757 int r
= parse_option(values
, tracker
, args
, i
, oss
, CONF_OVERRIDE
);
764 void md_config_t::set_safe_to_start_threads()
766 safe_to_start_threads
= true;
769 void md_config_t::_clear_safe_to_start_threads()
771 safe_to_start_threads
= false;
774 int md_config_t::injectargs(ConfigValues
& values
,
775 const ConfigTracker
& tracker
,
776 const std::string
& s
, std::ostream
*oss
)
779 char b
[s
.length()+1];
780 strcpy(b
, s
.c_str());
781 std::vector
<const char*> nargs
;
785 while (*p
&& *p
!= ' ') p
++;
789 while (*p
&& *p
== ' ') p
++;
791 ret
= parse_injectargs(values
, tracker
, nargs
, oss
);
792 if (!nargs
.empty()) {
793 *oss
<< " failed to parse arguments: ";
795 for (std::vector
<const char*>::const_iterator i
= nargs
.begin();
796 i
!= nargs
.end(); ++i
) {
797 *oss
<< prefix
<< *i
;
803 update_legacy_vals(values
);
807 void md_config_t::set_val_or_die(ConfigValues
& values
,
808 const ConfigTracker
& tracker
,
809 const std::string
&key
,
810 const std::string
&val
)
812 std::stringstream err
;
813 int ret
= set_val(values
, tracker
, key
, val
, &err
);
815 std::cerr
<< "set_val_or_die(" << key
<< "): " << err
.str();
817 ceph_assert(ret
== 0);
820 int md_config_t::set_val(ConfigValues
& values
,
821 const ConfigTracker
& tracker
,
822 const std::string
&key
, const char *val
,
823 std::stringstream
*err_ss
)
826 if (err_ss
) *err_ss
<< "No key specified";
835 string
k(ConfFile::normalize_key_name(key
));
837 const auto &opt_iter
= schema
.find(k
);
838 if (opt_iter
!= schema
.end()) {
839 const Option
&opt
= opt_iter
->second
;
840 std::string error_message
;
841 int r
= _set_val(values
, tracker
, v
, opt
, CONF_OVERRIDE
, &error_message
);
843 if (err_ss
) *err_ss
<< "Set " << opt
.name
<< " to " << v
;
846 if (err_ss
) *err_ss
<< error_message
;
851 if (err_ss
) *err_ss
<< "Configuration option not found: '" << key
<< "'";
855 int md_config_t::rm_val(ConfigValues
& values
, const std::string
& key
)
857 return _rm_val(values
, key
, CONF_OVERRIDE
);
860 void md_config_t::get_defaults_bl(const ConfigValues
& values
,
863 if (defaults_bl
.length() == 0) {
866 for (const auto &i
: schema
) {
868 encode(i
.second
.name
, bl
);
869 auto [value
, found
] = values
.get_value(i
.second
.name
, CONF_DEFAULT
);
871 encode(Option::to_str(value
), bl
);
874 conf_stringify(_get_val_default(i
.second
), &val
);
878 encode(n
, defaults_bl
);
879 defaults_bl
.claim_append(bl
);
884 void md_config_t::get_config_bl(
885 const ConfigValues
& values
,
886 uint64_t have_version
,
888 uint64_t *got_version
)
890 if (values_bl
.length() == 0) {
893 values
.for_each([&](auto& name
, auto& configs
) {
894 if (name
== "fsid" ||
900 encode((uint32_t)configs
.size(), bl
);
901 for (auto& j
: configs
) {
903 encode(Option::to_str(j
.second
), bl
);
906 // make sure overridden items appear, and include the default value
907 for (auto& i
: ignored_mon_values
) {
908 if (values
.contains(i
.first
)) {
911 if (i
.first
== "fsid" ||
915 const Option
*opt
= find_option(i
.first
);
921 encode((uint32_t)1, bl
);
922 encode((int32_t)CONF_DEFAULT
, bl
);
924 conf_stringify(_get_val_default(*opt
), &val
);
927 encode(n
, values_bl
);
928 values_bl
.claim_append(bl
);
929 encode(ignored_mon_values
, values_bl
);
932 if (have_version
!= values_bl_version
) {
934 *got_version
= values_bl_version
;
938 int md_config_t::get_val(const ConfigValues
& values
,
939 const std::string
&key
, char **buf
, int len
) const
941 string
k(ConfFile::normalize_key_name(key
));
942 return _get_val_cstr(values
, k
, buf
, len
);
945 int md_config_t::get_val(
946 const ConfigValues
& values
,
947 const std::string
&key
,
948 std::string
*val
) const
950 return conf_stringify(get_val_generic(values
, key
), val
);
953 Option::value_t
md_config_t::get_val_generic(
954 const ConfigValues
& values
,
955 const std::string
&key
) const
957 string
k(ConfFile::normalize_key_name(key
));
958 return _get_val(values
, k
);
961 Option::value_t
md_config_t::_get_val(
962 const ConfigValues
& values
,
963 const std::string
&key
,
964 expand_stack_t
*stack
,
965 std::ostream
*err
) const
968 return Option::value_t(boost::blank());
971 // In key names, leading and trailing whitespace are not significant.
972 string
k(ConfFile::normalize_key_name(key
));
974 const Option
*o
= find_option(key
);
976 // not a valid config option
977 return Option::value_t(boost::blank());
980 return _get_val(values
, *o
, stack
, err
);
983 Option::value_t
md_config_t::_get_val(
984 const ConfigValues
& values
,
986 expand_stack_t
*stack
,
987 std::ostream
*err
) const
989 expand_stack_t a_stack
;
993 return _expand_meta(values
,
994 _get_val_nometa(values
, o
),
998 Option::value_t
md_config_t::_get_val_nometa(const ConfigValues
& values
,
999 const Option
& o
) const
1001 if (auto [value
, found
] = values
.get_value(o
.name
, -1); found
) {
1004 return _get_val_default(o
);
1008 const Option::value_t
& md_config_t::_get_val_default(const Option
& o
) const
1010 bool has_daemon_default
= !boost::get
<boost::blank
>(&o
.daemon_value
);
1011 if (is_daemon
&& has_daemon_default
) {
1012 return o
.daemon_value
;
1018 void md_config_t::early_expand_meta(
1019 const ConfigValues
& values
,
1021 std::ostream
*err
) const
1023 expand_stack_t stack
;
1024 Option::value_t v
= _expand_meta(values
,
1025 Option::value_t(val
),
1026 nullptr, &stack
, err
);
1027 conf_stringify(v
, &val
);
1030 bool md_config_t::finalize_reexpand_meta(ConfigValues
& values
,
1031 const ConfigTracker
& tracker
)
1033 for (auto& [name
, value
] : may_reexpand_meta
) {
1034 set_val(values
, tracker
, name
, value
);
1037 if (!may_reexpand_meta
.empty()) {
1038 // meta expands could have modified anything. Copy it all out again.
1039 update_legacy_vals(values
);
1046 Option::value_t
md_config_t::_expand_meta(
1047 const ConfigValues
& values
,
1048 const Option::value_t
& in
,
1050 expand_stack_t
*stack
,
1051 std::ostream
*err
) const
1053 //cout << __func__ << " in '" << in << "' stack " << stack << std::endl;
1057 const std::string
*str
= boost::get
<const std::string
>(&in
);
1063 auto pos
= str
->find('$');
1064 if (pos
== std::string::npos
) {
1065 // no substitutions!
1070 stack
->push_back(make_pair(o
, &in
));
1073 decltype(pos
) last_pos
= 0;
1074 while (pos
!= std::string::npos
) {
1075 ceph_assert((*str
)[pos
] == '$');
1076 if (pos
> last_pos
) {
1077 out
+= str
->substr(last_pos
, pos
- last_pos
);
1080 // try to parse the variable name into var, either \$\{(.+)\} or
1082 const char *valid_chars
= "abcdefghijklmnopqrstuvwxyz_";
1085 if ((*str
)[pos
+1] == '{') {
1087 endpos
= str
->find_first_not_of(valid_chars
, pos
+ 2);
1088 if (endpos
!= std::string::npos
&&
1089 (*str
)[endpos
] == '}') {
1090 var
= str
->substr(pos
+ 2, endpos
- pos
- 2);
1095 endpos
= str
->find_first_not_of(valid_chars
, pos
+ 1);
1096 if (endpos
!= std::string::npos
)
1097 var
= str
->substr(pos
+ 1, endpos
- pos
- 1);
1099 var
= str
->substr(pos
+ 1);
1106 //cout << " found var " << var << std::endl;
1107 // special metavariable?
1108 if (var
== "type") {
1109 out
+= values
.name
.get_type_name();
1110 } else if (var
== "cluster") {
1111 out
+= values
.cluster
;
1112 } else if (var
== "name") {
1113 out
+= values
.name
.to_cstr();
1114 } else if (var
== "host") {
1115 if (values
.host
== "") {
1116 out
+= ceph_get_short_hostname();
1120 } else if (var
== "num") {
1121 out
+= values
.name
.get_id().c_str();
1122 } else if (var
== "id") {
1123 out
+= values
.name
.get_id();
1124 } else if (var
== "pid") {
1125 out
+= stringify(getpid());
1127 may_reexpand_meta
[o
->name
] = *str
;
1129 } else if (var
== "cctid") {
1130 out
+= stringify((unsigned long long)this);
1131 } else if (var
== "home") {
1132 const char *home
= getenv("HOME");
1133 out
= home
? std::string(home
) : std::string();
1135 if (var
== "data_dir") {
1136 var
= data_dir_option
;
1138 const Option
*o
= find_option(var
);
1140 out
+= str
->substr(pos
, endpos
- pos
);
1142 auto match
= std::find_if(
1143 stack
->begin(), stack
->end(),
1144 [o
](pair
<const Option
*,const Option::value_t
*>& item
) {
1145 return item
.first
== o
;
1147 if (match
!= stack
->end()) {
1148 // substitution loop; break the cycle
1150 *err
<< "variable expansion loop at " << var
<< "="
1151 << Option::to_str(*match
->second
) << "\n"
1152 << "expansion stack:\n";
1153 for (auto i
= stack
->rbegin(); i
!= stack
->rend(); ++i
) {
1154 *err
<< i
->first
->name
<< "="
1155 << Option::to_str(*i
->second
) << "\n";
1158 return Option::value_t(std::string("$") + o
->name
);
1160 // recursively evaluate!
1162 conf_stringify(_get_val(values
, *o
, stack
, err
), &n
);
1168 pos
= str
->find('$', last_pos
);
1170 if (last_pos
!= std::string::npos
) {
1171 out
+= str
->substr(last_pos
);
1177 return Option::value_t(out
);
1180 int md_config_t::_get_val_cstr(
1181 const ConfigValues
& values
,
1182 const std::string
&key
, char **buf
, int len
) const
1188 if (conf_stringify(_get_val(values
, key
), &val
) == 0) {
1189 int l
= val
.length() + 1;
1191 *buf
= (char*)malloc(l
);
1194 strncpy(*buf
, val
.c_str(), l
);
1197 snprintf(*buf
, len
, "%s", val
.c_str());
1198 return (l
> len
) ? -ENAMETOOLONG
: 0;
1201 string
k(ConfFile::normalize_key_name(key
));
1203 // couldn't find a configuration option with key 'k'
1207 void md_config_t::get_all_keys(std::vector
<std::string
> *keys
) const {
1208 const std::string
negative_flag_prefix("no_");
1211 keys
->reserve(schema
.size());
1212 for (const auto &i
: schema
) {
1213 const Option
&opt
= i
.second
;
1214 keys
->push_back(opt
.name
);
1215 if (opt
.type
== Option::TYPE_BOOL
) {
1216 keys
->push_back(negative_flag_prefix
+ opt
.name
);
1221 /* The order of the sections here is important. The first section in the
1222 * vector is the "highest priority" section; if we find it there, we'll stop
1223 * looking. The lowest priority section is the one we look in only if all
1224 * others had nothing. This should always be the global section.
1226 void md_config_t::get_my_sections(const ConfigValues
& values
,
1227 std::vector
<std::string
> §ions
) const
1229 _get_my_sections(values
, sections
);
1232 void md_config_t::_get_my_sections(const ConfigValues
& values
,
1233 std::vector
<std::string
> §ions
) const
1235 sections
.push_back(values
.name
.to_str());
1237 sections
.push_back(values
.name
.get_type_name());
1239 sections
.push_back("global");
1242 // Return a list of all sections
1243 int md_config_t::get_all_sections(std::vector
<std::string
> §ions
) const
1245 for (ConfFile::const_section_iter_t s
= cf
.sections_begin();
1246 s
!= cf
.sections_end(); ++s
) {
1247 sections
.push_back(s
->first
);
1252 int md_config_t::get_val_from_conf_file(
1253 const ConfigValues
& values
,
1254 const std::vector
<std::string
> §ions
,
1255 const std::string
&key
,
1259 int r
= _get_val_from_conf_file(sections
, key
, out
);
1264 expand_stack_t stack
;
1265 auto v
= _expand_meta(values
, Option::value_t(out
), nullptr, &stack
, nullptr);
1266 conf_stringify(v
, &out
);
1271 int md_config_t::_get_val_from_conf_file(
1272 const std::vector
<std::string
> §ions
,
1273 const std::string
&key
,
1274 std::string
&out
) const
1276 std::vector
<std::string
>::const_iterator s
= sections
.begin();
1277 std::vector
<std::string
>::const_iterator s_end
= sections
.end();
1278 for (; s
!= s_end
; ++s
) {
1279 int ret
= cf
.read(s
->c_str(), key
, out
);
1282 } else if (ret
!= -ENOENT
) {
1289 int md_config_t::_set_val(
1290 ConfigValues
& values
,
1291 const ConfigTracker
& observers
,
1292 const std::string
&raw_val
,
1295 std::string
*error_message
)
1297 Option::value_t new_value
;
1298 ceph_assert(error_message
);
1299 int r
= opt
.parse_value(raw_val
, &new_value
, error_message
);
1304 // unsafe runtime change?
1305 if (!opt
.can_update_at_runtime() &&
1306 safe_to_start_threads
&&
1307 !observers
.is_tracking(opt
.name
)) {
1308 // accept value if it is not actually a change
1309 if (new_value
!= _get_val_nometa(values
, opt
)) {
1310 *error_message
= string("Configuration option '") + opt
.name
+
1311 "' may not be modified at runtime";
1316 // Apply the value to its entry in the `values` map
1317 auto result
= values
.set_value(opt
.name
, std::move(new_value
), level
);
1319 case ConfigValues::SET_NO_CHANGE
:
1321 case ConfigValues::SET_NO_EFFECT
:
1324 case ConfigValues::SET_HAVE_EFFECT
:
1326 _refresh(values
, opt
);
1332 void md_config_t::_refresh(ConfigValues
& values
, const Option
& opt
)
1334 // Apply the value to its legacy field, if it has one
1335 auto legacy_ptr_iter
= legacy_values
.find(std::string(opt
.name
));
1336 if (legacy_ptr_iter
!= legacy_values
.end()) {
1337 update_legacy_val(values
, opt
, legacy_ptr_iter
->second
);
1339 // Was this a debug_* option update?
1340 if (opt
.subsys
>= 0) {
1342 conf_stringify(_get_val(values
, opt
), &actual_val
);
1343 values
.set_logging(opt
.subsys
, actual_val
.c_str());
1345 // normal option, advertise the change.
1346 values
.changed
.insert(opt
.name
);
1350 int md_config_t::_rm_val(ConfigValues
& values
,
1351 const std::string
& key
,
1354 if (schema
.count(key
) == 0) {
1357 auto ret
= values
.rm_val(key
, level
);
1361 if (ret
== ConfigValues::SET_HAVE_EFFECT
) {
1362 _refresh(values
, *find_option(key
));
1369 template<typename Size
>
1370 struct get_size_visitor
: public boost::static_visitor
<Size
>
1372 get_size_visitor() {}
1374 template<typename T
>
1375 Size
operator()(const T
&) const {
1378 Size
operator()(const Option::size_t& sz
) const {
1379 return static_cast<Size
>(sz
.value
);
1381 Size
operator()(const Size
& v
) const {
1387 * Handles assigning from a variant-of-types to a variant-of-pointers-to-types
1389 template<class Config
>
1390 class assign_visitor
: public boost::static_visitor
<>
1393 Option::value_t val
;
1396 assign_visitor(Config
*conf_
, Option::value_t val_
)
1397 : conf(conf_
), val(val_
)
1400 template <typename T
>
1401 void operator()(T
Config::* ptr
) const
1403 T
*member
= const_cast<T
*>(&(conf
->*(boost::get
<const T
Config::*>(ptr
))));
1405 *member
= boost::get
<T
>(val
);
1407 void operator()(uint64_t Config::* ptr
) const
1410 auto member
= const_cast<T
*>(&(conf
->*(boost::get
<const T
Config::*>(ptr
))));
1411 *member
= boost::apply_visitor(get_size_visitor
<T
>{}, val
);
1413 void operator()(int64_t Config::* ptr
) const
1416 auto member
= const_cast<T
*>(&(conf
->*(boost::get
<const T
Config::*>(ptr
))));
1417 *member
= boost::apply_visitor(get_size_visitor
<T
>{}, val
);
1420 } // anonymous namespace
1422 void md_config_t::update_legacy_vals(ConfigValues
& values
)
1424 for (const auto &i
: legacy_values
) {
1425 const auto &name
= i
.first
;
1426 const auto &option
= schema
.at(name
);
1427 auto ptr
= i
.second
;
1428 update_legacy_val(values
, option
, ptr
);
1432 void md_config_t::update_legacy_val(ConfigValues
& values
,
1434 md_config_t::member_ptr_t member_ptr
)
1436 Option::value_t v
= _get_val(values
, opt
);
1437 boost::apply_visitor(assign_visitor(&values
, v
), member_ptr
);
1440 static void dump(Formatter
*f
, int level
, Option::value_t in
)
1442 if (const bool *v
= boost::get
<const bool>(&in
)) {
1443 f
->dump_bool(ceph_conf_level_name(level
), *v
);
1444 } else if (const int64_t *v
= boost::get
<const int64_t>(&in
)) {
1445 f
->dump_int(ceph_conf_level_name(level
), *v
);
1446 } else if (const uint64_t *v
= boost::get
<const uint64_t>(&in
)) {
1447 f
->dump_unsigned(ceph_conf_level_name(level
), *v
);
1448 } else if (const double *v
= boost::get
<const double>(&in
)) {
1449 f
->dump_float(ceph_conf_level_name(level
), *v
);
1451 f
->dump_stream(ceph_conf_level_name(level
)) << Option::to_str(in
);
1455 void md_config_t::diff(
1456 const ConfigValues
& values
,
1460 values
.for_each([this, f
, &values
] (auto& name
, auto& configs
) {
1461 if (configs
.size() == 1 &&
1462 configs
.begin()->first
== CONF_DEFAULT
) {
1463 // we only have a default value; exclude from diff
1466 f
->open_object_section(name
.c_str());
1467 const Option
*o
= find_option(name
);
1468 dump(f
, CONF_DEFAULT
, _get_val_default(*o
));
1469 for (auto& j
: configs
) {
1470 dump(f
, j
.first
, j
.second
);
1472 dump(f
, CONF_FINAL
, _get_val(values
, *o
));
1477 void md_config_t::complain_about_parse_errors(CephContext
*cct
)
1479 ::complain_about_parse_errors(cct
, &parse_errors
);